機械学習でタイタニック号の生存者を予測する。同じ40歳でも、金持ち女性は生き残れたけど、労働階級男性は死亡!悲しいけど、これが現実なのよね…。
参考URL
1, タイタニック号の乗客データを取得して、表示
1 2 3 4 |
from sklearn import datasets X, y = datasets.fetch_openml(data_id=40945, return_X_y=True, as_frame=True) print(X) # 乗客データ(1309名) print(y) # 生存結果 0 (死亡) 1 (生存) |
1309名の乗客データがあるらしい。多すぎるとprintは省略されるのか?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
pclass name sex \ 0 1.0 Allen, Miss. Elisabeth Walton female 1 1.0 Allison, Master. Hudson Trevor male 2 1.0 Allison, Miss. Helen Loraine female 3 1.0 Allison, Mr. Hudson Joshua Creighton male 4 1.0 Allison, Mrs. Hudson J C (Bessie Waldo Daniels) female ... ... ... ... 1304 3.0 Zabour, Miss. Hileni female 1305 3.0 Zabour, Miss. Thamine female 1306 3.0 Zakarian, Mr. Mapriededer male 1307 3.0 Zakarian, Mr. Ortin male 1308 3.0 Zimmerman, Mr. Leo male age sibsp parch ticket fare cabin embarked boat body \ 0 29.0000 0.0 0.0 24160 211.3375 B5 S 2 NaN 1 0.9167 1.0 2.0 113781 151.5500 C22 C26 S 11 NaN 2 2.0000 1.0 2.0 113781 151.5500 C22 C26 S None NaN 3 30.0000 1.0 2.0 113781 151.5500 C22 C26 S None 135.0 4 25.0000 1.0 2.0 113781 151.5500 C22 C26 S None NaN ... ... ... ... ... ... ... ... ... ... 1304 14.5000 1.0 0.0 2665 14.4542 None C None 328.0 1305 NaN 1.0 0.0 2665 14.4542 None C None NaN 1306 26.5000 0.0 0.0 2656 7.2250 None C None 304.0 1307 27.0000 0.0 0.0 2670 7.2250 None C None NaN 1308 29.0000 0.0 0.0 315082 7.8750 None S None NaN home.dest 0 St Louis, MO 1 Montreal, PQ / Chesterville, ON 2 Montreal, PQ / Chesterville, ON 3 Montreal, PQ / Chesterville, ON 4 Montreal, PQ / Chesterville, ON ... ... 1304 None 1305 None 1306 None 1307 None 1308 None [1309 rows x 13 columns] 0 1 1 1 2 0 3 0 4 0 .. 1304 0 1305 0 1306 0 1307 0 1308 0 Name: survived, Length: 1309, dtype: category Categories (2, object): ['0', '1'] |
2, 項目と欠損値を確認
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
print(X.isnull().sum()) # 乗客データの欠損値(NULL)を、項目別にカウント pclass 0 # チケットクラス( 1=上層クラス(お金持ち),2=中級クラス(一般階級),3=下層クラス(労働階級) name 0 # 乗客名 sex 0 # 性別(male=男性、female=女性) age 263 # 年齢 263/1309 = 20%も空欄! sibsp 0 # タイタニックに同乗している兄弟/配偶者の数 parch 0 # タイタニックに同乗している親/子供の数 ticket 0 # チケット番号 fare 1 # 料金 cabin 1014 # 客室番号。1014/1309 = 77%も空欄! embarked 2 # 出港地(タイタニックへ乗った港) boat 823 # 救命ボート番号 body 1188 # 遺体収容時の識別番号 home.dest 564 # 自宅または目的地 dtype: int64 |
3, 欠損値を保管する・文字データを数値化する
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import pandas as pd # データ成形用ライブラリ from sklearn import datasets # データセットのライブラリ X, y = datasets.fetch_openml(data_id=40945, return_X_y=True, as_frame=True) # 年齢がnullのデータは、平均年齢で代用 X["age"] = X["age"].fillna(X["age"].median()) # 出港地がnullのデータは、一番多いSで代用 X["embarked"] = X["embarked"].fillna("S") # 性別データは男女の二択なので、先頭のデータと同じかそうでないかで0, 1を入れます。 X["sex"] = pd.get_dummies(X["sex"], drop_first=True) # `embarked`はC,Q,Sの三択なので、それぞれを分解して、Cであるかそうでないか、Qであるかそうでないか、… の0, 1のフラグにします。 X = pd.concat([X, pd.get_dummies(X["embarked"], prefix="embarked")], axis=1).drop(columns=["embarked"]) # 不要っぽいカラムは無視する(除外) feature_columns =["name", "sibsp", "parch", "ticket", "fare", "cabin", "boat", "body", "home.dest"] X = X.drop(columns=feature_columns, axis=1) print(X) # 乗客データ(1309名) # print(y) # 生存結果 0 (死亡) 1 (生存) |
だいぶパラメータが減った。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
pclass sex age embarked_C embarked_Q embarked_S 0 1.0 0 29.0000 0 0 1 1 1.0 1 0.9167 0 0 1 2 1.0 0 2.0000 0 0 1 3 1.0 1 30.0000 0 0 1 4 1.0 0 25.0000 0 0 1 ... ... ... ... ... ... ... 1304 3.0 0 14.5000 1 0 0 1305 3.0 0 28.0000 1 0 0 1306 3.0 1 26.5000 1 0 0 1307 3.0 1 27.0000 1 0 0 1308 3.0 1 29.0000 0 0 1 [1309 rows x 6 columns] |
4, 30%を学習用データに、70%を検証用データにしてみたら、正解率は81.6%(0.816793893129771)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
import pandas as pd # データ成形用ライブラリ from sklearn import datasets # データセットのライブラリ from sklearn.model_selection import train_test_split # データ分割ライバル from sklearn.tree import DecisionTreeClassifier # 決定木ライブラリ from sklearn.metrics import accuracy_score # 機会学習の評価ライブラリ # タイタニック号の乗客データと生存結果を、変数に格納 X, y = datasets.fetch_openml(data_id=40945, return_X_y=True, as_frame=True) # 年齢がnullのデータは、平均年齢で代用 X["age"] = X["age"].fillna(X["age"].median()) # 出港地がnullのデータは、一番多いSで代用 X["embarked"] = X["embarked"].fillna("S") # 性別データは男女の二択なので、先頭のデータと同じかそうでないかで0, 1を入れます。 X["sex"] = pd.get_dummies(X["sex"], drop_first=True) # 出港地(embarked)はC,Q,Sの三択なので、それぞれを分解して、Cであるかそうでないか、Qであるかそうでないか、… の0, 1のフラグにします。 X = pd.concat([X, pd.get_dummies(X["embarked"], prefix="embarked")], axis=1).drop(columns=["embarked"]) # 不要っぽいカラムは無視する(除外) feature_columns =["name", "sibsp", "parch", "ticket", "fare", "cabin", "boat", "body", "home.dest"] X = X.drop(columns=feature_columns, axis=1) # print(X) # 乗客データ(1309名) # print(y) # 生存結果 0 (死亡) 1 (生存) # 乗客データの30%を、学習用データに使う train_X, test_X, train_y, test_y = train_test_split(X, y, test_size=0.3, random_state=0) # 数理モデルは、2値分類(決定木モデル) tree_model = DecisionTreeClassifier() # fit()で機械学習させる tree_model.fit(train_X, train_y) # 残り70%の検証用データを使って、結果を算出 pred_X = tree_model.predict(test_X) # モデルの精度を調べてみる accuracy_score(test_y, pred_X) |
5. 適当な人物データを、学習済みモデルに渡して、生存出来たかを推測させてみる
同じ40歳でも、金持ち女性は生き残れた(1)けど、労働階級男性は死亡!(0)
悲しいけど、これが現実なのよね…。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
print(tree_model.predict([[ 1, # 上層クラス(お金持ち) 0, # 女性 40, # 30歳 0, # embarked_C 0, # embarked_Q 1 # embarked_S ]])) print(tree_model.predict([[ 3, # 下層クラス(労働階級) 1, # 男性 40, # 30歳 0, # embarked_C 0, # embarked_Q 1 # embarked_S ]])) |