エンコーディング手法の総まとめ
機械学習におけるエンコーディングとは、カテゴリデータを数値データに変更する操作を表します。
今回はエンコーディングの手法をまとめます。
エンコーディング手法
一般的な利用頻度順に並べています
1. ワンホットエンコーディング (One-Hot Encoding)
各カテゴリに対して配列を作成し、該当するカテゴリには1、それ以外には0を割り当てます。
例えばA=[1,0,0],B=[0,1,0],C=[0,0,1]などです。
2. ラベルエンコーディング (Label Encoding)
各カテゴリにユニークな整数を割り当てます。
例えばA=0,B=1,C=2などです。
3. ダミーエンコーディング (Dummy Encoding)
ワンホットエンコーディングと似ていますが、カテゴリ変数の各レベルに対して一つ少ないダミー変数を生成します。全て0を一つのカテゴリとして扱います。
4. ordinalエンコーディング (Ordinal Encoding)
カテゴリを順序付けられた整数に変換します。カテゴリに自然な順序がある場合に使用します。例えば'low'=0,'middle'=1,'high'=2などで、結果的にラベルエンコーディングと似たエンコーディングになることもあります。
5. バイナリエンコーディング (Binary Encoding)
カテゴリを整数に変換(ラベルエンコード)した後、それをバイナリ数として表現します。
6. ターゲットエンコーディング (Target Encoding)
カテゴリを、そのカテゴリに属する目的変数の平均値に置き換えます。他のfoldの平均値を利用するなどしてリークを防ぎます。
推論時には目的変数が存在しないため、学習時に利用したエンコード値を利用します。
7. frequencyエンコーディング (Frequency Encoding)
カテゴリを、そのカテゴリがデータセットに出現する頻度に置き換えます。
※以降は利用シーンが限定的になります。
8. Effectエンコーディング (Effect Encoding)
各カテゴリを-1、0、または1の値でエンコードします。カテゴリの平均を基準とした比較が可能になります。
9. ベースNエンコーディング (BaseN Encoding)
バイナリエンコーディングの一般化で、2以外の基数(例えば3進数など)を使用してカテゴリをエンコードします。
10. ハッシングエンコーディング (Hashing Encoding)
カテゴリをハッシュ関数を用いて固定長のベクトルに変換します。特徴量の次元を削減できます。
11. Leave-One-Out Encoding (LOO Encoding)
各カテゴリを、そのカテゴリを除いた全データの目的変数の平均値で置き換えます。
12. Ranked Frequencyエンコーディング (Ranked Frequency Encoding)
カテゴリをその出現頻度に基づいてランク付けし、それに基づいて数値を割り当てます。
13. ヘルメルトエンコーディング (Helmert Encoding)
各カテゴリが前のすべてのカテゴリの平均との対比でエンコードされます。カテゴリに対応する数字(の差異)を利用してエンコードするという操作は、カテゴリ列以外のデータを利用するという点でターゲットエンコーディングに似ています。
14. バックワードディファレンスエンコーディング (Backward Difference Encoding)
各カテゴリが前のカテゴリ(直前のカテゴリのみ)との差異に基づいてエンコードされます。
メリット/デメリット
メリット/デメリット
- ワンホットエンコーディング (One-Hot Encoding)
・メリット
直感的で理解しやすい。
数値的な距離が発生しないため、モデルに誤解を与えることがない。
・デメリット
変数のカテゴリが多い場合、データセットの次元が大きくなり、メモリ消費が増加する(次元の呪い)。 - ラベルエンコーディング (Label Encoding)
・メリット
実装が簡単で、ワンホットエンコーディングよりもデータセットの次元を増加させない。
・デメリット
順序情報がないカテゴリデータに数値を割り当てると、モデルが順序情報を誤って解釈する可能性がある。 - ダミーエンコーディング (Dummy Encoding)
・メリット
ワンホットエンコーディングと比較して1つ少ない特徴量を生成し、多重共線性の問題を避けることができる。
・デメリット
依然として多数のカテゴリが存在する場合には、次元の増加という問題が発生する。 - ordinalエンコーディング (Ordinal Encoding)
・メリット
順序情報があるカテゴリに対して自然で直感的なエンコーディング手法。
・デメリット
順序情報がないカテゴリには適しておらず、不適切に使用すると誤解を招く可能性がある。 - バイナリエンコーディング (Binary Encoding)
・メリット
ワンホットエンコーディングよりもはるかに少ないメモリを使用し、カテゴリ数が多い場合でも効率的。
・デメリット
バイナリ化されたデータは人間が理解しにくく、データの解釈が難しい。 - ターゲットエンコーディング (Target Encoding)
・メリット
ターゲット変数との関係を直接エンコーディングに反映させるため、予測モデルに有益な情報を提供できる。
・デメリット
過学習を引き起こす可能性がある。特にカテゴリ内のデータポイントが少ない場合。 - frequencyエンコーディング (Frequency Encoding)
・メリット
カテゴリの出現頻度に基づくため、情報の損失が少なく、モデルが重要なカテゴリを識別しやすくなる。
・デメリット
異なるカテゴリが同じ頻度で発生する場合、区別がつかなくなる。 - Effectエンコーディング (Effect Encoding)
・メリット
モデルにカテゴリの平均的な効果を反映させることができ、特に回帰分析で有用。
・デメリット
ダミーエンコーディングやワンホットエンコーディングと同様に、多数のカテゴリを持つ特徴に対しては次元が大きくなる。 - ベースNエンコーディング (BaseN Encoding)
・メリット
バイナリエンコーディングよりも柔軟性があり、次元数を制御することができる。
・デメリット
基数の選択がモデルのパフォーマンスに影響を与えるため、適切な基数の選定が必要。 - ハッシングエンコーディング (Hashing Encoding)
・メリット
大量のカテゴリを効率的に処理でき、固定長のベクトル表現を生成する。
・デメリット
ハッシュ衝突の可能性があり、情報の損失が生じる可能性がある。 - Leave-One-Out Encoding (LOOエンコーディング)
・メリット
ターゲットデータのリークを防ぎながら、ターゲット変数との関連をエンコーディングに反映できる。
・デメリット
計算コストが高い場合があり、特にデータセットが大きい場合。 - Ranked Frequencyエンコーディング (Ranked Frequency Encoding)
・メリット
頻度に基づくランキングにより、カテゴリ間の相対的な重要性を捉えることができる。
・デメリット
頻度が同じカテゴリは区別がつかなくなる。 - ヘルメルトエンコーディング (Helmert Encoding)
・メリット
カテゴリ間の比較を容易にし、統計モデルでの解釈を助ける。
・デメリット
複数のカテゴリを持つ変数に対しては複雑で、理解しにくい場合がある。 - バックワードディファレンスエンコーディング (Backward Difference Encoding)
・メリット
カテゴリの相対的な変化を捉えることができる。
・デメリット
カテゴリ間の関係が直線的でない場合には適していない。
実装
コード
import pandas as pd
def main():
data = {
'category': ['Red', 'Blue', 'Green', 'Blue', 'Red'],
'value': [1, 2, 3, 2, 1]
}
train = pd.DataFrame(data)
# データ
# category value
# 0 Red 1
# 1 Blue 2
# 2 Green 3
# 3 Blue 2
# 4 Red 1
# 1. ワンホットエンコーディング (One-Hot Encoding)
one_hot_encoded = pd.get_dummies(train['category'], prefix='category')
print("\n" + "/"*10 + "One-Hot Encoding" + "/"*20)
print(one_hot_encoded.astype(int))
# 2. ラベルエンコーディング (Label Encoding)
from sklearn.preprocessing import LabelEncoder
encoder = LabelEncoder()
train['category_label'] = encoder.fit_transform(train['category'])
print("\n" + "/"*10 + "Label Encoding" + "/"*20)
print(train)
train = train.iloc[:,:-1]
# 3. ダミーエンコーディング (Dummy Encoding)
dummy_encoded = pd.get_dummies(train['category'], drop_first=True)
print("\n" + "/"*10 + "Dummy Encoding" + "/"*20)
print(dummy_encoded.astype(int))
# 4. ordinalエンコーディング (Ordinal Encoding)
ordinal_encoded = train['category'].astype('category').cat.codes
print("\n" + "/"*10 + "Ordinal Encoding" + "/"*20)
print(ordinal_encoded)
# 5. バイナリエンコーディング (Binary Encoding)
import category_encoders as ce
encoder = ce.BinaryEncoder(cols=['category'])
binary_encoded = encoder.fit_transform(train['category'])
print("\n" + "/"*10 + "Binary Encoding" + "/"*20)
print(binary_encoded)
# 6. ターゲットエンコーディング (Target Encoding)
# この例ではvalue列をtarget列として仮定します
# 過学習を防ぐためのsmoothingにより、完全な平均値にはなりません
import category_encoders as ce
encoder = ce.TargetEncoder()
train['category_target'] = encoder.fit_transform(train['category'], train['value'])
print("\n" + "/"*10 + "Target Encoding" + "/"*20)
print(train)
train = train.iloc[:,:-1]
# 7. frequencyエンコーディング (Frequency Encoding)
frequency = train['category'].value_counts() / len(train)
train['category_frequency'] = train['category'].map(frequency)
print("\n" + "/"*10 + "Frequency Encoding" + "/"*20)
print(train)
train = train.iloc[:,:-1]
# 8. Effectエンコーディング (Effect Encoding)
import category_encoders as ce
encoder = ce.BackwardDifferenceEncoder()
effect_encoded = encoder.fit_transform(train['category'])
print("\n" + "/"*10 + "Effect Encoding" + "/"*20)
print(effect_encoded)
# 9. ベースNエンコーディング (BaseN Encoding)
import category_encoders as ce
encoder = ce.BaseNEncoder(base=3) # 例として基数3を使用
basen_encoded = encoder.fit_transform(train['category'])
print("\n" + "/"*10 + "BaseN Encoding" + "/"*20)
print(basen_encoded)
# 10. ハッシングエンコーディング (Hashing Encoding)
import category_encoders as ce
encoder = ce.HashingEncoder(n_components=5) # 例として5次元ベクトルに変換
hashing_encoded = encoder.fit_transform(train[['category']])
print("\n" + "/"*10 + "Hashing Encoding" + "/"*20)
print(hashing_encoded)
# 11. Leave-One-Out Encoding (LOOエンコーディング)
# この例では目的変数が存在すると仮定します
import category_encoders as ce
encoder = ce.LeaveOneOutEncoder()
loo_encoded = encoder.fit_transform(train['category'], train['value'])
print("\n" + "/"*10 + "Leave-One-Out Encoding" + "/"*20)
print(loo_encoded)
# 12. Ranked Frequencyエンコーディング (Ranked Frequency Encoding)
frequency = train['category'].value_counts().rank()
train['category_ranked_frequency'] = train['category'].map(frequency)
print("\n" + "/"*10 + "Ranked Frequency Encoding" + "/"*20)
print(train)
train = train.iloc[:,:-1]
# 13. ヘルメルトエンコーディング (Helmert Encoding)
import category_encoders as ce
encoder = ce.HelmertEncoder()
helmert_encoded = encoder.fit_transform(train['category'])
print("\n" + "/"*10 + "Helmert Encoding" + "/"*20)
print(helmert_encoded)
# 14. バックワードディファレンスエンコーディング (Backward Difference Encoding)
import category_encoders as ce
encoder = ce.BackwardDifferenceEncoder()
backward_difference_encoded = encoder.fit_transform(train['category'])
print("\n" + "/"*10 + "Backward Difference Encoding" + "/"*20)
print(backward_difference_encoded)
if __name__ == '__main__':
main()
出力
# 元データ
# category value
# 0 Red 1
# 1 Blue 2
# 2 Green 3
# 3 Blue 2
# 4 Red 1
//////////One-Hot Encoding////////////////////
category_Blue category_Green category_Red
0 0 0 1
1 1 0 0
2 0 1 0
3 1 0 0
4 0 0 1
//////////Label Encoding////////////////////
category value category_label
0 Red 1 2
1 Blue 2 0
2 Green 3 1
3 Blue 2 0
4 Red 1 2
//////////Dummy Encoding////////////////////
Green Red
0 0 1
1 0 0
2 1 0
3 0 0
4 0 1
//////////Ordinal Encoding////////////////////
0 2
1 0
2 1
3 0
4 2
dtype: int8
//////////Binary Encoding////////////////////
category_0 category_1
0 0 1
1 1 0
2 1 1
3 1 0
4 0 1
//////////Target Encoding////////////////////
category value category_target
0 Red 1 1.686519
1 Blue 2 1.828370
2 Green 3 1.956130
3 Blue 2 1.828370
4 Red 1 1.686519
//////////Frequency Encoding////////////////////
category value category_frequency
0 Red 1 0.4
1 Blue 2 0.4
2 Green 3 0.2
3 Blue 2 0.4
4 Red 1 0.4
//////////Effect Encoding////////////////////
intercept category_0 category_1
0 1 -0.666667 -0.333333
1 1 0.333333 -0.333333
2 1 0.333333 0.666667
3 1 0.333333 -0.333333
4 1 -0.666667 -0.333333
//////////BaseN Encoding////////////////////
category_0 category_1
0 0 1
1 0 2
2 1 0
3 0 2
4 0 1
//////////Hashing Encoding////////////////////
col_0 col_1 col_2 col_3 col_4
0 0 0 1 0 0
1 0 0 0 1 0
2 0 0 0 0 1
3 0 0 0 1 0
4 0 0 1 0 0
//////////Leave-One-Out Encoding////////////////////
category
0 1.0
1 2.0
2 1.8
3 2.0
4 1.0
//////////Ranked Frequency Encoding////////////////////
category value category_ranked_frequency
0 Red 1 2.5
1 Blue 2 2.5
2 Green 3 1.0
3 Blue 2 2.5
4 Red 1 2.5
//////////Helmert Encoding////////////////////
intercept category_0 category_1
0 1 -1.0 -1.0
1 1 1.0 -1.0
2 1 0.0 2.0
3 1 1.0 -1.0
4 1 -1.0 -1.0
//////////Backward Difference Encoding////////////////////
intercept category_0 category_1
0 1 -0.666667 -0.333333
1 1 0.333333 -0.333333
2 1 0.333333 0.666667
3 1 0.333333 -0.333333
4 1 -0.666667 -0.333333
補足
補足
・ターゲットエンコーディングのスムージング平均値算出式
スムージングされた平均
・欠損値(NaN)の取り扱い
欠損値は補完、削除のほかに、欠損値自体が情報を持つ(欠損しているデータがあることを特徴量とする)として扱うことができます。
手法としては、NaN値の存在有無を示す特徴量の列を作成する、NaNを一つのカテゴリとしてone-hot-engodingを行うなどがあります。
以上になります。最後まで読んでいただきありがとうございました。
Discussion