titanic挑戦3

機械学習の精度を上げることを目的としたい
前書いた記事から導かれた仮説が
・PaseengerIdを除くと良いかも
・FamilySizeを入れると良いかも
・Parch,SibSpが0の人はSurvived率が低いかも
・0~10歳は生存者が多いかも
の4つだった。この仮説を検証する

1,処理を関数化
今回は処理があまり大きくないため、
・トレーニングデータとテストデータの前処理用の関数
・トレーニングデータの評価用の関数
・テストデータ予測用の関数
・特徴量の重要度を出力
の4つを関数化する

ソースコードが以下

```
%matplotlib inline
import warnings
import numpy as np
import pandas as pd
import xgboost as xgb
import matplotlib.pylab as plt

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

warnings.filterwarnings('ignore')


def validate(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)
        feature_importances.append(clf.feature_importances_)
        accuracies.append(accuracy_score(val_y, pred_y))
    print(np.mean(accuracies))
    return accuracies, feature_importances


def plot_feature_importances(feature_importances, cols):
    df_fimp = pd.DataFrame(feature_importances, columns=cols)
    df_fimp.plot(kind="box", rot=90)


def preprocess_df(df):
    # CabinはこのあとDropするので、コードから削除
    df["Age"] = df["Age"].fillna(df["Age"].mean())
    df["Embarked"] = df["Embarked"].fillna(df["Embarked"].mode())
    
    # 列の削除
    df.drop(["Name", "Ticket", "Cabin"], axis=1, inplace=True)
    
    # Sexの01化とEmbarkedのダミー化 
    df["Sex"] = df["Sex"].replace({"male": 0, "female": 1})
    df = pd.get_dummies(df)

    return df


# test dataのpredict
def predict_df(train_x, train_y, test_x, df_test_raw, path_output="result.csv"):
    clf = xgb.XGBClassifier()
    clf.fit(train_x, train_y)
    preds = clf.predict(test_x)
    
    _df = pd.DataFrame()
    _df["PassengerId"] = df_test_raw["PassengerId"]
    _df["Survived"] = preds
    _df.to_csv(path_output, index=False)


# デバッグするときはmain関数から外して、直で叩く方が楽です。
def main():
    df_train = pd.read_csv("train.csv")

    # ここは前処理
    train_y = df_train["Survived"]
    train_x = df_train.drop("Survived", axis=1)

    train_x = preprocess_df(train_x)
    accuracies, feature_importances = validate(train_x, train_y)
    plot_feature_importances(feature_importances, train_x.columns)

    flag_product = True
    if flag_product:
        df_test = pd.read_csv("test.csv")
        df_test_raw = df_test.copy()
        test_x = preprocess_df(df_test)
        predict_df(train_x, train_y, test_x, df_test_raw, "result.csv")


#  `if __name__ == '__main__':` はおまじないのようなモノと思ってください。
if __name__ == '__main__':
    main()
```
結果

```
0.8103254769921436
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275793/c6f2320a-8352-95dc-d339-1ea8e8c7656f.png)
仮説検証１、PassengerIdを除く
列を除くにはdf.drop(列名,axis=1,inplace=True)でOK

```
def preprocess_df(df):
    # CabinはこのあとDropするので、コードから削除
    df["Age"] = df["Age"].fillna(df["Age"].mean())
    df["Embarked"] = df["Embarked"].fillna(df["Embarked"].mode())
    
    # 列の削除
    df.drop(["Name", "Ticket", "Cabin", "PassengerId"], axis=1, inplace=True)
    
    # Sexの01化とEmbarkedのダミー化 
    df["Sex"] = df["Sex"].replace({"male": 0, "female": 1})
    df = pd.get_dummies(df)

    return df

if __name__ == '__main__':
    main()
```
結果

```
0.8226711560044894
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275793/db6f55c0-2a45-db5d-8467-f59a602aaf41.png)
cvの値が上がることを確認できる

