AI 機械学習コンペ タイタニック号の生存者予測 に参加してみた

Pythonでscikit-learnやPandasといったライブラリを使ってあれこれ試行錯誤( 後述 )しながらプログラミング
・最終的なスコアは 0.81339
・順位は 601位 / 9,525チーム
・もうちょっとスコアをあげたかったけど 上位10%に入った し この件をQiitaやブログに書いている 日本人にはだいたい勝っている と思うのでよしとする


試行錯誤したことのメモ
・ワンホットエンコーディングは効果高い
多項式特徴量を使って特徴量の種類を増やすと良かった
・特徴量のスケール変換は忘れずに
・年齢や運賃などの連続値はビニングする。ビニングしたらランダムフォレストより線形の方が精度がよくなった
・線形SVCとロジスティック回帰で試して、最終的にはロジスティック回帰を選択
・苗字は効果有り。Nameから苗字だけ取り出す
・Mr. や Mrs等の敬称は逆効果
・チケット番号は効果有り
・年齢の欠損値の補完は属性が近い人のを使う
・メモリはたくさん必要 自宅PCの24GBでは 足りなくて買い足した

# Titanic: Machine Learning from Disaster
# https://www.kaggle.com/c/titanic/
# 2018/1/10 18:26 JST
# Score 0.81339 594位 /  9,567 チーム
# LogisticRegression Best parameters: {'C': 70.0, 'random_state': 0}
# LogisticRegression Best score: 0.837

import numpy as np
import pandas as pd
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import MinMaxScaler
from sklearn.grid_search import GridSearchCV
from IPython.display import clear_output, Image, display
import time

start = time.time()

# データの読み込み
train = pd.read_csv("train.csv", dtype={"Age": np.float64}, )
test  = pd.read_csv("test.csv", dtype={"Age": np.float64}, )

# 前処理
## 訓練データと評価データを前処理のために一旦合体する
train_test = pd.concat([train, test], ignore_index=True)

## 欠損値の補正
### Embarked の欠損値の補足 訓練データに2件ある
train_test.Embarked = train_test.Embarked.fillna("S")
    
### Fare が欠損値の補足 評価データに1件ある
train_test.Fare = train_test.Fare.fillna(test.query("Pclass==3").Fare.median())

### 年齢の欠損値の補正
#### 階層ごとのテストデータの年齢の中央値とする
AgeFill={}
AgeFill[1] = train_test.query("Age>0 & Pclass==1").Age.median()
AgeFill[2] = train_test.query("Age>0 & Pclass==2").Age.median()
AgeFill[3] = train_test.query("Age>0 & Pclass==3").Age.median()

for i in range(len(train_test)):
    if np.isnan(train_test['Age'][i]):
        if train_test['Pclass'][i] == 1:
            train_test['Age'][i] = AgeFill[1]
        elif train_test['Pclass'][i] == 2:
            train_test['Age'][i] = AgeFill[2]
        else:
            train_test['Age'][i] = AgeFill[3]

## ビニング
### 年齢 16分割
bins = np.linspace(train_test['Age'].min(), train_test['Age'].max(), 17)
train_test['Age_bin'] = np.digitize(train_test['Age'], bins=bins)

### 運賃 80分割
bins = np.linspace(train_test['Fare'].min(), train_test['Fare'].max(), 81)
train_test['Fare_bin'] = np.digitize(train_test['Fare'], bins=bins)

# 苗字の取り出し
train_test['FamilyName'] = train_test.Name.str.extract('([A-Za-z]+)\,', expand=False)
 
## 使わない列を削除
del train_test['Cabin']
del train_test['Survived']
del train_test['Name']
del train_test['Age']
del train_test['Fare']

## ワンホットエンコーディング
train_test_dummies = pd.get_dummies(train_test, columns=['Pclass', 'Sex', 'Embarked', 'Ticket', 'Age_bin', 'Fare_bin', 'FamilyName'])

## 訓練データと評価データに分離
train_dummies = train_test_dummies[train_test_dummies.PassengerId < 892]
test_dummies = train_test_dummies[train_test_dummies.PassengerId >= 892]
del train_dummies['PassengerId']
del test_dummies['PassengerId']

## スケール変換
scaler = MinMaxScaler()
scaler.fit(train_dummies)
train_dummies_scaled= scaler.transform(train_dummies)
test_dummies_scaled= scaler.transform(test_dummies)

### 多項式特徴量を追加
from sklearn.preprocessing import PolynomialFeatures
poly = PolynomialFeatures(degree=2).fit(train_dummies_scaled)
train_dummies_scaled_poly = poly.transform(train_dummies_scaled)
test_dummies_scaled_poly = poly.transform(test_dummies_scaled)

# LogisticRegressionでグリッドサーチ
parameters = {
        'C'      : [ 50.0, 60.0, 70.0, 80.0, 100.0, 150.0],
        'random_state'      : [0]
}
svc = GridSearchCV(LogisticRegression(), parameters, cv=3)
svc.fit(train_dummies_scaled_poly, train["Survived"])

print('LogisticRegression Best parameters: {}'.format(svc.best_params_))
print('LogisticRegression Best score: {:.3f}'.format(svc.best_score_))

predictions = svc.predict(test_dummies_scaled_poly)
submission = pd.DataFrame({
        "PassengerId": test["PassengerId"],
        "Survived": predictions
    })
submission.to_csv('submissionLogisticRegression.csv', index=False)
print ("elapsed_time:{}".format( time.time() - start ) )