カテゴリカル変数のEncoding手法について
Structured Data(構造化データ)の下処理をおこなう際に避けて通れないのがFeature Engineering(特徴量エンジニアリング)。
特に悩ましいのがカテゴリ変数の扱いで、どのように扱えば良いか困ることが多く、また、使った手法もすぐに忘れてしまいがちなので、自分なりに整理して記事にまとめておきたいというのが趣旨。
1.よく使われる手法
まずはよく用いられる定番の手法から。次元を増やすかどうかで大まかに次の2つに分類できる。
・次元を増やさない場合(Label, Count, LabelCount, Target)
・次元を増やす場合(One-Hot, Entity Embedding)
これらの手法について、python上の実装例とともに見ていきたい。
用いたライブラリは主にPandas、少しScikit-Learn。
2.次元を増やさない場合
Label Encoding
最もシンプルな手法で、与えられたカテゴリ変数に数字を割り当てるもの。
たとえば、「東京」、「大阪」、「名古屋」というカテゴリに対して、
0、1、2といったようにラベルをふって単純に数値化するだけ。
以下、実行例。
Method 1はPandasを使ったもので、Method 2はScikit-Learnを使ったもの。
import pandas as pd import numpy as np d = {'city': ['tokyo', 'nagoya', 'osaka', 'tokyo', 'nagoya', 'osaka', 'tokyo', 'osaka', 'tokyo'], 'target': [0, 1, 0, 1, 0, 1, 0, 1, 0]} df = pd.DataFrame(d) df['city'] = df['city'].astype('category') df.dtypes # method 1 df['label_enc'] = df['city'].cat.codes # method 2 from sklearn.preprocessing import LabelEncoder le = LabelEncoder() df['label_enc'] = le.fit_transform(df['city'])
Count Encoding
これも比較的シンプルな手法で、データに含まれるカテゴリ変数の出現回数を数えるもの。
以下、Pandasのgroupbyを使った実行例を2つ(Method 1, 2)。
# method 1 count_mean = df.groupby('city').target.count() df['count_enc'] = df['city'].map(count_mean) # method 2 df['count_enc'] = df.groupby('city')['target'].transform('count')
LabelCount (Count Rank) Encoding
次は上述したCount Encodingの応用編で、カテゴリ変数の出現回数が多い順に順位づけするもの。
以下、Pandasを使った実行例。
count_rank = df.groupby('city')['target'].count().rank(ascending=False) df['count_rank'] = df['city'].map(count_rank)
Target Encoding
Mean EncodingやLikelihood Encodingとも呼ばれる手法で、目的変数(Target)に対してカテゴリ変数の処理をおこなう。
Kaggle等の機械学習コンペで頻繁に用いられている。
やり方としては、目的変数が2値のみをとるブーリアン型であればカテゴリ毎の確率を、目的変数が数値であればカテゴリ毎の平均を取るというもの。
ただし、データセットが学習データとテストデータに分かれている場合、テストデータの目的変数は未知であるため、演算は学習データに対しておこない、得られた特徴量をテストデータ内のカテゴリへ適用する。
この部分に注意しないと、Cross Validation をおこなったときにデータがリークすることになってしまう。
以下、Pandasのgroupbyを使った実行例を2つ(Method 1, 2)。
# method 1 target_mean = df.groupby('city').target.mean() df['target_enc'] = df['city'].map(target_mean) # method 2 df['target_enc'] = df.groupby('city')['target'].transform('mean')
3.次元を増やす場合
One hot encoding
データ中に存在するカテゴリ変数の数だけ次元(構造化データでいうと、新しい列)を用意して、各行に含まれているカテゴリ変数に対応する次元を1に、それ以外を0にする方法。
これにより、カテゴリ変数を単に連続する数としてではなく、独立する値として扱うことが可能となる。
以下、実行例。
Method 1はPandasを使ったもので、Method 2はScikit-Learnを使ったもの。
ただしMethod 2ではカラム名が自動で返ってこないため、何らかの形で指定してあげる必要がある。
# method 1 oh_enc = pd.get_dummies(df['city']) df = pd.concat([df, oh_enc], axis=1) # method 2 from sklearn.preprocessing import OneHotEncoder oh_enc = OneHotEncoder(sparse=False) df[['nagoya', 'osaka', 'tokyo']] = oh_enc.fit_transform(df.label_enc.reshape(len(df.label_enc), 1))
ここまでの実行結果は下表の通り。
city | target | label_end | count_enc | count_rank | target_enc | nagoya | osaka | tokyo |
---|---|---|---|---|---|---|---|---|
tokyo | 0 | 2 | 4 | 1.0 | 0.250000 | 0.0 | 0.0 | 1.0 |
nagoya | 1 | 0 | 2 | 3.0 | 0.500000 | 1.0 | 0.0 | 0.0 |
osaka | 0 | 1 | 3 | 2.0 | 0.666667 | 0.0 | 1.0 | 0.0 |
tokyo | 1 | 2 | 4 | 1.0 | 0.250000 | 0.0 | 0.0 | 1.0 |
nagoya | 0 | 0 | 2 | 3.0 | 0.500000 | 1.0 | 0.0 | 0.0 |
Entity Embedding
KaggleのRossmannコンペで有名になった手法。
これまで挙げてきたものとはややアプローチが異なるが、ニューラルネットワークでカテゴリ変数に対応する重み行列を学習させ、その重み行列とOHEの積をとったもの(と理解している)。
最終的には数値データと合わせてニューラルネットワークの全結合層に放り込むこととなる。
KaggleのPorto Seguroコンペデータを使った実験を行い、以下Githubに公開したので、参考までに。
次のステップとして、抽出した重み行列を特徴量としてGradient Boosting Decision Tree (GBDT) にうまくまぜられないかと思案中。
4.参考記事
参考にしたのは以下のblog記事。とてもうまくまとめられており、インスパイアされるきっかけとなった。
あとは、英語だが以下の記事もEncodingについて体系的に書かれており、非常に役に立った。
Entity Embeddingについてはこの記事を参照。
最後に、最近発売されたこの本。大全というだけあって網羅的に整理されており、辞書的な使い方ができるのがよい。
前処理大全[データ分析のためのSQL/R/Python実践テクニック]
- 作者: 本橋智光
- 出版社/メーカー: 技術評論社
- 発売日: 2018/04/13
- メディア: 大型本
- この商品を含むブログ (1件) を見る