【sklearn】LabelEncoderやOneHotEncoderの使い方が分かる【Kaggle Course – Intermediate Machine Learning 3】カテゴリ変数の前処理

Kaggle

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

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

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

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

sklearn.preprocessing.LabelEncoder
sklearn.preprocessing.OneHotEncoder

 

Categorical Variables(カテゴリ変数の前処理)

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

カテゴリー変数は、数値と違って数が限られています。例えば、車の車種をたずねたときは「トヨタ」「ホンダ」「日産」など、いくつかのカテゴリに分けられます。

ところが、これらの変数を文字列のまま、前処理せずそのままPythonの機械学習モデルに入れようとすると、大抵はエラーが発生してしまいます。そのため、このCourseではカテゴリ変数を処理する3つのアプローチを取りあげます。

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

Drop Categorical Variables(カテゴリ変数の削除)

一番単純な方法は、データセットからカテゴリ変数を削除してしまうことです。この方法は、その列に有用な情報が含まれていない場合にのみ有効です。

Label Encoding(ラベルエンコーディング)

ラベルエンコーディングでは、下記のようにカテゴリ変数に整数を割り振ります。

View post on imgur.com

Never 0
Rarely 1
Most days 2
Every day 3

この例ではカテゴリの順番が明白ですので、順序変数と呼ばれます。

決定木やランダムフォレストのモデルを使うときは、順序変数をラベルエンコーディングすると、いい精度が出ることが多いようです。

One-Hot Encoding(ワンホットエンコーディング)

ワンホットエンコーディングは、カテゴリ変数を0と1の値を持つ新しい特徴量で置き換えます

View post on imgur.com

元のデータセットでは、「Color」は「Red」「Yellow」「Green」の3つのカテゴリ変数でした。

ワンホットエンコーディングでは、元の値が「Red」の場合は「Red」の列に1を入れ、元の値が「Yellow」の場合は「Yellow」の列に1を入れる、という処理をします。

なぜ、わざわざこんなまどろっこしいことをするかというと、この「Color」というカテゴリはさきほどの「Breakfast」のカテゴリと違って、カテゴリに明白な順序付けがないからです。

このように、明確な順序付けがないカテゴリ変数を名目変数と呼び、ワンホットエンコーディングで処理します。

ただし、カテゴリがたくさんある場合(例えば15個以上)には、列が増えすぎてしまうため、この方法は使いません

Example(具体例)

さて、具体的なコードを見ていきましょう。メルボルンの住宅価格のデータセットを使用します。

まずは、前処理の対象となるカテゴリ変数の列名を取得します。

datatypeobjectとなっていると、そのカラムには一般的にテキストが入っていることになります。今回はテキストの列はカテゴリ変数であると判断し、object_colsに格納しています。

# Get list of categorical variables
s = (X_train.dtypes == 'object')
object_cols = list(s[s].index)

print("Categorical variables:")
print(object_cols)
Categorical variables:
['Type', 'Method', 'Regionname']

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=100, random_state=0)
    model.fit(X_train, y_train)
    preds = model.predict(X_valid)
    return mean_absolute_error(y_valid, preds)

アプローチ1 カテゴリ変数の削除

カテゴリ変数を単純に削除します。select_dtypesobject列を取り除いたあと、score_dataset()関数を使ってMAEスコアを取得します

drop_X_train = X_train.select_dtypes(exclude=['object'])
drop_X_valid = X_valid.select_dtypes(exclude=['object'])

print("MAE from Approach 1 (Drop categorical variables):")
print(score_dataset(drop_X_train, drop_X_valid, y_train, y_valid))
MAE from Approach 1 (Drop categorical variables):
175703.48185157913

アプローチ2 ラベルエンコーディング(LabelEncoder)

次に、ラベルエンコーディングでカテゴリ変数を数値に置き換える方法です。コードの全体像はこちらです。

from sklearn.preprocessing import LabelEncoder

# Make copy to avoid changing original data 
label_X_train = X_train.copy()
label_X_valid = X_valid.copy()

# Apply label encoder to each column with categorical data
label_encoder = LabelEncoder()
for col in object_cols:
    label_X_train[col] = label_encoder.fit_transform(X_train[col])
    label_X_valid[col] = label_encoder.transform(X_valid[col])

print("MAE from Approach 2 (Label Encoding):") 
print(score_dataset(label_X_train, label_X_valid, y_train, y_valid))
MAE from Approach 2 (Label Encoding):
165936.40548390493

まず、sklearnライブラリからLabelEncoderクラスを取得します。

次に、元のデータを残すため、訓練データと評価データをcopyします。

