Kaggle入門【Titanic】

初めに

以下のKernelsを参考にさせて頂きました.
https://www.kaggle.com/omarelgabry/a-journey-through-titanic

Titanicでは乗客の特徴量を元に生死を予測するものです.

データセットをダウンロードはこちらからです.
Titanic: Machine Learning from Disaster | Kaggle
ダンロードしたら早速始めて行きます.

大まかに以下の手順で進めていきます.

1.不要データ削除
2.欠損値補完
3.文字を数値に変換
4.学習

データの確認

まずはデータをざっと見てみる

もろもろインポート

# pandas
import pandas as pd
from pandas import Series,DataFrame

# numpy, matplotlib, seaborn
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_style('whitegrid')
%matplotlib inline

# machine learning
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC, LinearSVC
from sklearn.ensemble import RandomForestClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.naive_bayes import GaussianNB

head()で訓練データ・テストデータの最初の数行を見てみる

train.head()

https://cdn-ak.f.st-hatena.com/images/fotolife/m/munemakun/20180708/20180708002853.png?1530977534

test.head()

https://cdn-ak.f.st-hatena.com/images/fotolife/m/munemakun/20180708/20180708002850.png?1530977486

各カラムはこんな感じです

  • PassengerId – 乗客識別のID
  • Survived – 生存フラグ(0=死亡、1=生存)
  • Pclass – チケットクラス
  • Name – 乗客の名前
  • Sex – 性別(male=男性、female=女性)
  • Age – 年齢
  • SibSp – タイタニックに同乗している兄弟/配偶者の数
  • parch – タイタニックに同乗している親/子供の数
  • ticket – チケット番号
  • fare – 料金
  • cabin – 客室番号
  • Embarked – 出港地(タイタニックへ乗った港)

describe()で統計量の確認

train.describe()

https://cdn-ak.f.st-hatena.com/images/fotolife/m/munemakun/20180708/20180708002846.png

欠損値はどれくらいか

info()で欠損値の確認

train.info()
print("----------------------------")
test.info()

https://cdn-ak.f.st-hatena.com/images/fotolife/m/munemakun/20180708/20180708024948.png?1530985832

まとめると

カラム 訓練/テスト 欠損具合
Age(年齢) 訓練 and テスト 20%
Cabin(客室番号) 訓練 and テスト 80%
Embarked(出港地) 訓練 2個
Fare(料金) テスト 1個

客室番号の欠損率は非常に高いですね.年齢の欠損率も高いです.出港地や料金は非常に少ないので問題にはならないでしょう.

不要データ削除

Cabinは欠損率が非常に高いので削除します.また,Name,Ticketは処理が面倒なので削除します.訓練データのPassengerIdも不要なので削除します.(テストデータのPassengerIdは提出するファイルを作成する際に使うので削除しない)

train = train.drop(['Cabin','PassengerId','Name','Ticket'], axis=1)
test    = test.drop(['Cabin','Name','Ticket'], axis=1)

要素の補完・追加

欠損値の補完

EmbarkedとFareはデータ数が数個しかないので,平均値で補完します.

# train['Embarked']の2個の欠損値は最頻値であるSを入れる
train["Embarked"] = train["Embarked"].fillna("S")
#test['Fare']の欠損値に平均を入れる
test["Fare"].fillna(test["Fare"].median(), inplace=True)

しかしAgeは20%も抜けているので,単に平均値を入れるのではなく,
平均値±標準偏差内のランダムな値で補完することにします.(分布が正規分布に従う場合,平均値±標準偏差内にデータが含まれる確率は約68%)

#平均と標準偏差を求める
average_age_titanic   = train["Age"].mean()
std_age_titanic       = train["Age"].std()
count_nan_age_titanic = train["Age"].isnull().sum()

average_age_test   = test["Age"].mean()
std_age_test       = test["Age"].std()
count_nan_age_test = test["Age"].isnull().sum()

#  (mean - std) & (mean + std)内のランダムな値
rand_1 = np.random.randint(average_age_titanic - std_age_titanic, average_age_titanic + std_age_titanic, size = count_nan_age_titanic)
rand_2 = np.random.randint(average_age_test - std_age_test, average_age_test + std_age_test, size = count_nan_age_test)
# NaNに補完
train["Age"][np.isnan(train["Age"])] = rand_1
test["Age"][np.isnan(test["Age"])] = rand_2

