【Kaggle】Spaceship Titanic 日本語で上位35%に入る方法を解説

eyecatch space titanic 機械学習
この記事は約15分で読めます。
※記事内には広告を含む場合がございます

概要

最初の目的地に向かう途中でアルファ ケンタウリを周回しているときに、
塵の雲の中に隠された時空異常と衝突してしまった!
悲しいことに、それは 1000 年前の名前の由来と同様の運命をたどりました。
船は無傷のままでしたが、乗客のほぼ半分が別の次元に運ばれました!

乗組員を救出し、失われた乗客を取り戻すために、宇宙船の損傷したコンピューター システムから回収された記録を使用して、異常によってどの乗客が輸送されたかを予測することが求められます。

戦略

基本的にはタイタニックの派生問題なので、同じような手法が使えます。
が、
タイタニックより、データの欠損がかなり多いです。
データの欠損を以下のコードで確かめます。

# alldata == 読み込んだCSVを全部合体したもの
print(alldata.isnull().sum())
PassengerId        0
HomePlanet       288
CryoSleep        310
Cabin            299
Destination      274
Age              270
VIP              296
RoomService      263
FoodCourt        289
ShoppingMall     306
Spa              284
VRDeck           268
Name             294
Transported        0
train_or_test      0

よってこの欠損した値を規則性を元にして、正しい or 近い 値で復元する事が求められます。

ノートブックURL

GistのURLはこちら(ノートブック形式で見れます。)

space_taitanic.ipynb
GitHub Gist: instantly share code, notes, and snippets.

[su_spoiler title=”ここをクリックでノートブックをプレビュー” icon=”plus-square-1″]

[/su_spoiler]

解説

# まだインストールしてない場合
!pip install pandas
!pip install scikit-learn
!pip install xgboost
!pip install lightgbm

ローカル環境などで、 まだライブラリをインストールしてない時用。

# データを読み込み、分ける
import pandas as pd
import numpy as np
 
train_raw = pd.read_csv("./spaceship-titanic/train.csv")
test_raw  = pd.read_csv("./spaceship-titanic/test.csv")

train_mid = train_raw.copy()
test_mid = test_raw.copy()

train_mid["train_or_test"] = 'train'
test_mid["train_or_test"] = 'test'
test_mid['Transported'] = False

alldata = pd.concat(
[
    train_mid,
    test_mid
],
    sort=False,
    axis=0
).reset_index(drop=True)

print('The size of the alldata data:' + str(alldata.shape))

kaggleからダウンロードした、データのCSVを使いやすいように分けます。
具体的にはtrainとtestに判別用にラベルを付けておきます
この後、全体に対して処理をするので、trainとtestで変数を分けることはしません。

# それっぽい値で埋める
alldata.HomePlanet.fillna(alldata.HomePlanet.mode()[0],inplace=True)
alldata.CryoSleep.fillna(alldata.CryoSleep.mode()[0],inplace=True)
alldata.Cabin.fillna(alldata.Cabin.mode()[0],inplace=True)
alldata.Destination.fillna(alldata.Destination.mode()[0],inplace=True)
alldata.Age.fillna(alldata.Age.mode()[0],inplace=True)
alldata.VIP.fillna(alldata.VIP.mode()[0],inplace=True)
alldata.Cabin.fillna(alldata.Cabin.mode()[0],inplace=True)
alldata.RoomService.fillna(alldata.RoomService.mode()[0],inplace=True)
alldata.FoodCourt.fillna(alldata.FoodCourt.mode()[0],inplace=True)
alldata.ShoppingMall.fillna(alldata.ShoppingMall.mode()[0],inplace=True)
alldata.Spa.fillna(alldata.Spa.mode()[0],inplace=True)
alldata.VRDeck.fillna(alldata.VRDeck.mode()[0],inplace=True)
alldata.Name.fillna(alldata.Name.mode()[0],inplace=True)
# 欠損確認
print(alldata.isnull().sum())

fillna関数で、適当な値で埋めます。
最後にprint(alldata.isnull().sum())で確認。