# Apply label encoder to each column with categorical data
label_encoder = LabelEncoder()
for col in object_cols:
    label_X_train[col] = label_encoder.fit_transform(X_train[col])
    label_X_valid[col] = label_encoder.transform(X_valid[col])

続いて、LabelEncoderが登場します。

学習データlabel_X_trainのカテゴリ変数に対して、整数の割り当てfitと整数への変換transformfit_transform同時に行います。

評価データlabel_X_validのカテゴリ変数に対しては整数への変換transformだけを行います。

以上を、すべてのカテゴリ変数object_colsに対して繰り返し行います。

print("MAE from Approach 2 (Label Encoding):") 
print(score_dataset(label_X_train, label_X_valid, y_train, y_valid))

最後にscore_dataset()関数を使ってMAEスコアを取得します。

この例では、カテゴリ変数に対してランダムに整数の割り当てを行いましたが、順序変数(例えばTシャツのサイズなど)においてはLabelEncoderを使用せず、個別に整数の割り当てを行う方がよいでしょう。

アプローチ3 ワンホットエンコーディング(OneHotEncoder)

最後に、ワンホットエンコーディングの処理方法です。コードの全体像はこちらです。

from sklearn.preprocessing import OneHotEncoder

# Apply one-hot encoder to each column with categorical data
OH_encoder = OneHotEncoder(handle_unknown='ignore', sparse=False)
OH_cols_train = pd.DataFrame(OH_encoder.fit_transform(X_train[object_cols]))
OH_cols_valid = pd.DataFrame(OH_encoder.transform(X_valid[object_cols]))

# One-hot encoding removed index; put it back
OH_cols_train.index = X_train.index
OH_cols_valid.index = X_valid.index

# Remove categorical columns (will replace with one-hot encoding)
num_X_train = X_train.drop(object_cols, axis=1)
num_X_valid = X_valid.drop(object_cols, axis=1)

# Add one-hot encoded columns to numerical features
OH_X_train = pd.concat([num_X_train, OH_cols_train], axis=1)
OH_X_valid = pd.concat([num_X_valid, OH_cols_valid], axis=1)

print("MAE from Approach 3 (One-Hot Encoding):") 
print(score_dataset(OH_X_train, OH_X_valid, y_train, y_valid))
MAE from Approach 3 (One-Hot Encoding):
166089.4893009678

まず、sklearnライブラリからLabelEncoderクラスを取得します。

# Apply one-hot encoder to each column with categorical data
OH_encoder = OneHotEncoder(handle_unknown='ignore', sparse=False)
OH_cols_train = pd.DataFrame(OH_encoder.fit_transform(X_train[object_cols]))
OH_cols_valid = pd.DataFrame(OH_encoder.transform(X_valid[object_cols]))

次に、OneHotEncoderのパラメータを指定します。

handle_unknown=’ignore’で、検証データの中に訓練データにないクラスがある場合のエラーを回避しています。また、sparse=Falseを設定することで、エンコードされた列がnumpy配列として返されるようにしています。(こちらのサイトが詳しいです)

その後、インデックス列の生成fitを行ったあと、インデックスデータへ変換transformします。

# One-hot encoding removed index; put it back
OH_cols_train.index = X_train.index
OH_cols_valid.index = X_valid.index

# Remove categorical columns (will replace with one-hot encoding)
num_X_train = X_train.drop(object_cols, axis=1)
num_X_valid = X_valid.drop(object_cols, axis=1)

# Add one-hot encoded columns to numerical features
OH_X_train = pd.concat([num_X_train, OH_cols_train], axis=1)
OH_X_valid = pd.concat([num_X_valid, OH_cols_valid], axis=1)

インデックスが削除されているので元に戻して、元のカテゴリ変数を削除したDataFrameを用意し、変換したインデックスデータと結合します。

最後にscore_dataset()関数を使ってMAEスコアを取得して、終了です。

Which approach is best?(どの手法が優れいているか?)

この例では、カテゴリ変数の列を削除した場合がもっとも悪いスコアとなり、残り2つのアプローチにはそれほど差が出ませんでした。

一般的には、アプローチ3>2>1という順番でスコアがよくなるようです。

Conclusion(結論)

カテゴリデータを扱う機会は多いので、今回紹介したアプローチを知っていれば役に立つと思います。

Your Turn(練習問題)

今回紹介した3つのアプローチを練習問題で試してみましょう。

今回はKaggleでカテゴリ変数の処理について学びました。LabelEncoderOneHotEncoderを使いこなせるようになれば、データ量が多くても安心ですね。次回は機械学習のパイプラインについて学んでいきます。

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

コメント

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