【sklearn】SimpleImputerの使い方が分かる【Kaggle Course – Intermediate Machine Learning 1-2】欠損値の処理

Kaggle

アメリカのデータ分析コンペKaggleでは、世界中の優秀なデータサイエンティストによって作成された様々なeラーニング教材が無料で公開されています。

その中でも、この記事では以下の講座について解説します。

ただ、全編英語ですし、ところどころ説明を省略しているところもあるので、私なりにかみ砕いて解説していきます。

この記事で取り扱うクラスは以下の通りです。

sklearn.impute.SimpleImputer

 

Introduction(イントロダクション)

Welcome to Kaggle Learn’s Intermediate Machine Learning micro-course!

本コースでは、以下のことを学んでいきます。

  • 欠損値、カテゴリ変数の扱い方
  • 機械学習パイプラインの設計
  • 交差検証の使用方法
  • 最先端のモデル構築(XGBoost)
  • データ漏洩について

また、ここで学んだことは下記のコンペで実践していきます。

Housing Prices Competition for Kaggle Learn Users(Kaggle学習者のための住宅価格コンペ)

Prerequisites(受講の前提条件)

以前に機械学習モデルを構築したことがあり、モデルの検証、アンダーフィッティングとオーバーフィッティング、ランダムフォレストなどのトピックに精通していれば、このコースに参加できます。

機械学習が初めての方は、先にIntro to Machine Learningを受講してみてください。

Your Turn(練習問題)

まず準備運動として、KaggleのコンペでSubmitする方法を練習問題で学びます。

Missing Values(欠損値の処理)

Introduction(イントロダクション)

ほとんどの機械学習ライブラリ(scikit-learnなど)は、欠損値のあるデータを使ってモデルを構築しようとするとエラーになってしまいます。

そのため、以下の3つのアプローチのいずれかを選ぶ必要があります。

Three Approaches(3つのアプローチ)

1.欠損値の削除

シンプルに、値が欠けている列を削除します。

View post on imgur.com

ただ、わずかな欠損値のために、その列のすべての情報が失われるというデメリットがあります。

2.欠損値の補完

欠損値を何らかの数値(平均値や中央値など)で埋めます。

View post on imgur.com

補完した値は正しいとは限りませんが、列を削除してしまうよりはよい結果になることが多いです。

3.欠損値の補完とデータ拡張

値が欠損していること自体に意味がある場合(MARやMNARの場合)、欠損値を埋めるだけよりも、欠損していたかどうかの情報をテーブルに付加すると役に立つことがあります。

例えば、収入が低ければ低いほど自分の収入を報告しない(収入の欠損値が収入と関連)といったケースです。

View post on imgur.com

これによって結果が大きく改善される場合もあれば、全く役に立たない場合もあるので注意が必要です。

Example(例)

例として、メルボルンの住宅価格のデータセットを使用します。ここでは、部屋数や土地の広さなどの情報を使って住宅価格を予測します。

3つのアプローチを比較するための関数を定義

さきほどの3つのアプローチを比較するために、score_datasetという関数を定義します。この関数で、ランダムフォレストによる平均絶対誤差(MAE)を計算します。

from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_absolute_error

# Function for comparing different approaches
def score_dataset(X_train, X_valid, y_train, y_valid):
    model = RandomForestRegressor(n_estimators=10, random_state=0)
    model.fit(X_train, y_train)
    preds = model.predict(X_valid)
    return mean_absolute_error(y_valid, preds)

アプローチ1 欠損値の削除

欠損値を単純に削除するケースです。

欠損値のある列名を取得し、X_trainX_validからその列を削除dropしたあと、先ほど作成したscore_dataset関数を使ってMAEスコアを取得します。

# Get names of columns with missing values
cols_with_missing = [col for col in X_train.columns
                     if X_train[col].isnull().any()]

# Drop columns in training and validation data
reduced_X_train = X_train.drop(cols_with_missing, axis=1)
reduced_X_valid = X_valid.drop(cols_with_missing, axis=1)

print("MAE from Approach 1 (Drop columns with missing values):")
print(score_dataset(reduced_X_train, reduced_X_valid, y_train, y_valid))
MAE from Approach 1 (Drop columns with missing values):
183550.22137772635

