今回のコンペはSpaceship Titanicという、以前実施したタイタニックコンペの宇宙版になります。
今回は量が多いため、いくつかのパートに分けて説明していきます。
概要
宇宙の謎を解くためにデータ サイエンスのスキルが必要とされる 2912 年へようこそ。4 光年離れたところから通信を受信しましたが、様子がよくありません。
宇宙船タイタニック号は、1 か月前に打ち上げられた星間旅客船でした。約 13,000 人の乗客を乗せたこの船は、太陽系から近くの星を周回する 3 つの新たに居住可能な太陽系外惑星に移民を輸送する処女航海に出発しました。
最初の目的地である灼熱の 55 Cancri E に向かう途中でアルファ ケンタウリを周回しているときに、不注意な宇宙船タイタニック号は、塵の雲の中に隠された時空異常と衝突しました。悲しいことに、それは 1000 年前の名前の由来と同様の運命をたどりました。船は無傷のままでしたが、乗客のほぼ半分が別の次元に運ばれました!
乗組員を救出し、失われた乗客を取り戻すために、宇宙船の損傷したコンピューター システムから回収された記録を使用して、異常によってどの乗客が輸送されたかを予測することが求められます。
それらを保存して歴史を変えるのを手伝ってください!
https://www.kaggle.com/competitions/spaceship-titanic
こちらのサイトを参考に進めていきました。
学んだこと
- データを区切って、新しいカラムに入れる方法
- 欠損値を最頻値と中央値で埋める方法
データの加工
データの参照
いつもと同様に、ライブラリをインポートしていきます。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
plt.style.use("ggplot")
import optuna
import seaborn as sns
df_train=pd.read_csv("../input/spaceship-titanic/train.csv")
df_test=pd.read_csv("../input/spaceship-titanic/test.csv")
訓練データの中身を一部参照すると、以下のようになります。

それぞれこの項目意味は以下の通りとなります。
- カラムの意味
- PassengerId:乗客ID
- HomePlanet:乗客が乗船した惑星
- CryoSleep:コールドスリープ状態にあるか
- Cabin:乗客が滞在しているキャビン号
- Destination:目的地
- Age:年齢
- VIP:VIPかどうか
- RoomService:ルームサービスで支払った金額
- Food Court:フードコートで支払った金額
- Shopping Mall:モールで支払った金額
- Spa:スパで支払った金額
- VRDeck:VRデッキで支払った金額
- Name:名前
- Transported:別次元に飛ばされたかどうか(目的変数)
一度訓練データとテストデータを結合して、インデックス番号を振り直します。
# 学習データの説明変数と、予測用データを結合
df_all = pd.concat([df_train,df_test])
# インデックス番号を振り直す。
# drop=Trueは、元のインデックスを削除します。
df_all=df_all.reset_index(drop=True)
区切り文字でデータを分別
それでは、データの整形を行なっていきます。
まずは、ある特定文字で区切られているデータもあるため、必要に応じて、データを分けています。
対象のカラムは「PassengerId」と「Cabin」です。
PassengerIdの中身は、下記のように"_"区切りで2つの数字がくっついているため、1度分けます。
処理内容としては、PassengerIdを一度String型に変換し、splitの"_"で区切ります。
複数の列に分割してpandas.DataFrame
として取得するには、引数expand=True
を指定します。デフォルトはexpand=False
です。
区切りから前の数字は”ID01”のカラムに入れて、後ろの数字は”ID02”に入れます。
さらに、別々のカラムに分けた数字はString型のため、astypeでint型に変換します。

df_all["ID01"]=df_all["PassengerId"].str.split("_",expand=True)[0]
df_all["ID02"]=df_all["PassengerId"].str.split("_",expand=True)[1]
df_all["ID01"]=df_all["ID01"].astype(int)
df_all["ID02"]=df_all["ID02"].astype(int)
Cabinは下記のように”/”で区切られているため、PassengerIdと同様に加工します。
”/”で区切ったときに、1番目と2番目、3番目のそれぞれのデータを新しく作成したカラムに入れていきます。

df_all["Cabin1"] = df_all["Cabin"].str.split("/",expand=True)[0]
df_all["Cabin2"] = df_all["Cabin"].str.split("/",expand=True)[1]
df_all["Cabin3"] = df_all["Cabin"].str.split("/",expand=True)[2]
欠損値の処理
データの分別ができたら、続いて欠損値の処理です。
欠損値については、こちらのサイトがわかりやすいと思います。
それぞれのカラムでの欠損値の数は以下の通りです。かなり多い印象を受けますね。

欠損値の処理方法はいくつかありますが、今回は単一代入法を用います。これは、欠損地に平均値や中央値、最頻値などを代入することです。
ただあまり有効ではないそうです。
https://dataanablog.com/types-of-missing-values-and-how-to-deal-with-them
まずは、最頻値で欠損処理をするため、最頻値を確認していきます。
下記のようにグラフで見ると、最頻値がEarth
であることがわかります。
他のカラムも”HomePlanet”
の箇所を変更して、確認していきますが、量が多いため割愛します。
plt.figure(figsize=(16,9))
sns.countplot(df_all["HomePlanet"],hue=df_all["Transported"])

一通り確認できたら、欠損地を最頻値と中央値でそれぞれ埋めていきます。
#最頻値で埋める
df_all["HomePlanet"].fillna("Earth",inplace=True)
df_all["CryoSleep"].fillna("False",inplace=True)
df_all["CryoSleep"]=df_all["CryoSleep"].astype(bool) #objectのためboolに変換
df_all["Cabin1"].fillna("F",inplace=True)
df_all.loc[df_all["Cabin2"]==2000,"Cabin2"]=1
df_all["Cabin3"].fillna("S",inplace=True)
df_all["Destination"].fillna("TRAPPIST-1e",inplace=True)
df_all["VIP"].fillna("False",inplace=True)
df_all["VIP"]=df_all["VIP"].astype(bool) #objectのためboolに変換
#中央値で埋める
df_all["RoomService"].fillna(df_all["RoomService"].median(),inplace=True)
df_all["FoodCourt"].fillna(df_all["FoodCourt"].median(),inplace=True)
df_all["ShoppingMall"].fillna(df_all["ShoppingMall"].median(),inplace=True)
df_all["Spa"].fillna(df_all["Spa"].median(),inplace=True)
df_all["VRDeck"].fillna(df_all["VRDeck"].median(),inplace=True)
最後にそれぞれの金額を足した合計の特徴量を作成します。
df_all["pay"]=df_all["RoomService"]+df_all["FoodCourt"]+df_all["ShoppingMall"]+df_all["Spa"]+df_all["VRDeck"]
これで第1パートは完了です。
区切りデータの分別と欠損値処理をしていきました。
まだ下準備の段階ですが、精度の高いモデルを作成するためには必要な作業です。
第2パートもできたら投稿します。