Kaggleにおける一連の流れにおいて、panderaの活用例を紹介します。
これにより、以下のメリットが得られます。
- データのバリデーションが実現
- 各データの内容(および関連する処理が期待するデータの内容)の読み取りが可能
「Titanic - Machine Learning from Disaster」を題材とします。
実行環境はKaggleのNotebookを使用しています。
はじめに、panderaをインストールします。
!pip install pandera
そして、必要なモジュールをインポートします。
import pandas as pd
import pandera as pa
from pandera.typing import Series
まずは、以下のデータを読み込みます。
- 訓練データ(train.csv)
- テストデータ(test.csv)
- 提出用サンプルデータ(gender_submission.csv)
そこで、訓練データのスキーマを定義します。これにより、訓練データのスキーマの内容の補足、訓練データのスキーマのバリデーションが実現されます。スキーマは実際にデータを確認した上で定義しています。その過程については省略します。
# 訓練データのスキーマ
class TrainSchema(pa.SchemaModel):
PassengerId: Series[int] = pa.Field(ge=1)
Survived: Series[int] = pa.Field(isin=(0, 1))
Pclass: Series[int] = pa.Field(isin=(1, 2, 3))
Name: Series[str]
Sex: Series[str] = pa.Field(isin=("male", "female"))
Age: Series[float] = pa.Field(nullable=True, ge=0)
SibSp: Series[int]
Parch: Series[int]
Ticket: Series[str]
Fare: Series[float] = pa.Field(nullable=True)
Cabin: Series[str] = pa.Field(nullable=True)
Embarked: Series[str] = pa.Field(nullable=True)
# 訓練データのの読み込み(バリデーションも実施)
train = TrainSchema.validate(pd.read_csv("../input/titanic/train.csv"))
テストデータにおいても同様にスキーマを定義します。
# テストデータのスキーマ
class TestSchema(pa.SchemaModel):
PassengerId: Series[int] = pa.Field(ge=1)
Pclass: Series[int] = pa.Field(isin=(1, 2, 3))
Name: Series[str]
Sex: Series[str] = pa.Field(isin=("male", "female"))
Age: Series[float] = pa.Field(nullable=True, ge=0)
SibSp: Series[int]
Parch: Series[int]
Ticket: Series[str]
Fare: Series[float] = pa.Field(nullable=True)
Cabin: Series[str] = pa.Field(nullable=True)
Embarked: Series[str] = pa.Field(nullable=True)
# テストデータの読み込み(バリデーションも実施)
test = TestSchema.validate(pd.read_csv("../input/titanic/test.csv"))
提出用データのスキーマにおいても同様にスキーマを定義します。
# 提出用データのスキーマ
class SubmissionSchema(pa.SchemaModel):
PassengerId: Series[int] = pa.Field(ge=1)
Survived: Series[int] = pa.Field(isin=(0, 1))
# 提出用サンプルデータの読み込み(バリデーションも実施)
gender_submission = SubmissionSchema.validate(
pd.read_csv("../input/titanic/gender_submission.csv"))
続いて、学習や予測のために、データの前処理を実施します。そこで、前処理済みのデータのスキーマを定義します。前処理を関数として実装し、返り値がスキーマに従うようにしています。前処理の内容については省略します。
# データセットを目的変数と説明変数に分割
y_train = train["Survived"]
x_train = train.drop("Survived", axis=1)
x_test = test
# 前処理で必要な値の算出
fare_mean = x_train["Fare"].mean()
age_mean = x_train["Age"].mean()
embarked_mode = x_train["Embarked"].mode()[0]
# 前処理済みのデータのスキーマ
class PreprocessedSchema(pa.SchemaModel):
Pclass: Series[int] = pa.Field(isin=(1, 2, 3))
Sex: Series[int] = pa.Field(isin=(0, 1))
Age: Series[int] = pa.Field(ge=0)
SibSp: Series[int]
Parch: Series[int]
Fare: Series[float]
Embarked: Series[int]
# 簡単な前処理を実施する関数(バリデーションも実施)
def preprocess(df):
df["Sex"].replace({"male": 0, "female": 1}, inplace=True)
df["Age"].fillna((age_mean), inplace=True)
df["Age"] = df["Age"].astype(int)
df["Fare"].fillna((fare_mean), inplace=True)
df["Embarked"].fillna((embarked_mode), inplace=True)
df["Embarked"].replace({"S": 0, "C": 1, "Q": 2}, inplace=True)
df.drop(["PassengerId", "Name", "Ticket", "Cabin"], axis=1, inplace=True)
return PreprocessedSchema.validate(df)
# 簡単な前処理
preprocessed_x_train = preprocess(x_train)
preprocessed_x_test = preprocess(x_test)
最後に学習、予測を実施します。先ほどの前処理済みの訓練データを学習させます。そして、前処理済みのテストデータで予測させます。前処理済みのデータのスキーマがあることにより訓練データ、テストデータの形式が同様であることの担保が可能です。予測の結果は提出用データのスキーマでバリデーションすることで提出が可能な形式の担保されます。
from sklearn.linear_model import LogisticRegression
# ロジスティック回帰による学習
clf = LogisticRegression()
clf.fit(preprocessed_x_train, y_train)
# ロジスティック回帰による予測
y_pred = clf.predict(preprocessed_x_test)
# 提出用データの準備(バリデーションも実施)
submission = gender_submission
submission["Survived"] = y_pred
submission = SubmissionSchema.validate(submission)
submission.to_csv("submission.csv", index=False)
このように、Kaggleにおける一連の流れにおいて、panderaを活用することで、「データのバリデーションが実現」、「各データの内容(および関連する処理が期待するデータの内容)の読み取りが可能」というメリットが得られます。全体的にメリットが得られますが、特に、Kaggleにおいては「提出してみたらエラーだった」というというトラブルを減らすことにつながる可能性があります。