カテゴリ変数のエンコーディング

# 前略
[カテゴリカル変数のEncoding手法のまとめ](http://jotkn.ciao.jp/wp/2017/08/22/post-67/)などでカテゴリカル変数のエンコーディング手法の紹介がされていたので、pythonでの実現をやってみました。N番煎じですが。。。


ほとんど`sklearn.preprocessing`で実現できるので、一度公式ドキュメントをじっくり読むと良いかもしれませんね。他にも前処理に便利なものがありそうでした。

## データ準備
タイタニックデータを使用します。

```python


from sklearn.preprocessing import LabelBinarizer


from sklearn.preprocessing import LabelBinarizer
from sklearn import preprocessing
import pandas as pd
import numpy as np

# データセットを読み込み
df = pd.read_csv('train.csv')
```

今回はカテゴリ変数`Embarked`を使用します。簡単のためnullの行は削除します。

```python
# nullチェック -> 2件存在
df['Embarked'].isnull().sum()
```

```python
# 今回はnull行を落とす
df = df.dropna(subset=['Embarked'])
```


## ラベルエンコーディング

```python
# ラベルエンコーディング（LabelEncoder）
from sklearn.preprocessing import LabelEncoder


le = LabelEncoder()
encoded = le.fit_transform(df['Embarked'].values)
decoded = le.inverse_transform(encoded)
df['encoded'] = encoded

print('存在するクラス: ', le.classes_)
print('変換先: C, Q, S ->', le.transform(['C', 'Q', 'S']))
print('エンコード結果: ', encoded)
print('元に戻す: ', decoded)
```
複数変数を一気に変換したいときはこちら。

```python
# ラベルエンコーディング（OrdinalEncoder）
from sklearn.preprocessing import OrdinalEncoder


oe = preprocessing.OrdinalEncoder()
encoded = oe.fit_transform(df[['Embarked', 'Sex']].values)
decoded = oe.inverse_transform(encoded)

print('エンコード結果: ', encoded)
print('元に戻す: ', decoded)
```


## カウントエンコーディング

```python
# カウントエンコーディング
import collections


counter = collections.Counter(df['Embarked'].values)
count_dict = dict(counter.most_common())
encoded = df['Embarked'].map(lambda x: count_dict[x]).values
df['encoded'] = encoded

print('エンコード結果: ', encoded)
```

## ラベルカウントエンコーディング

```python
# ラベルカウントエンコーディング
import collections


counter = collections.Counter(df['Embarked'].values)
count_dict = dict(counter.most_common())
label_count_dict = {key:i for i, key in enumerate(count_dict.keys(), start=1)}
encoded = df['Embarked'].map(lambda x: label_count_dict[x]).values
df['encoded'] = encoded

print('エンコード結果: ', encoded)
```


## ターゲットエンコーディング
`sklearn.preprocessing`にも用意されていますが、pandasを使ったほうがデータフレームの整形が楽です。

```python
# ターゲットエンコーディング
target_dict = df[['Embarked','Survived']].groupby(['Embarked'])['Survived'].mean().to_dict()
encoded = df['Embarked'].map(lambda x: target_dict[x]).values
df['encoded'] = encoded

print('エンコード結果: ', encoded)
```
## One-hotエンコーディング

```python
# One-hotエンコーディング
# 多重共線性を取り除くためにdrop_firstをTrueとする
df_ = pd.get_dummies(df, drop_first=True, columns=['Embarked'])

df_.head()
```

こちらは複数変数を変換できますが、`drop`が使いにくいです。`drop`をTrueにしても`ohe.get_feature_names`で取得できる列ラベルはdropしないので、データフレームにする場合、ラベルを取得するのがひと手間あります。

```python
# One-hotエンコーディング（OneHotEncoder）
from sklearn.preprocessing import OneHotEncoder


ohe = OneHotEncoder(sparse=False)
# ohe = OneHotEncoder(sparse=False, drop='first')
encoded = ohe.fit_transform(df[['Embarked', 'Sex']].values)

print('カテゴリ: ', ohe.categories_)
print('カテゴリ名: ', ohe.get_feature_names(['Embarked', 'Sex']))

# 列名を取得
label = ohe.get_feature_names(['Embarked', 'Sex'])

# データフレーム化
df_ = pd.DataFrame(encoded, columns=label, dtype=np.int8)

# データフレームを結合
pd.concat([df, df_], axis=1)
```
バイナリの値専用の変換器。

```python
# 2クラスのデータを2値に変換（OneHotEncoder）
lb = LabelBinarizer()
encoded = lb.fit_transform(df[['Sex']].values)

print('エンコード結果: ', encoded)
```

## ハッシュエンコーディング

```python
# ハッシュエンコーディング
# http://contrib.scikit-learn.org/categorical-encoding/index.html#
import category_encoders as ce

encoder = ce.HashingEncoder(cols=['Embarked', 'Sex'], n_components=4)
encoder.fit(df['Embarked'], df['Survived'])
encoded = encoder.transform(df['Embarked'])
encoded
```


## 草々