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

ただ、全編英語ですし、ところどころ説明を省略しているところもあるので、私なりにかみ砕いて解説していきます。
この記事で取り扱うクラスは以下の通りです。
sklearn.preprocessing.LabelEncoder sklearn.preprocessing.OneHotEncoder
Categorical Variables(カテゴリ変数の前処理)
Introduction(イントロダクション)
カテゴリー変数は、数値と違って数が限られています。例えば、車の車種をたずねたときは「トヨタ」「ホンダ」「日産」など、いくつかのカテゴリに分けられます。
ところが、これらの変数を文字列のまま、前処理せずそのままPythonの機械学習モデルに入れようとすると、大抵はエラーが発生してしまいます。そのため、このCourseではカテゴリ変数を処理する3つのアプローチを取りあげます。
Three Approaches(3つのアプローチ)
Drop Categorical Variables(カテゴリ変数の削除)
一番単純な方法は、データセットからカテゴリ変数を削除してしまうことです。この方法は、その列に有用な情報が含まれていない場合にのみ有効です。
Label Encoding(ラベルエンコーディング)
ラベルエンコーディングでは、下記のようにカテゴリ変数に整数を割り振ります。
Never | 0 |
Rarely | 1 |
Most days | 2 |
Every day | 3 |
この例ではカテゴリの順番が明白ですので、順序変数と呼ばれます。

決定木やランダムフォレストのモデルを使うときは、順序変数をラベルエンコーディングすると、いい精度が出ることが多いようです。
One-Hot Encoding(ワンホットエンコーディング)
ワンホットエンコーディングは、カテゴリ変数を0と1の値を持つ新しい特徴量で置き換えます。
元のデータセットでは、「Color」は「Red」「Yellow」「Green」の3つのカテゴリ変数でした。
ワンホットエンコーディングでは、元の値が「Red」の場合は「Red」の列に1を入れ、元の値が「Yellow」の場合は「Yellow」の列に1を入れる、という処理をします。

なぜ、わざわざこんなまどろっこしいことをするかというと、この「Color」というカテゴリはさきほどの「Breakfast」のカテゴリと違って、カテゴリに明白な順序付けがないからです。
このように、明確な順序付けがないカテゴリ変数を名目変数と呼び、ワンホットエンコーディングで処理します。
ただし、カテゴリがたくさんある場合(例えば15個以上)には、列が増えすぎてしまうため、この方法は使いません。
Example(具体例)
さて、具体的なコードを見ていきましょう。メルボルンの住宅価格のデータセットを使用します。
まずは、前処理の対象となるカテゴリ変数の列名を取得します。
datatypeがobjectとなっていると、そのカラムには一般的にテキストが入っていることになります。今回はテキストの列はカテゴリ変数であると判断し、object_colsに格納しています。
# Get list of categorical variables
s = (X_train.dtypes == 'object')
object_cols = list(s[s].index)
print("Categorical variables:")
print(object_cols)
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_dtypesでobject列を取り除いたあと、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))
アプローチ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))
まず、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と整数への変換transformをfit_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))
まず、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でカテゴリ変数の処理について学びました。LabelEncoderやOneHotEncoderを使いこなせるようになれば、データ量が多くても安心ですね。次回は機械学習のパイプラインについて学んでいきます。
以上、最後までお読みいただきありがとうございました。
コメント