アプローチ2 欠損値の補完

次に、SimpleImputerを使って、欠損値を各列の平均値で置き換えるケースです。

  • まず、欠損値の保管方法を定義SimpleImputer
  • X_trainで統計情報(平均値)を取得fitしたあと、欠損値を平均値で補完transformします
  • X_validにも、先ほど取得した平均値で補完transformします
  • 列名がなくなっているので、元に戻し
  • 先ほど作成したscore_dataset関数を使ってMAEスコアを取得します
from sklearn.impute import SimpleImputer

# Imputation
my_imputer = SimpleImputer()
imputed_X_train = pd.DataFrame(my_imputer.fit_transform(X_train))
imputed_X_valid = pd.DataFrame(my_imputer.transform(X_valid))

# Imputation removed column names; put them back
imputed_X_train.columns = X_train.columns
imputed_X_valid.columns = X_valid.columns

print("MAE from Approach 2 (Imputation):")
print(score_dataset(imputed_X_train, imputed_X_valid, y_train, y_valid))
MAE from Approach 2 (Imputation):
178166.46269899711

欠損値を削除しただけの時より、MAEスコアが改善しましたね。

アプローチ3 欠損値の補完とデータ拡張

最後に、どの値が補完されたかを記録しながら、欠損値を補完します。

  • まず、データセットをコピーcopy
  • 欠損値を含む列ごとに新しい列_was_missingを追加し、欠損値かどうかを入力isnull()
  • 欠損値の保管方法を定義SimpleImputer
  • X_trainで統計情報(平均値)を取得fitしたあと、欠損値を平均値で補完transformします
  • X_validにも、先ほど取得した平均値で補完transformします
  • 列名がなくなっているので、元に戻し
  • 先ほど作成したscore_dataset関数を使ってMAEスコアを取得します
# Make copy to avoid changing original data (when imputing)
X_train_plus = X_train.copy()
X_valid_plus = X_valid.copy()

# Make new columns indicating what will be imputed
for col in cols_with_missing:
    X_train_plus[col + '_was_missing'] = X_train_plus[col].isnull()
    X_valid_plus[col + '_was_missing'] = X_valid_plus[col].isnull()

# Imputation
my_imputer = SimpleImputer()
imputed_X_train_plus = pd.DataFrame(my_imputer.fit_transform(X_train_plus))
imputed_X_valid_plus = pd.DataFrame(my_imputer.transform(X_valid_plus))

# Imputation removed column names; put them back
imputed_X_train_plus.columns = X_train_plus.columns
imputed_X_valid_plus.columns = X_valid_plus.columns

print("MAE from Approach 3 (An Extension to Imputation):")
print(score_dataset(imputed_X_train_plus, imputed_X_valid_plus, y_train, y_valid))
MAE from Approach 3 (An Extension to Imputation):
178927.503183954

アプローチ2よりスコアが低下してしまいましたね。

なぜ欠損値を補完したほうがスコアがいいのでしょうか?

今回扱ったのは10,864行、12列のデータで、そのうち3列は欠損値を含んでいます。

その3列の欠損率は半分以下となっているので、列をまるごと削除してしまうと、むしろ多くの有用な情報が削除されてしまうため、データを平均値で補完したほうが良い結果になるのは自然なことでした。

# Shape of training data (num_rows, num_columns)
print(X_train.shape)

# Number of missing values in each column of training data
missing_val_count_by_column = (X_train.isnull().sum())
print(missing_val_count_by_column[missing_val_count_by_column > 0])
(10864, 12)
Car               49
BuildingArea    5156
YearBuilt       4307
dtype: int64

Conclusion(結論)

一般的に言われているように、欠損値の補完は、欠損値の削除よりもより良い結果になります

逆にデータのほとんどが欠損している場合は、単純に削除したほうがうまくいくかもしれませんね。

Your Turn(練習問題)

実際に自分で欠損値を処理してみる練習問題です。

今回はKaggleで欠損値の処理について学びました。SimpleImputerを使うことで、手動で欠損値処理するよりも効率よく時間を使えそうですね。次回はカテゴリ変数の扱い方について学んでいきます。

以上、最後までお読みいただきありがとうございました。

コメント

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