はじめに
Kaggleのタイタニック号で生き残るのは誰?に初チャレンジしてみたので、その方法を解説したいと思います。
ゴール

この記事を読むこと、タイタニック号で生き残るのは誰?にSubmitすることができるようになります。
コード
こちらを参考にさせていただきました。
CSVをダウンロードする。
以下のリンク先から、「Download All」でCSVを落とします。

データを確認する
import pandas as pd
train_df = pd.read_csv("train.csv")
test_df = pd.read_csv("test.csv")
gender_submission_df = pd.read_csv("gender_submission.csv")
以下のようなデータセットになっています。

パラメータは以下のようになっています。
- survival:1は生きる、0は死ぬ
- pclass:チケットのクラス 1=上 、2=中央、 3=低い
Embarked = 各変数の定義は下記の通り
- C = Cherbourg
- Q = Queenstown
- S = Southampton
Variable | Definition | Key |
---|---|---|
survival | Survival | 0 = No, 1 = Yes |
pclass | Ticket class | 1 = 1st, 2 = 2nd, 3 = 3rd |
sex | Sex | |
Age | Age in years | |
sibsp | # of siblings / spouses aboard the Titanic | |
parch | # of parents / children aboard the Titanic | |
ticket | Ticket number | |
fare | Passenger fare | |
cabin | Cabin number | |
embarked | Port of Embarkation | C = Cherbourg, Q = Queenstown, S = Southampton |
欠損値の把握
以下のコードで欠損値を把握しました。欠損値があるとデータ分析ができないので、0にしたり中央値にしたりなどの処理方法があります。
'''欠損数の把握'''
def kesson_table(df):
null_val = df.isnull().sum()
percent = 100 * df.isnull().sum()/len(df)
kesson_table = pd.concat([null_val, percent], axis=1)
kesson_table_ren_columns = kesson_table.rename(
columns = {0 : '欠損数', 1 : '%'})
return kesson_table_ren_columns
欠損値
欠損データ. 分析に利用するデータには多くの場合、なんらかの理由により記録されなかった値、欠損値 (欠測、欠落 missing data) が含まれます。全ての変数の値が観測されているデータを完全データ (complete data) と呼びます。
欠損値の処理
- Ageの欠損値は中央値で処理しました。
- EmbarkedはSで埋めました
train_df["Age"] = train_df["Age"].fillna(train_df["Age"].median())
train_df["Embarked"] = train_df["Embarked"].fillna("S")
kesson_table(train_df)
文字列を数字に変換
以下のように変換しました。
train_df["Sex"][train_df["Sex"] == "male"] = 0
train_df["Sex"][train_df["Sex"] == "female"] = 1
train_df["Embarked"][train_df["Embarked"] == "S" ] = 0
train_df["Embarked"][train_df["Embarked"] == "C" ] = 1
train_df["Embarked"][train_df["Embarked"] == "Q"] = 2
この変換はLabelEncoderでもできます。これは、これは0,これは1という風に定義しなくても自動で割り振ってくれます。
from sklearn.preprocessing import LabelEncoder
def Label(data):
#LabelEncoderのインスタンスを生成
le = LabelEncoder()
#ラベルを覚えさせる
le = le.fit(data)
#ラベルを整数に変換
data = le.transform(data)
return data
train_df['Sex']=Label(train_df['Sex'])
testデータの欠損値の処理
#test_df_dfの欠損値の処理
test_df["Age"] = test_df["Age"].fillna(test_df["Age"].median())
test_df["Sex"][test_df["Sex"] == "male"] = 0
test_df["Sex"][test_df["Sex"] == "female"] = 1
test_df["Embarked"][test_df["Embarked"] == "S"] = 0
test_df["Embarked"][test_df["Embarked"] == "C"] = 1
test_df["Embarked"][test_df["Embarked"] == "Q"] = 2
test_df.Fare[152] = test_df.Fare.median()
ランダムフォレストの実装
なぜランダムフォレスト?と聞かれると、なんとなくそこそこ精度がよくとりあえず使えるって感覚で使っています。一応解説します。以下wikipediaを参照しました。

