機械学習モデルへのビニングを利用した特徴量の作成

Posted on 2019/12/25 in 機械学習 , Updated on: 2019/12/25

ビニング処理で特徴量を作成

機械学習モデルへ渡す特徴量として、連続値を任意の境界で区切ったビニング処理を行い、新たにカテゴリ特徴量を作成する方法。年齢などの連続値に対して、幼児、小中高生、大学生、社会人、定年後など意味のある特徴量を簡単に作成することができる。

例として、タイタニックデータの年齢カラムを使ってビニング処理を紹介する。

インポート

In [17]:
import numpy as np
import pandas as pd

# 描画用ライブラリ
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_style('darkgrid')
%matplotlib inline

データの読み込み

titanic データを読み込み、Age カラムだけを残し、欠損値を 100 で置換。これによりこの特徴量には、0 ~ 100 の値が入っている。

In [18]:
df = pd.read_csv('train.csv')
# Survived, Pclass, Age 以外のカラムを削除
df = df[['Age']]
# Ageカラムの NaN を 100 で置換して、type を integer に
df['Age'] = df['Age'].fillna(100).astype('int')

df.head()
Out[18]:
Age
0 22
1 38
2 26
3 35
4 35

データを確認

まず、Age カラムの分布を確認するためにヒストグラムを確認する。
ヒストグラムは連続値を任意の bin 数で区切ってそれぞれの範囲内に収まる個数を表示するので、ビニング処理を行ってるのと等しい。
ここでは、bin数 (=範囲数)を 10 個として表示。

In [33]:
plt.figure(figsize=(6, 4))
plt.hist(df['Age'], bins=10, color='orange')
plt.xlabel('Age', fontsize=15); plt.ylabel('Counts', fontsize=15)
plt.show()

ビニング処理 (pandas.cut())

pandas の cut() 関数は、引数にビニングしたい配列(リストやSeries)を渡し、bin数(整数)を渡すと、ビニングする境界値は、分割した値になる。

しかし、機械学習モデルに使用する際には、指定した任意の境界を設定したいことが多い。例えば、0 ~ 50 までを 10等分して、それ以上は一つの bin に収めるなど。
その場合は、引数に区切りたい境界値を要素とするリストを渡すことで実現できる。また、引数に labels を指定することで新しい特徴量のカテゴリを指定した label で表現できる。

In [20]:
# 単純に境界を 11 等分した区切りでビニングした特徴量
df['Age_split'] = pd.cut(df['Age'],
                         bins=10,
                         labels=[i for i in range(1, 11)])

# 指定した境界(0~45まで9等分、それ以上は一つのbinに)でビニングした特徴量
# 範囲の左端(この場合 0 を範囲内に含めるために、bins の初めの要素を -1 に
# 範囲の右端は、指定要素の値を含むので 100 で OK 
df['Age_optional'] = pd.cut(df['Age'],
                            bins=[-1,5,10,15,20,25,30,35,40,45,100],
                            labels=[i for i in range(1, 11)])

df.head()
Out[20]:
Age Age_split Age_optional
0 22 3 5
1 38 4 8
2 26 3 6
3 35 4 7
4 35 4 7

上記の処理で、2種類の新しいカテゴリ特徴量が作成された。どちらも labels に同じリスト(1~10)を渡しているが、bins の境界位置が異なることにより、実際に割り当てられた label が異なっていることがわかる。

例) index 0 において

  • Age_split : Age=22 より、20~30 という3個目のbin label 3 が割り当てられている。
  • Age_optional : Age=22 より、20~25 という5個目のbin label 5 が割り当てられている。 

グラフで確認

作成された2つの特徴量の個数を棒グラフとして表示してみる。ここで、Age_split は、10等分ビニングしただけなので、この棒グラフと初めのヒストグラムは同じ形になるはずである。

In [38]:
fig, ax = plt.subplots(1, 2, figsize=(10, 4), sharey='row')

bar1 = ax[0].bar(df['Age_split'].value_counts().index,
                 df['Age_split'].value_counts().values,
                 color='orange', width=1)
bar2 = ax[1].bar(df['Age_optional'].value_counts().index,
                 df['Age_optional'].value_counts().values,
                 color='skyblue', width=1)

ax[0].set_xlabel('bin number', fontsize=15)
ax[0].set_ylabel('Counts', fontsize=15)
ax[0].set_title('10 split binning', fontsize=15)
ax[1].set_xlabel('bin number', fontsize=15)
ax[1].set_title('Optional binning', fontsize=15)

plt.tight_layout()
plt.show()

左は、初めのヒストグラムと同じ結果になっていることがわかる。また左右のグラフから異なる境界でビニングされたことがわかる。
これにより、任意のビニング分割することで、同じ特徴量から異なる意味のカテゴリ特徴量を作成することができた。