# テストデータの敬称(honorific)を抽出
alldata['honorific'] = alldata['Name'].map(lambda x: x.split(' ')[1].split('. ')[0])

# 敬称(honorific)の加工
alldata['honorific'].replace(['Col','Dr', 'Rev'], 'Rare',inplace=True) #少数派の敬称を統合
alldata['honorific'].replace('Mlle', 'Miss',inplace=True) #Missに統合
alldata['honorific'].replace('Ms', 'Miss',inplace=True) #Missに統合

# Cabinの頭文字
alldata['Cabin_ini'] = alldata['Cabin'].map(lambda x:str(x)[0])
alldata['Cabin_ini'].replace(['G','T'], 'Rare',inplace=True) #少数派のCabin_iniを統合

# 部屋番号を抽出する。
alldata.loc[:,['Room']] = alldata.PassengerId.apply(lambda x: x[0:4] )

alldata['Room'] = alldata['Room'].astype('int')

ここからデータ処理を始めていきます。
ここでは、敬称(honorific)、Cabinの頭文字、部屋番号を抽出しています。
少しでもデータを増やすのが狙いです。

# 部屋番号を抽出する。
alldata.loc[:,['Room']] = alldata.PassengerId.apply(lambda x: x[0:4] )

alldata['Room'] = alldata['Room'].astype('int')

# 欠損を埋める.dropna()また部屋が重複しているなら削除する.drop_duplicates()
guide_VIP=alldata.loc[:,['Room','VIP']].dropna().drop_duplicates('Room')
guide_Cabin=alldata.loc[:,['Room','Cabin']].dropna().drop_duplicates('Room')
guide_HomePlanet=alldata.loc[:,['Room','HomePlanet']].dropna().drop_duplicates('Room')
guide_Destination=alldata.loc[:,['Room','Destination']].dropna().drop_duplicates('Room')

# 仮の変更を元データにもどす。
alldata=pd.merge(alldata,guide_Cabin,how="left",on='Room',suffixes=('','_y'))
alldata=pd.merge(alldata,guide_VIP,how="left",on='Room',suffixes=('','_y'))
alldata=pd.merge(alldata,guide_HomePlanet,how="left",on='Room',suffixes=('','_y'))
alldata=pd.merge(alldata,guide_Destination,how="left",on='Room',suffixes=('','_y'))

PassengerIdには部屋番号が含まれています。
部屋番号を抽出した後、重複を削除します。

# CryoSleep 寝てる人は何も買うことができないはず
alldata['Expenses'] = alldata.iloc[:,7:12].sum(axis=1)
alldata.loc[:,['CryoSleep']]=alldata.apply(lambda x: True if x.Expenses ==0 and pd.isna(x.CryoSleep) else x,axis =1)

CryoSleep == True
寝てる人は何も購買行動をすることができないですよね?
Expenses(支出)はないはずです。

# 確認
print(alldata.columns)
# キャビンを忘れないでください。(Discussionから)
# split column and add new columns to df
# キャビンごとにデータを分けておく
alldata[['Deck', 'Num', 'Side']] = alldata['Cabin'].str.split('/', expand=True)
alldata['Num'] = alldata['Num'].astype('int')
# display the dataframe
alldata.head()

Kaggleのディスカッションで発見
キャビンをちゃんと分類して、追加するのを忘れんなよ
とのこと、意外とスコアに影響しました。

from sklearn.preprocessing import LabelEncoder

# カテゴリ特徴量についてlabel encoding
le_target_col = ['HomePlanet_y','VIP_y','honorific','Destination_y','Deck','Side']
le = LabelEncoder()
for col in le_target_col:
     alldata.loc[:, col] = le.fit_transform(alldata[col])

cat_col = ['HomePlanet_y','Cabin_ini','VIP_y','Destination_y']
alldata = pd.get_dummies(alldata, drop_first=True, columns=cat_col)

print(alldata.columns)

学習のためにlabel encodingします。

# 型確認
pd.set_option('display.max_columns', None)
pd.set_option('display.max_rows', None)
alldata.dtypes
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from xgboost import XGBClassifier
from lightgbm import LGBMClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import mean_squared_error
from sklearn.linear_model import LinearRegression
from xgboost import XGBClassifier