ランダムフォレストは少ないサンプル数で説明因子が多くてもそれなりに高速で動くので、パパッと試したいときに私は、使います。ちなみに初手に使うのであれあれば、GBDTを使います。
ランダムフォレストのアルゴリズム
- 学習を行いたい観測データから、ブートストラップ法によるランダムサンプリングにより B 組のサブサンプルを生成する
- 各サブサンプルをトレーニングデータとし、B 本の決定木を作成する
- 指定したノード数 {\displaystyle n_{\mathrm {min} }}
に達するまで、以下の方法でノードを作成する
- トレーニングデータの説明変数のうち、m 個をランダムに選択する
- 選ばれた説明変数のうち、トレーニングデータを最も良く分類するものとそのときの閾値を用いて、ノードのスプリット関数を決定する
要点は、ランダムサンプリングされたトレーニングデータとランダムに選択された説明変数を用いることにより、相関の低い決定木群を作成すること。
長所
- 説明変数が多数であってもうまく働く
- 学習・評価が高速
- 決定木の学習は完全に独立しており、並列に処理可能
- 説明変数の重要度(寄与度)を算出可能
- Out of Bag エラーの計算により、クロスバリデーションのような評価が可能
- AdaBoost などと比べて特定の説明変数への依存が少ないため、クエリデータの説明変数が欠損していても良い出力を与える
短所
- 説明変数のうち意味のある変数がノイズ変数よりも極端に少ない場合にはうまく働かない
- グリッドサーチによるハイパーパラメータの最適化
ランダムフォレストのコード
from tqdm import tqdm
from sklearn.datasets import load_breast_cancer
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.model_selection import train_test_split
from sklearn.metrics import f1_score
X = train_df[["Pclass", "Sex", "Age", "Fare"]].values
y = train_df["Survived"].values
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)
#条件設定
max_score = 0
SearchMethod = 0
RFC_grid = {RandomForestClassifier(): {"n_estimators": [i for i in range(1, 21)],
"criterion": ["gini", "entropy"],
"max_depth":[i for i in range(1, 5)],
"random_state": [i for i in range(0, 101)]
}}
#ランダムフォレストの実行
for model, param in tqdm(RFC_grid.items()):
clf = GridSearchCV(model, param)
clf.fit(X_train, y_train)
pred_y = clf.predict(X_test)
score = f1_score(y_test, pred_y, average="micro")
if max_score < score:
max_score = score
best_param = clf.best_params_
best_model = model.__class__.__name__
b_model = clf
print("ベストスコア:{}".format(max_score))
print("モデル:{}".format(best_model))
print("パラメーター:{}".format(best_param))
#ハイパーパラメータを調整しない場合との比較
model = RandomForestClassifier()
model.fit(X_train, y_train)
score = model.score(X_test, y_test)
print("")
print("デフォルトスコア:", score)
出力結果
うーん、デフォルトスコアの方が高い・・・ハイパーパラメータの最適化は不要そうですね。
ベストスコア:0.8134328358208955 モデル:RandomForestClassifier パラメーター:{'criterion': 'entropy', 'max_depth': 4, 'n_estimators': 5, 'random_state': 36} デフォルトスコア: 0.832089552238806
一応、両方サブミットしてみる。
サブミット
my_prediction = model.predict(test_df_features)#デフォルト条件
my_prediction_2 = b_model.predict(test_df_features)#ハイパーパラメータ最適化条件
import numpy as np
# PassengerIdを取得
PassengerId = np.array(test_df["PassengerId"]).astype(int)
# my_prediction(予測データ)とPassengerIdをデータフレームへ落とし込む
my_solution = pd.DataFrame(my_prediction, PassengerId, columns = ["Survived"])
my_solution_2 = pd.DataFrame(my_prediction_2, PassengerId, columns = ["Survived"])
# my_tree_one.csvとして書き出し
my_solution.to_csv("random_forest.csv", index_label = ["PassengerId"])
my_solution_2.to_csv("random_forest_grid.csv", index_label = ["PassengerId"])
Submit Predictionsをクリックする。

以下の赤枠をクリックし、出力したcsvをアップロードする。

Make Submitをクリック。

初挑戦結果
提出結果です。上が、グリッドサーチで最適化した結果。下がデフォルト条件。
グリッドサーチで最適化した方がスコアがよいという結果になりました。

なんとか、初挑戦できました。今後もこのスコアをまずは、伸ばせるよう頑張りたいと思います。
コメント