titanic挑戦2

次は精度を上げるための前準備を目的とする

以下の3工程をやる
クロスバリデーション:手元データで簡易的に精度を評価する
特徴量の可視化：どの変数が判別に効いているかを評価する
EDA:特徴量エンジニアリングのためにデータを可視化する

１．クロスバリデーション
trainデータだけで精度をだせるようにしたい。クロスバリデーションは訓練用データと評価用データに分けることで訓練用データだけで精度を出す
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275793/29a66247-0222-1398-3453-f4bd0ba57ee6.png)

今回は訓練用データを訓練用データと評価用データにわけて学習する

ソースコード

```
import numpy as np
import pandas as pd
import xgboost as xgb

from sklearn.metrics import accuracy_score
from sklearn.model_selection import StratifiedKFold

df_train = pd.read_csv("train.csv")

# 欠損値を埋める
df_train["Age"] = df_train["Age"].fillna(df_train["Age"].mean())
df_train["Cabin"] = df_train["Cabin"].fillna(df_train["Cabin"].mode())
df_train["Embarked"] = df_train["Embarked"].fillna(df_train["Embarked"].mode())

# train
df_train.drop(["Name", "Ticket", "Cabin"], axis=1, inplace=True)
df_train["Sex"] = df_train["Sex"].replace({"male": 0, "female": 1})
df_train = pd.get_dummies(df_train)

train_y = df_train["Survived"]
train_x = df_train.drop("Survived", axis=1)

clf = xgb.XGBClassifier()
clf.fit(train_x, train_y)

accuracies = []
cv = StratifiedKFold(n_splits=3, shuffle=True, random_state=0)
for train_idx, test_idx in cv.split(train_x, train_y):
    trn_x = train_x.iloc[train_idx, :]
    val_x = train_x.iloc[test_idx, :]

    trn_y = train_y.iloc[train_idx]
    val_y = train_y.iloc[test_idx]

    clf = xgb.XGBClassifier()
    clf.fit(trn_x, trn_y)

    pred_y = clf.predict(val_x)
    accuracies.append(accuracy_score(val_y, pred_y))

print(np.mean(accuracies))
```

分割はscikit-learnライブラリの中のStratifiedKFoldで実施できる
#sklearnで交差検証をする時に使うKFold,StratifiedKFold,ShuffleSplitのそれぞれの動作について簡単にまとめ
交差検証ではデータセットをk個に分割しモデルの訓練と評価をk回行います。得られたk個の評価値の平均をとった値を最終的なモデルのスコアとして扱う。具体的には1,データをk個のブロックに分けるこれを分割という。2,最初の分割1をtest set,残りの分割2~5をtraining setとしてモデルの学習と評価を行う。3,分割2をtest set,残りの分割1,3~5をtraining setとしてモデルの学習と評価を行うこの過程を繰り返す得られたk個の精度の平均値をモデルの評価値とする。
KFold(K-分割交差検証)
kは分割数で任意で決めることができる。もしk=5と設定すればまずデータを5つの同じサイズに分割する。
n_split：データの分割数．つまりk．検定はここで指定した数値の回数おこなわれる．
shuffle：Trueなら連続する数字でグループ分けせず，ランダムにデータを選択する．
random_state：乱数のシードを指定できる．
例

```
import numpy as np
from sklearn.model_selection import KFold

x = np.array([[1, 2], [3, 4], [1, 2], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4]])
y = np.array([0, 0, 1, 1, 1, 1, 1, 1, 1, 1])

kf = KFold(n_splits=5)
for train_index, test_index in kf.split(x, y):
    print("train_index:", train_index, "test_index:", test_index)

# output
# train_index: [2 3 4 5 6 7 8 9] test_index: [0 1]
# train_index: [0 1 4 5 6 7 8 9] test_index: [2 3]
# train_index: [0 1 2 3 6 7 8 9] test_index: [4 5]
# train_index: [0 1 2 3 4 5 8 9] test_index: [6 7]
# train_index: [0 1 2 3 4 5 6 7] test_index: [8 9]
```

