【Kaggle挑戦記】Spaceship Titanic 攻略 #14:GroupSizeのカテゴリ化。重要度への反映と精度のトレードオフ
1. 実験:人数の「意味」をモデルに教える
前回の分析で判明した「4人グループの異常な転送率(64%)」をモデルに直接認識させるため、GroupSizeを Solo / Small / Large の3カテゴリに分類しました。 連続的な数値としてではなく、独立した属性として扱うことで、モデルの「気付き」を促す狙いです。
2. 【実装】GroupCategory導入版・フルソースコード
分析に基づき、最も転送率が高かった2〜4人を「Small」と定義。これを特徴量として追加したコードです。
import pandas as pd
import numpy as np
import lightgbm as lgb
# 1. データの読み込み
train = pd.read_csv('train.csv')
test = pd.read_csv('test.csv')
# グループサイズ計算用
all_df = pd.concat([train, test], axis=0)
all_groups = all_df['PassengerId'].apply(lambda x: x.split('_')[0]).value_counts()
# 2. 特徴量エンジニアリング
spend_cols = ["RoomService", "FoodCourt", "ShoppingMall", "Spa", "VRDeck"]
def get_group_category(size):
if size == 1:
return 'Solo'
elif 2 <= size <= 4:
return 'Small' # 転送率が極めて高い層
else:
return 'Large' # 大家族層
for df in [train, test]:
# --- A. 支出の論理補完 ---
df[spend_cols] = df[spend_cols].fillna(0)
total_spend = df[spend_cols].sum(axis=1)
df.loc[(df['CryoSleep'].isnull()) & (total_spend > 0), 'CryoSleep'] = False
df.loc[(df['CryoSleep'].isnull()) & (total_spend == 0), 'CryoSleep'] = True
df['Age'] = df['Age'].fillna(df['Age'].median())
# --- B. Cabinの物理分解 ---
df['Cabin'] = df['Cabin'].fillna('U/U/U')
df['Cabin_Deck'] = df['Cabin'].apply(lambda x: x.split('/')[0])
df['Cabin_Side'] = df['Cabin'].apply(lambda x: x.split('/')[-1])
# --- C. GroupSizeのカテゴリ化 ---
group_id = df['PassengerId'].apply(lambda x: x.split('_')[0])
df['GroupSize'] = group_id.map(all_groups)
df['GroupCategory'] = df['GroupSize'].apply(get_group_category)
# 3. 特徴量の選定
features = [
"CryoSleep", "Age", "RoomService", "FoodCourt", "ShoppingMall",
"Spa", "VRDeck", "Cabin_Deck", "Cabin_Side", "GroupCategory"
]
# 4. 整形と学習
X = pd.get_dummies(train[features], drop_first=True)
y = train["Transported"].astype(int)
X_test = pd.get_dummies(test[features], drop_first=True)
X, X_test = X.align(X_test, join='left', axis=1, fill_value=0)
model = lgb.LGBMClassifier(n_estimators=100, learning_rate=0.05, random_state=1)
model.fit(X, y)
# 5. 予測と保存
predictions = model.predict(X_test)
output = pd.DataFrame({'PassengerId': test['PassengerId'], 'Transported': predictions.astype(bool)})
output.to_csv('sub_v14_group_cat.csv', index=False)
# 6. 分析ログの出力
print("\n 特徴量寄与度 (Importance) - Top 15")
importances = pd.DataFrame({'Feature': X.columns, 'Importance': model.feature_importances_}).sort_values(by='Importance', ascending=False)
print(importances.head(15))
3. 結果と分析:重要度の浮上と精度の乖離
リーダーボードの結果は 0.79985。惜しくも0.8を下回る結果となりました。 一方で、コンソールの Importance には明らかな変化が現れました。
特徴量寄与度 (Importance) ... 13. Cabin_Deck_F : 55 14. Cabin_Deck_U : 54 15. GroupCategory_Small : 24 (New!)
前回は圏外だったグループ関連の指標が、上位15項目に食い込んできました。 モデルが「2〜4人組であること」を判断の一助にしたことは確かです。 しかし、スコアが下がった理由は、カテゴリ化したことで「5人組」や「8人組」といった細かな人数の違いによる情報の解像度が失われ、予測がマイルドになりすぎたことにあると考えられます。
4. 結論
特徴量を「意味のある塊」にまとめる手法は、重要度を上げるのには有効でしたが、今回のような複雑なデータセットでは、生の数値が持っていた細かなニュアンスも重要だったようです。 次は、この「人数の意味」を消さずに、さらに情報の密度を高めるアプローチ(グループ内の他者の状態など)への転換が必要です。
一歩下がって、データの解像度を見直す。スコアの変動は、モデルからのフィードバックに他ならない。
PR