大人と子どもを追加

大人と子どもで生存に差がありそうなので,大人か子どもかも区別したいと思います.
そこで,新たに'Person'というカラムを追加します'.
子どもの定義を16歳以下とし,'Person'には,16歳以下ならばchildで,そうでなければ,性別を格納するようにします.
'Person'を追加したら'Sex'は不要なので削除します.

# 16歳以下をchildとする
#child・man・womanに分ける
def get_person(passenger):
    age,sex = passenger
    return 'child' if age < 16 else sex

#'Person'カテゴリを作成
train['Person'] = train[['Age','Sex']].apply(get_person,axis=1)
test['Person']    = test[['Age','Sex']].apply(get_person,axis=1)

# 'Sex'カテゴリを削除
train.drop(['Sex'],axis=1,inplace=True)
test.drop(['Sex'],axis=1,inplace=True)

文字を数値に落とし込む

数値に直さなければならないカラムは

  • Sex – 性別(male=男性、female=女性)
  • Embarked – 出港地(タイタニックへ乗った港)

get_dummies()でダミー変数を作り,join()で格納します.

#'Embarked'のダミー変数を作成
embark_dummies_titanic  = pd.get_dummies(train['Embarked'])
embark_dummies_test  = pd.get_dummies(test['Embarked'])
#格納する
train = train.join(embark_dummies_titanic)
test    = test.join(embark_dummies_test)
#'Embarked'は不要になったので削除
train.drop(['Embarked'], axis=1,inplace=True)
test.drop(['Embarked'], axis=1,inplace=True)

#'Person'
person_dummies_titanic  = pd.get_dummies(train['Person'])
person_dummies_titanic.columns = ['Child','Female','Male']

person_dummies_test  = pd.get_dummies(test['Person'])
person_dummies_test.columns = ['Child','Female','Male']

train = train.join(person_dummies_titanic)
test    = test.join(person_dummies_test)

train.drop(['Person'],axis=1,inplace=True)
test.drop(['Person'],axis=1,inplace=True)

学習

今までの確認

実際に確認してみると,全て数値に変わっていて,欠損値もないので大丈夫そうです.

train.head()

https://cdn-ak.f.st-hatena.com/images/fotolife/m/munemakun/20180708/20180708025000.png?1530985925

test.head()

https://cdn-ak.f.st-hatena.com/images/fotolife/m/munemakun/20180708/20180708024957.png?1530985907

train.isnull().any()

https://cdn-ak.f.st-hatena.com/images/fotolife/m/munemakun/20180708/20180708024954.png?1530985892

test.isnull().any()

https://cdn-ak.f.st-hatena.com/images/fotolife/m/munemakun/20180708/20180708024951.png?1530985876

ランダムフォレスト

ランダムフォレストで学習します.

X_train = train.drop("Survived",axis=1)
Y_train = train["Survived"]
X_test  = test.drop("PassengerId",axis=1)
# Random Forests

random_forest = RandomForestClassifier(n_estimators=100)
random_forest.fit(X_train, Y_train)
Y_pred = random_forest.predict(X_test)
random_forest.score(X_train, Y_train)

訓練データのaccuracyは98%になりました.
ですが,テストデータに対するaccuracyが重要なので,これで安心してはいけません.

結果は・・・

提出するためにはテストデータを予測したcsvファイルを提出しなければなりません.テストデータを予測して,ファイルを作成します.

submission = pd.DataFrame({
        "PassengerId": test["PassengerId"],
        "Survived": Y_pred
    })
submission.to_csv('titanic.csv', index=False)

ファイルを作成したら「submit Predictions」を押して提出ページにアクセスしファイルをアップロードすれば完了です.
https://cdn-ak.f.st-hatena.com/images/fotolife/m/munemakun/20180708/20180708032058.png?1530987695

スコアは71%でした.見事に過学習していました.80%は目指したいところですので今後改善していこうと思います.

https://cdn-ak.f.st-hatena.com/images/fotolife/m/munemakun/20180708/20180708032613.png?1530988534