機械学習によるタイタニック号の生存者予測 with Python
かなり前に勉強会でやった内容のKaggleチュートリアルの復習。
タイタニック(RMS Titanic)は、20世紀初頭に建造された豪華客船である。
処女航海中の1912年4月14日深夜、北大西洋上で氷山に接触、翌日未明にかけて沈没した。犠牲者数は乗員乗客合わせて1,513人(他に1,490人、1,517人、1,522~23人など様々な説がある)であり、当時世界最悪の海難事故であった。その後、映画化されるなどして世界的にその名を知られている。
乗客やスタッフに対する十分な数の救命ボートがなかったため、被害が拡大したと言われています。
他にも生存に必要なさまざまな要素が欠けていたのですが、女性や子供、上流階級といったあるグループの人は他の人よりも生存する傾向にあったので、今回はどのような性質を持った人々が生存する傾向にあるかについて機械学習のツールを使って分析を行います。
評価方法について
・データは"training set"と"test set"の2つに分かれています。
"training set"に対しては各々の乗客に対する結果(ground truth)を提供します。
この訓練セットはテストセットに対する予測を行うためのモデル作成に利用可能にする。
・テストセットの各々の乗客に対し、沈没に際し生存したかどうかを予測
0が死亡を表し,1が生存を表します。
正しく予測できた割合がスコア。
データセット
変数について
・survival 生存したか否か
・0 = No, 1 = Yes
・pclass 乗客の階級
・1 = 1st; 2 = 2nd; 3 = 3rd
・社会経済におけるステータス、1stが上流、2ndが中流、3rdが下流
・name 名前
・sex 性別
・age 年齢
・1歳未満の場合は小数
・推定年齢の場合はxx.5などという表記
・親族関係の変数
・sibsp タイタニック号に乗っていたSibling/Spouseの数
・Sibling: 兄弟、姉妹、義兄弟、義姉妹
・Spouse: 夫、妻
・愛人や婚約者については除外
・parch タイタニック号に乗っていたParent/Childrenの数
・Parent: 母親、父親
・Child: 息子、娘、まま息子、まま娘
・無視されている関係も存在
・いとこ、甥、姪、おじ、おば、舅、姑は除外
・ベビーシッターとだけ旅行していた何人かの子供はparch=0
・親友や近所の人と旅行に来ていた人に対しての属性はない
・ticket チケットナンバー
・fare 乗車料金
・cabin Cabin(船室の部屋番号?)
・embarked 乗船場
・C = Cherbourg, Q = Queenstown; S = Southampton
numpyとcsvライブラリを使って学習データを読む
import csv as csv import numpy as np csv_file_object = csv.reader(open('./data/train.csv', 'rb')) header = csv_file_object.next() data=[] for row in csv_file_object: data.append(row) data = np.array(data) print header print data print data.shape
実行結果
['PassengerId', 'Survived', 'Pclass', 'Name', 'Sex', 'Age', 'SibSp', 'Parch', 'Ticket', 'Fare', 'Cabin', 'Embarked']
[['1' '0' '3' ..., '7.25' '' 'S']
['2' '1' '1' ..., '71.2833' 'C85' 'C']
['3' '1' '3' ..., '7.925' '' 'S']
...,
['889' '0' '3' ..., '23.45' '' 'S']
['890' '1' '1' ..., '30' 'C148' 'C']
['891' '0' '3' ..., '7.75' '' 'Q']]
(891, 12)|
# Ageの列のデータを15個読む print header[5] print data[0:15, 5]
実行結果
Age
['22' '38' '26' '35' '35' '' '54' '2' '27' '14' '4' '58' '20' '39' '14']
# stringをfloatに変換する print data[0:5, 5].astype(np.float) print data[0::,5].astype(np.float) # 欠損値""のためにできない
実行結果
[ 22. 38. 26. 35. 35.]
pandasをつかって読み込む
import pandas as pd import numpy as np # pandasのバージョン print pd.version.version # csvファイルの読み込み df = pd.read_csv('./data/train.csv', header=0) # 0行目がヘッダー(defaultの動作、指定しない場合はNoneを指定) # 各カラムの型がpandasにはどのように解釈されているかを表示 # pandasは小数点がある場合は自動的に浮動小数点として解釈する df.dtypes
データの前処理
pandasのDataFrameではデータのフィルタリング, 操作, 欠損値の棄却, フィル, 変換および置換が簡単にできる
# 平均を算出、これはdf.describe()で表示された値と同じ df["Age"].mean() # 中央値を表示 df["Age"].median() # 複数の列にまとめてアクセスする方法 df[ ["Sex", "Pclass", "Age"]] # フィルターされたデータの内特定の列だけ見たい場合 df[df['Age'] > 60][['Sex', 'Pclass', 'Age', 'Survived']]
実行結果
この結果から60歳以上のほとんどが男性で、ほとんどが上流階級(Pclass==1)、ほとんどが死亡(survived==0)したことが分かる
# 階級ごとの男性の数を出す for i in range(1,4): print i, len(df[ (df['Sex'] == 'male') & (df['Pclass'] == i) ])
実行結果
1 122
2 108
3 347
結果を可視化
matplotlibを用いる。%matplotlib inlineはインラインプロットするためのmagic
付けないとブラウザ外のウインドウに描画される。
%matplotlib inline import matplotlib.pyplot as plt df["Age"].hist()
実行結果
# 欠損値を除いた上でビン数と透過度、範囲を指定 df["Age"].dropna().hist(bins=16, range=(0, 80), alpha=0.5)
データをクリーニングする
文法は大体分かったので機械学習に使える形にデータを整形することを考える。"male"と"female"の文字列に対して解析を掛けるのはややこしい。
異なる三つの方法でデータの変換を練習する。
二つは興味のために行い、一つは実用的なものを紹介します。
新しいカラムに結果は保存するので元のカラムは保持する。
一つ目の方法
# pandasでは新しいカラムを追加するのは新しいカラム名に値を代入するだけ df["Gender"] = 4 # 元はSex df.head(10) # Genderカラムが増えている
実行結果
二つ目の方法
# Genderの値をSexから何かしら作る df["Gender"] = df["Sex"].map( lambda x: x[0].upper() ) # 文字列の一文字目を大文字に変換 df.head(10)
実行結果
三つ目の方法
# 実際には整数が欲しいので女性を0、男性を1にする df["Gender"] = df["Sex"].map( {'female': 0, "male": 1} ).astype(int) # 辞書を渡すだけで変換してくれる df.head(10)
実行結果
Embarked(船着場)に関しても同様な操作が可能になる。
Ageの欠損値の取り扱い
機械学習では完全に値が埋まったサンプル集合が必要なので欠損値はどうにかしないといけない。
推測によって値を埋めることで予測モデルに何らかのノイズを加えることになるが、合理的な推測が可能であればそれは歴史的な真実に近づくはず。
どのような値で埋めるか?
年齢の分かっている全乗客の年齢の平均(29.6991176)?
数少ない70, 80代の影響を減らせるため、中央値の方が良い!!!
以前にプロットした年齢のヒストグラムは正の歪度を持つ
𝔼[(x−𝔼[x]σ)3]
歪度は正の場合ヒストグラムが左に偏る、負の場合は右に偏る
これらはkaggleにおいて自分自身のモデルを作る際に決定しないといけないことの一つ
結局、階級、性別ごとの年齢の中央値を利用する。
続きはPart2へ!