# 最初に統合したtrainとtestを分離
train = alldata.query('train_or_test == "train"')
test = alldata.query('train_or_test == "test"')

# ターゲット変数と、学習に不要なカラムを定義
target_col = 'Transported'
drop_col = ['PassengerId', 
            'Cabin',
            'Cabin_y',
            'Name',
            'Transported',
            'train_or_test',
            'honorific',
            'HomePlanet',
            'Destination'
           ]

# 学習に必要な特徴量のみを保持
train_feature = train.drop(columns=drop_col)

test_feature = test.drop(columns=drop_col)
train_tagert = train[target_col]
# trainデータを分割
X_train, X_test, y_train, y_test = train_test_split(
    train_feature, train_tagert, test_size=0.2, random_state=0, stratify=train_tagert)

# 色々試してみる。

print('='*20)
print('RandomForestClassifier')

rfc = RandomForestClassifier(n_estimators=100, max_depth=10,random_state=0)
rfc.fit(X_train,y_train)

print("accuracy of train set:", rfc.score(X_train,y_train))
print("accuracy of test set:", rfc.score(X_test,y_test))


lgb = LGBMClassifier(n_estimators=100,learning_rate=0.13,random_state=0)
lgb.fit(X_train, y_train)
print('='*20)
print('LGBMClassifier')
print(f'accuracy of train set: {lgb.score(X_train, y_train)}')
print(f'accuracy of train set: {lgb.score(X_test, y_test)}')

xgbc = XGBClassifier(n_estimators=120,
                     learning_rate = 0.13,
                     n_jobs=4,)
xgbc.fit(X_train, y_train)

print('='*20)
print(' XGBClassifier')
print(f'accuracy of train set: {xgbc.score(X_train, y_train)}')
print(f'accuracy of train set: {xgbc.score(X_test, y_test)}')

# 採用!!
result = xgbc.predict(test_feature)# 

学習します。
drop_col[]で指定した要素は、学習に不要 or 悪影響 なため除外します。
参考程度に、accuracy(正解率)も算出します
今回はの3つのアルゴリズムを試しました。
結果としてXGBClassifierを採用しています。predict()してresult変数に入れておきます。

結果

RandomForestClassifier

  • accuracy of train set: 0.8625251653724475
  • kaggle score : 0.7898

LGBMClassifier

  • accuracy of train set: 0.8957434570031636
  • kaggle score : 0.79448

XGBClassifier

  • accuracy of train set: 0.8819384526890998
  • kaggle score : 0.80032

accuracy は参考程度ですね、
実際のところはKaggleにアップロードして判断するほうがいいでしょう。


# アップロード用データを作る。
PassengerId = test["PassengerId"]

result_bool = np.array(result, dtype=bool)

ans = pd.DataFrame(result_bool,PassengerId,columns=["Transported"])

print(ans.dtypes)

ans.to_csv('./myans.csv',index_label = ["PassengerId"])

kaggleにアップするデータ(csv)を作ります。

まとめ

ディスカッションに書いてあることを試し最適な組み合わせを見つける。
そして、XGBClassifierで予測することでスコア: 0.80032を達成した。
これで上位35%ほどのスコア

Kaggleの機能で他の方のコードを見ることが出来るが、
スコア0.81より上は、正直Kaggle初心者の私には何をやっているのか不明だった。
逆にスコア0.8までは、地道なデータのNull埋めでのし上がる事ができた。

あとがき

スコア履歴

やったこと / スコア上昇量

ノートブックのCSV読み込みから、学習までの間のブロックの1かたまりにつき
大体0.02位ポイントが上がっている。

上の画像のans2.csvのスコア : 0.794は上位65%くらい

かなり細かいスコアの争いで、0.01上がるだけでもかなりランキングに影響する。
個人的には0.8が壁であると感じました。


~サイト支援のお願い~

以下のリンクはアフェリエイトリンクです。
この本は、私自身が実際に購入し、学習に活用した本です。

コメント

タイトルとURLをコピーしました