StratifiedKFold
分布に大きな不均衡がある場合に用いるKFold
分布の比率を維持したままデータを訓練用とテスト用に分割する
n_splitがデータ数が最も少ないクラスのデータ数よりも多いと怒られる

```
import numpy as np
from sklearn.model_selection import StratifiedKFold

x = np.array([[1, 2], [3, 4], [1, 2], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4]])
y = np.array([0, 0, 1, 1, 1, 1, 1, 1, 1, 1])

skf = StratifiedKFold(n_splits=2)
for train_index, test_index in skf.split(x, y):
    print("train_index:", train_index, "test_index:", test_index)

# output
# train_index: [1 6 7 8 9] test_index: [0 2 3 4 5]
# train_index: [0 2 3 4 5] test_index: [1 6 7 8 9]
```
変数の宣言と実際の分割の２ステップが必要



```
from sklearn.model_selection import StratifiedKFold

#変数の宣言
cv=StratifiedKFold(n_split=3,shuffle=True,random_state=0)
#実際の分割
for train_idx,test_idx in cv.split(train_x,train_y):
    trn_x=train_x.iloc[train_idx,:]
    val_x=train_x.iloc[test_idx,:]
    
    trn_y=train_y.iloc[train_idx]
    val_y=train_y.iloc[test_idx]
```

iloc

```

#
# 行番号を指定
#
 
df.iloc[[2,3]]
#----------------------------
#        col_0  col_1  col_2
# row_2    100    200    300
# row_3   1000   2000   3000
#----------------------------
 
#
# 列番号を指定
#
 
df.iloc[:, [1,2]]
#----------------------------
#        col_1  col_2
# row_0      2      3
# row_1     20     30
# row_2    200    300
# row_3   2000   3000
#----------------------------
 
#
# 行、列番号を指定
#
 
df.iloc[[2,3], [1,2]]
#----------------------------
#        col_1  col_2
# row_2    200    300
# row_3   2000   3000
#----------------------------
 
#
# 行、列番号をスライスで指定
#
 
df.iloc[2:4,1:3]
#----------------------------
#        col_1  col_2
# row_2    200    300
# row_3   2000   3000
#----------------------------
 
#
# boolean で指定
#
 
df.iloc[[False,False,True,True],[False,True,True]]
#----------------------------
#        col_1  col_2
# row_2    200    300
# row_3   2000   3000
#---------------------------
```

分割のための変数が数字であるためilocが必要であることに注意する。random_stateを固定しないと分割が変わり結果が変わってしまうためrandom_stateはどの値でもよいので同じ値を使用する。

2,特徴量の可視化
特徴量の重要度とは予測の時にどの列の重要度が高いかであり値はclf.feature_importances_で出すことができる

ソースコード

```
%matplotlib inline
import numpy as np
import pandas as pd
import xgboost as xgb

from sklearn.metrics import accuracy_score
from sklearn.model_selection import StratifiedKFold

df_train = pd.read_csv("train.csv")

# 欠損値を埋める
df_train["Age"] = df_train["Age"].fillna(df_train["Age"].mean())
df_train["Cabin"] = df_train["Cabin"].fillna(df_train["Cabin"].mode())
df_train["Embarked"] = df_train["Embarked"].fillna(df_train["Embarked"].mode())

# train
df_train.drop(["Name", "Ticket", "Cabin"], axis=1, inplace=True)
df_train["Sex"] = df_train["Sex"].replace({"male": 0, "female": 1})
df_train = pd.get_dummies(df_train)

train_y = df_train["Survived"]
train_x = df_train.drop("Survived", axis=1)

clf = xgb.XGBClassifier()
clf.fit(train_x, train_y)

accuracies = []
feature_importances = []
cv = StratifiedKFold(n_splits=3, shuffle=True, random_state=0)
for train_idx, test_idx in cv.split(train_x, train_y):
    trn_x = train_x.iloc[train_idx, :]
    val_x = train_x.iloc[test_idx, :]

    trn_y = train_y.iloc[train_idx]
    val_y = train_y.iloc[test_idx]

    clf = xgb.XGBClassifier()
    clf.fit(trn_x, trn_y)

    pred_y = clf.predict(val_x)
    accuracies.append(accuracy_score(val_y, pred_y))
    feature_importances.append(clf.feature_importances_)

print(np.mean(accuracies))

df_fimp = pd.DataFrame(feature_importances, columns=train_x.columns)
df_fimp.plot(kind="box", rot=90)
```
結果
0.8103254769921436
<matplotlib.axes._subplots.AxesSubplot at 0x1a1d31b470>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275793/db13b600-1969-36ed-ce05-3b6aca9c194a.png)

