(Kaggle)決定木とランダムフォレストを使ったモデルを用いてTitanic生存者を予測した

#1　はじめに
　機械学習を学ぶ上でのチュートリアルとして皆さん必ず通る道であろうタイタニック号生存者の予測について、私が行った方法を備忘として記録します。

使用したバージョンについて

+ Python　3.7.6
+ numpy　1.18.1
+ pandas　 1.0.1
+ matplotlib 3.1.3
+ seaborn 0.10.0

使用したデータはKaggle登録後、こちらからダウンロードしました。
https://www.kaggle.com/c/titanic





#2　プログラムについて

#Importしたライブラリ等

```python

import pandas as pd
import numpy as np
df = pd.read_csv('train.csv') 

import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline 
```

データフレームdfとして定義しました。
ここで、読み込んだデータのうち最初の5行を見てみます。

```python

df.head()
```

![001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567823/9e01650c-0e3b-404f-99b2-295084120007.png)


　Survivedが1で生存、0で死亡を表します。その他の要因が何を指しているかは公式サイトをご覧ください。

#ヒストグラム
次にヒストグラムを見てみます。


```python

df.hist(figsize=(12,12))
plt.show()
```

![002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567823/5b6ac222-e29d-85f1-a497-68652a085a1a.png)


　歳は20～30代が多く、Pclass(等級）は3（一番安い）が多いことが分かります。

#相関係数を確認


```python

plt.figure(figsize = (15,15))
sns.heatmap(df.corr(),annot = True)

```


![003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567823/2c3b479f-7c9c-dda5-c804-0e523e9330e7.png)

　Survivedに対して相関係数が高い指標は0.26：Fare、-0.34：Pclassとなります。Pclass等級が決まればFare：運賃も自ずと決まると思いますが、二つは違う指標なんですね。。



#欠損値の取り扱い

```python

df.isnull().sum()
```

PassengerId      0
Survived         0
Pclass           0
Name             0
Sex              0
Age            177
SibSp            0
Parch            0
Ticket           0
Fare             0
Cabin          687
Embarked         2
dtype: int64

　Age:年齢とCabin:乗り組み番号が入っていない量が多いことが分かります。欠損値の取り扱いについては、下記を参考にしました。
https://qiita.com/0NE_shoT_/items/8db6d909e8b48adcb203

　今回は、年齢に関しては中央値を代入することとしました。また、Embarked：乗船地は最も多いSを代入しています。
　その他の欠損している値は削除しました。


```python

from sklearn.model_selection import  train_test_split
#欠損値処理
df['Fare'] = df['Fare'].fillna(df['Fare'].median())
df['Age'] = df['Age'].fillna(df['Age'].median())
df['Embarked'] = df['Embarked'].fillna('S')

#カテゴリ変数の変換
df['Sex'] = df['Sex'].apply(lambda x: 1 if x == 'male' else 0)
df['Embarked'] = df['Embarked'].map( {'S': 0 , 'C':1 , 'Q':2}).astype(int)

#不要なcolumnを削除
df = df.drop(['Cabin','Name','PassengerId','Ticket'],axis =1)
```

#学習データとテストデータの分類

```python

train_X = df.drop('Survived',axis = 1)
train_y = df.Survived
(train_X , test_X , train_y , test_y) = train_test_split(train_X, train_y , test_size = 0.3 , random_state = 0)
```

　今回は、テストデータを3割（test_size=0.3）としています。

#機械学習による予測（決定木）

```python

from sklearn.tree import DecisionTreeClassifier
clf = DecisionTreeClassifier(criterion='gini', random_state = 0)
clf = clf.fit(train_X , train_y)
pred = clf.predict(test_X)

#正解率の算出
from sklearn.metrics import (roc_curve , auc ,accuracy_score)
pred = clf.predict(test_X)
fpr, tpr, thresholds = roc_curve(test_y , pred,pos_label = 1)
auc(fpr,tpr)
accuracy_score(pred,test_y)
```

　決定木パラメータの取り扱いについてはこちらを参考にしました。
http://data-analysis-stats.jp/2019/01/14/%E6%B1%BA%E5%AE%9A%E6%9C%A8%E5%88%86%E6%9E%90%E3%81%AE%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF%E8%A7%A3%E8%AA%AC/




criterion='gini', 0.7798507462686567
criterion='entropy', 0.7910447761194029

若干entropyの方が正解率が上がりました。参考urlによると、

>使い分けのポイントとしては、ジニ係数の方が、連続データを得意としており、エントロピーはカテゴリーデータを得意としていると言われています。ジニ係数は、誤分類を最小化するのに対して、エントロピーは探索的に基準値を探していきます。


とのことです。今回は性別、乗船地等カテゴリーデータが多かったことからエントロピーが適切なのでしょう。

こちらも決定木に関して詳しく載っており参考になりました。
https://qiita.com/3000manJPY/items/ef7495960f472ec14377



#機械学習による予測（ランダムフォレスト）


```python

from sklearn.ensemble import RandomForestClassifier
clf = RandomForestClassifier(n_estimators = 10,max_depth=5,random_state = 0)　#ランダムフォレストのインスタンスを生成する。
clf = clf.fit(train_X , train_y)　#教師ラベルと教師データを用いてfitメソッドでモデルを学習
pred = clf.predict(test_X)
fpr, tpr , thresholds = roc_curve(test_y,pred,pos_label = 1)
auc(fpr,tpr)
accuracy_score(pred,test_y)
```
0.8283582089552238

　ランダムフォレストには、分類用（Classifier）と回帰分析用（Regressor）があります。今回は、生存か死亡かを分類することが目的なため分類用を用いて実行します。

　RandomForestClassifierクラスの学習パラメータ、引数はこちらを見ながら動かしてみました。
https://data-science.gr.jp/implementation/iml_sklearn_random_forest.html

　n_estimatorsを上げていくと正解率がサチレートしていきました。n_estimatorsやニューラルネットワークで使用するepochsは大きな値で計算しても取り越し苦労に終わることが多いと言われています。その内容、対策については下記をご覧ください。
https://amalog.hateblo.jp/entry/hyper-parameter-search



#予測結果の出力

　先ほどの結果から決定木よりランダムフォレストのほうがより正解率が高いことが分かりました。
　従って、今回はランダムフォレストにより予測することとしました。



```python

fin = pd.read_csv('test.csv')
fin.head()

passsengerid = fin['PassengerId']
fin.isnull().sum()
fin['Fare'] = fin['Fare'].fillna(fin['Fare'].median()) 
fin['Age'] = fin['Age'].fillna(fin['Age'].median())
fin['Embarked'] = fin['Embarked'].fillna('S')

#カテゴリ変数の変換
fin['Sex'] = fin['Sex'].apply(lambda x: 1 if x == 'male' else 0)
fin['Embarked'] = fin['Embarked'].map( {'S': 0 , 'C':1 , 'Q':2}).astype(int)

#不要なcolumnを削除
fin= fin.drop(['Cabin','Name','Ticket','PassengerId'],axis =1)
#ランダムフォレストで予測
predictions = clf.predict(fin)

submission = pd.DataFrame({'PassengerId':passsengerid, 'Survived':predictions})
submission.to_csv('submission.csv' , index = False)
```

![004.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567823/2e11f8bb-9df2-fa2b-7014-bebfdcf4512f.png)


4900位/16000位となりました。
もっと精進したいと思います。