🦉

エンコーディング手法の総まとめ

2024/03/10に公開

機械学習におけるエンコーディングとは、カテゴリデータを数値データに変更する操作を表します。
今回はエンコーディングの手法をまとめます。

エンコーディング手法

一般的な利用頻度順に並べています

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)

各カテゴリが前のカテゴリ(直前のカテゴリのみ)との差異に基づいてエンコードされます。

メリット/デメリット

メリット/デメリット
  1. ワンホットエンコーディング (One-Hot Encoding)
    ・メリット
    直感的で理解しやすい。
    数値的な距離が発生しないため、モデルに誤解を与えることがない。
    ・デメリット
    変数のカテゴリが多い場合、データセットの次元が大きくなり、メモリ消費が増加する(次元の呪い)。
  2. ラベルエンコーディング (Label Encoding)
    ・メリット
    実装が簡単で、ワンホットエンコーディングよりもデータセットの次元を増加させない。
    ・デメリット
    順序情報がないカテゴリデータに数値を割り当てると、モデルが順序情報を誤って解釈する可能性がある。
  3. ダミーエンコーディング (Dummy Encoding)
    ・メリット
    ワンホットエンコーディングと比較して1つ少ない特徴量を生成し、多重共線性の問題を避けることができる。
    ・デメリット
    依然として多数のカテゴリが存在する場合には、次元の増加という問題が発生する。
  4. ordinalエンコーディング (Ordinal Encoding)
    ・メリット
    順序情報があるカテゴリに対して自然で直感的なエンコーディング手法。
    ・デメリット
    順序情報がないカテゴリには適しておらず、不適切に使用すると誤解を招く可能性がある。
  5. バイナリエンコーディング (Binary Encoding)
    ・メリット
    ワンホットエンコーディングよりもはるかに少ないメモリを使用し、カテゴリ数が多い場合でも効率的。
    ・デメリット
    バイナリ化されたデータは人間が理解しにくく、データの解釈が難しい。
  6. ターゲットエンコーディング (Target Encoding)
    ・メリット
    ターゲット変数との関係を直接エンコーディングに反映させるため、予測モデルに有益な情報を提供できる。
    ・デメリット
    過学習を引き起こす可能性がある。特にカテゴリ内のデータポイントが少ない場合。
  7. frequencyエンコーディング (Frequency Encoding)
    ・メリット
    カテゴリの出現頻度に基づくため、情報の損失が少なく、モデルが重要なカテゴリを識別しやすくなる。
    ・デメリット
    異なるカテゴリが同じ頻度で発生する場合、区別がつかなくなる。
  8. Effectエンコーディング (Effect Encoding)
    ・メリット
    モデルにカテゴリの平均的な効果を反映させることができ、特に回帰分析で有用。
    ・デメリット
    ダミーエンコーディングやワンホットエンコーディングと同様に、多数のカテゴリを持つ特徴に対しては次元が大きくなる。
  9. ベースNエンコーディング (BaseN Encoding)
    ・メリット
    バイナリエンコーディングよりも柔軟性があり、次元数を制御することができる。
    ・デメリット
    基数の選択がモデルのパフォーマンスに影響を与えるため、適切な基数の選定が必要。
  10. ハッシングエンコーディング (Hashing Encoding)
    ・メリット
    大量のカテゴリを効率的に処理でき、固定長のベクトル表現を生成する。
    ・デメリット
    ハッシュ衝突の可能性があり、情報の損失が生じる可能性がある。
  11. Leave-One-Out Encoding (LOOエンコーディング)
    ・メリット
    ターゲットデータのリークを防ぎながら、ターゲット変数との関連をエンコーディングに反映できる。
    ・デメリット
    計算コストが高い場合があり、特にデータセットが大きい場合。
  12. Ranked Frequencyエンコーディング (Ranked Frequency Encoding)
    ・メリット
    頻度に基づくランキングにより、カテゴリ間の相対的な重要性を捉えることができる。
    ・デメリット
    頻度が同じカテゴリは区別がつかなくなる。
  13. ヘルメルトエンコーディング (Helmert Encoding)
    ・メリット
    カテゴリ間の比較を容易にし、統計モデルでの解釈を助ける。
    ・デメリット
    複数のカテゴリを持つ変数に対しては複雑で、理解しにくい場合がある。
  14. バックワードディファレンスエンコーディング (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

補足

補足

・ターゲットエンコーディングのスムージング平均値算出式
スムージングされた平均= \dfrac{n+m}{n×カテゴリ平均+m×全体平均}
n: カテゴリ内のサンプル数
m: スムージングパラメータ

・欠損値(NaN)の取り扱い
欠損値は補完、削除のほかに、欠損値自体が情報を持つ(欠損しているデータがあることを特徴量とする)として扱うことができます。
手法としては、NaN値の存在有無を示す特徴量の列を作成する、NaNを一つのカテゴリとしてone-hot-engodingを行うなどがあります。

以上になります。最後まで読んでいただきありがとうございました。

Discussion