機械学習の精度を上げる
機械学習の精度を上げるにはだいたい3つの手法がある
・特徴量エンジニアリング
・機械学習のハイパーパラメータのチューニング
・アンサンブル
この中で一般的に特徴量エンジニアリングが1番大きな効果が出る
特徴量エンジニアリングとは？
機械学習に入れるための特徴量とも呼ばれる追加の予測因子を構築してデータセットにy追加すること。要するに学習するデータに新しい説明変数を足してあげることで機械学習の精度を上げること
ex)学校の中間テストの点数と、その子が希望校に合格したかというテーブルがあるとする。この時3教科の合計点を特徴量に加えると機械学習の精度が上がる

特徴量エンジニアリングは１、仮説を立て２、コードを書き、３、機械学習を回して精度が上がるかを確かめるという3ステップを繰り返す。
１の仮説を立てるためにまずはEDA(探索的データ解析)を行う
ここでは主にseabornというpythonのライブラリを用いて可視化をする
EDA
・ヒストグラムを利用して数値の分布を見る
・ヒートマップを利用して特徴量同士の相関を見る。
・棒グラフ、箱ひげ図を利用してカテゴリアルデータで生存に差があるか見る

数値の分布をみることはとても大事
・データの外れ値により機械学習アルゴリズムが誤った学習をする→外れ値を除いてから機械学習に使用することで機械学習の精度がupする
・データが正規分布に従っていないため機械学習のアルゴリズムが学習しづらい→対数をとってから機械学習に使用することで機械学習に使用することで機械学習の精度がupする、などの効能がある
正規分布とは左右対称の連続型の確率分布
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275793/3a41d307-981b-75f0-a6d3-385432926335.png)
今回はAgeの分布を見ていく
ここではヒストグラムとヒストグラムのカーネル密度推定の結果を重ね合わせるsns.distplot()を使用する

```
import seaborn as sns

l = [9, 21, 24, 31, 33, 35, 37, 39, 42, 44, 53, 62]

sns.distplot(l)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275793/6e152cf6-427d-3649-8b59-3471e6e960a0.png)

次に実際のデータで分布を見る

```
import pandas as pd
import seaborn as sns
train = pd.read_csv("train.csv")
train.head()
```
![image.png](https://qiita-image-store.s3.ap-northeast-
1.amazonaws.com/0/275793/a501d5cf-84f7-2f47-6749-9b128ef65f4d.png)

```
sns.distplot(train["Age"].fillna(100))
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275793/c760c961-d791-7d4f-b113-9618d75008d9.png)

次に生存者(survived==0)と非生存者(survived==1)でヒストグラムを比較してみる
・０～１０は生存者が多い
・NaNは生存者が少ない
ようにみえる

特徴量エンジニアリング：特徴量同士の相関
相関とは：2つ以上の変数があったとき一方の変数が変化すると他方もそれに応じて変化する関係を指す。

```
%matplotlib inline
import scipy.stats as st         # 相関係数を計算する用のライブラリ
import matplotlib.pyplot as plt  # 散布図を描くためのライブラリ

scores_math = [9, 21, 24, 31, 33, 35, 37, 39, 42, 44, 53, 62]
scores_science = [12, 15, 31, 21, 42, 21, 35, 43, 53, 51, 43, 70]

print("相関係数 =", st.pearsonr(scores_math, scores_science)[0])
plt.scatter(scores_math, scores_science)
plt.xlabel("math")
plt.ylabel("science")
plt.show()
```
結果
相関係数 = 0.8679236556726426
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275793/dea25a82-0853-1478-37b7-2e5d1f20050e.png)