２、仮説検証-FamilySize列を作成
次にSibSpとParchの数を足してFamilySizeという列をつくってみる
preprocess_dfの中身だけ書き換えればok

```
def preprocess_df(df):
    # CabinはこのあとDropするので、コードから削除
    df["Age"] = df["Age"].fillna(df["Age"].mean())
    df["Embarked"] = df["Embarked"].fillna(df["Embarked"].mode())
    df["FamilySize"] = df["SibSp"] + df["Parch"] + 1
   
    # 列の削除
    df.drop(["Name", "Ticket", "Cabin", "PassengerId"], axis=1, inplace=True)
    
    # Sexの01化とEmbarkedのダミー化 
    df["Sex"] = df["Sex"].replace({"male": 0, "female": 1})
    df = pd.get_dummies(df)

    return df

if __name__ == '__main__':
    main()
```
結果

```
0.8237934904601572
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275793/32559f42-4c4d-4ed5-2598-36af1628f30d.png)
cvの平均値が上がることがわかる

3,Parch,SibSpが0の人のフラグを立てる
Parch,SibSpが0の人のフラグを立てる
先ほどFamilySizeという変数も作ったのでこちらの値が1の人も別途フラグを立ててみる

```
def preprocess_df(df):
    # CabinはこのあとDropするので、コードから削除
    df["Age"] = df["Age"].fillna(df["Age"].mean())
    df["Embarked"] = df["Embarked"].fillna(df["Embarked"].mode())
    df["FamilySize"] = df["SibSp"] + df["Parch"] + 1
   
    # 列の削除
    df.drop(["Name", "Ticket", "Cabin", "PassengerId"], axis=1, inplace=True)

    # Sexの01化とEmbarkedのダミー化 
    df["Sex"] = df["Sex"].replace({"male": 0, "female": 1})
    df = pd.get_dummies(df)
    
    # Parch, SibSp, FamilySize関連のFlag
    df["None_Parch"] = [1 if val == 0 else 0 for val in df["Parch"]]
    df["None_SibSp"] = [1 if val == 0 else 0 for val in df["SibSp"]]
    df["None_Family"] = [1 if val == 1 else 0 for val in df["FamilySize"]]

    return df

if __name__ == '__main__':
    main()
```
結果

```
0.819304152637486
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275793/12bb9b7b-ad76-db4a-9abd-2e1fcacff28a.png)
特徴量の重要度としても0なので全く使われていないことがわかる。今回これらは特徴量に入れなくてよさそう

４、０～１０歳は生存者が多いかも

```
def preprocess_df(df):
    # CabinはこのあとDropするので、コードから削除
    df["Age"] = df["Age"].fillna(df["Age"].mean())
    df["Embarked"] = df["Embarked"].fillna(df["Embarked"].mode())
    df["FamilySize"] = df["SibSp"] + df["Parch"] + 1
   
    # 列の削除
    df.drop(["Name", "Ticket", "Cabin", "PassengerId"], axis=1, inplace=True)

    # Sexの01化とEmbarkedのダミー化 
    df["Sex"] = df["Sex"].replace({"male": 0, "female": 1})
    df = pd.get_dummies(df)
    
    # Parch, SibSp, FamilySize関連のFlag
    df["Flag_Children"] = [1 if val < 11 else 0 for val in df["Age"]]

    return df

if __name__ == '__main__':
    main()
```

結果

```
0.819304152637486
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275793/c3661f3b-7aa2-3592-a4a8-c51cdfbc77c3.png)
今回はこれも特徴量にいれなくてよさそう

よって
PassengerIdを除くと良いかも、とFamily_sizeを入れると良いかもを採用する
（実際はクロスバリデーションで評価した値と実際のスコアは一致するとは限らない。今回の場合だと、Family_sizeを入れて計算した値をSubmitすると少しスコアが下がる)