次に実データで相関を計算してみる
pandasの場合、データフレーム.corr("pearson")でデータフレーム内の全対全の相関係数を計算してくれる。すべての相関係数を見るのは大変なのでまずは目的変数と近いところから見ていくとよいのでSurvivedと相関の高い項目を見ていく

```
# 相関 - Survivedのみ
df_train = pd.read_csv("train.csv")

# 性別は01変換
df_train["Sex"] = df_train["Sex"].replace({"male": 0, "female": 1})

# Embarkedはダミー変数化する
df_train = pd.concat([df_train, pd.get_dummies(train["Embarked"])], axis=1)
df_train.drop(["Embarked"], axis=1, inplace=True)

# Sex, Fareは正の相関が強く、Pclassは負の相関が高い。
# Sexは女性・子どもは優先して助けられたということによると思われる。
# 次に相関が高いのはPclassとFare。お金持ちの方が高いところにいたため、生存率が高いと思われる。
df_train.corr("pearson")["Survived"].to_frame()
```
結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275793/af125420-f288-b1fd-aefc-c57f11aff07c.png)
実データで相関を計算（全体全の相関）
sns.clustermapで全体全の相関を可視化する
cmap:デフォルトのカラーコードではわかりづらいため赤青に変更している
annot:実際の相関係数をclustermapの上に乗せている

```
sns.clustermap(df_train.corr("pearson"),annot=True,cmap=sns.color_palette("coolwarm",10))
```
![image.png](https://qiita-image-store.s3.ap-northeast-
1.amazonaws.com/0/275793/1493908e-ea20-f89d-ea40-4770c880a7c7.png)

特徴量エンジニアリング：層別解析
先ほどは全体を俯瞰的に見た今回はより繊細に見ていく
ここでは各変数の値で生存する割合が変化するかを詳細にみています
sns.countplot()を使用する
xとhueのオプションを使うことで層別に生存、非生存の数をカウントしてくれる
hueは色相という意味で色分けしたい場合に使われるオプション

```
df_train=pd.read_csv("train.csv")
for col in ['Parch','SibSp','Pclass','Sex','Embarked','Age']:
    sns.countplot(x=col,hue='Survived',data=df_train)
    #グラフの微調整
    plt.xticks(rotation=90)
    plt.title(col)
    plt.legend(['Not Survived','Survived'],loc='upper right')
    plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275793/7466837e-112b-6b47-911e-c73914228fa3.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275793/b9d13fbd-18f0-8be3-87f4-f325f9b4d8c8.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275793/c50a40fe-d874-4209-8375-013bdb83d13b.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275793/1b8d8b8d-e6f4-4a95-29fe-2079e1b3ce76.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275793/9b51919b-d61e-3c5a-4bcd-2600cc082755.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275793/68d47b1f-5de7-2ca4-9c3a-5801a5d0105e.png)

parchとsibspは0だとnotsurvived率が高いことがわかる
parchとsibspが多いとnotsurvivedが多いことがわかる

catplot
Ageの場合は生存した人としなかった人の年齢の分布が知りたいという問題設定に変えてみる
sns.catplot()を使用する

```
sns.catplot(x="Survived", y="Age", data=df_train, kind='violin')
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275793/a15dad7f-2f6e-40d5-94c4-89ba69b67bb8.png)
年齢とsurvivedはあまり関係なさそうにみえる

まとめると
・クロスバリデーション
・→訓練用データだけから精度をだすことができた
・特徴量の重要度
・→判別に効いている特徴量を出すことができた
・EDA
以下の3つの仮説が成り立った
・PassengerIdを除くと良いかも
・Parch,SibSpが0の人はSurvived率が低いかも
・０～１０は生存者が多いかも