機械学習を行う際、前処理をする必要があります。そんなときに文字データをそのまま学習させるとエラーになる場合があります。
そのような場合、「category_encoders」などを用いて数値に変換(エンコード)することで回避する必要が出てきます。
そこで、ここでは文字列を数値に変換する方法を紹介しています。
この記事の対象
カテゴリ変数とは?
カテゴリ変数とは、「定量的」ではないデータを指します。
回りくどいですが、「定量的ではないデータ」とは「数値型」ではないデータと言い換えられます。
例えば「性別」とか「評価(○×,合格,不合格)」などになります。補足ですが、数値であっても、クラスの1組、2組などの数値自体に意味のないもの(順序尺度)もカテゴリ変数となります。
整理すると、
- 順序尺度:サイズ、階層、評価など
- 名義尺度:性別、血液型、郵便番号など
となります。
カテゴリ変数を数値データに変換する目的
機械学習を行う際、決定木などはカテゴリ変数をそのまま扱うことができまが、ロジスティック回帰やニューラルネットワークでは、カテゴリ変数をそのまま使用すると悲惨なことになります。そこで、数値に変換して正しく機械に解釈させる必要があります。
ちなみに、これらの過程を「エンコーディング」と言われています。
エンコーディングの主な種類
代表的な数値化手法(エンコーディング)は以下になります。
- Label Encoding:カテゴリを0~カテゴリ種類n-1の値を割り当てる。決定木分析、ランダムフォレストに適している。
- One Hot Encoding:ダミー変数化する。多重共線性の問題が出る。回帰分析に適している。
- Count Encoding:各カテゴリデータの頻出回数を数値として割り当てる。
- Target Encoding:カテゴリごとに目的変数の平均情報を数値として割り当てる。
ここでは、「Label Encoding」「One Hot Encoding」「Count Encoding」の変換方法について解説しています。
順序尺度の数値化方法
例えば、テストの評価としてA,B,Cがあるとします。
これは「A>B>C」と順序がある尺度と解釈できます。この場合は「A→1」「B→2」「C→3」と数値化できます。
以下のようなデータがあるとします。
import pandas as pd
df = pd.DataFrame([
['A', 100, 'Math'],
['B', 50, 'Science'],
['C', 20, 'History']])
df.columns = ['evaluation', 'number', 'label']
出力結果は以下の通りです。
evaluation number label
0 A 100 Math
1 B 50 Science
2 C 20 History
それでは、これを数値化すると以下のようになります。
change_eva = {'A': 1, 'B': 2, 'C': 3}
df['evaluation'] = df['evaluation'].map(change_eva)
【結果】
evaluation number label
0 1 100 Math
1 2 50 Science
2 3 20 History
名義尺度の数値化方法
Label Encoding
「Label Encoding」は「scikit-learn」にある「LabelEncoder」というクラスを使用することで簡単に変換できます。
from sklearn.preprocessing import LabelEncoder
lab = LabelEncoder()
lab = lab.fit(df['label'])
df['label'] = lab.transform(df['label'])
「LabelEncoding」は後述する「One hot Encoding」に対して、新しく作成される変数は1つです。これはData数が多くならずに済むメリットがあります。
逆にデメリットとしては、数値の大小に意味がないので、ロジスティック回帰等には不向きな変換方法となります。
【結果】
evaluation number label
0 1 100 1
1 2 50 2
2 3 20 0
One Hot Encoding
「One Hot Encoding」はダミー変数化ともいい、カテゴリの数だけ変数を作成して該当するか否かで0,1のテーブルを作成します。
One Hot Encodingは「scikit-learn」の「OneHotEncoder」クラスを用いることで簡単に変換できますが、ここでは、便宜上「category_encoders」を用いています。
import category_encoders as cat_en
list_cols = ['label']
#1--列指定
oneh = cat_en.OneHotEncoder(cols=list_cols)#encoderの生成
oneh_out = oneh.fit_transform(df[list_cols])#実行
out = pd.concat([df, oneh_out], axis=1)#元のdataと結合
One Hot Encodingは非常にシンプルな数値変換になります。
デメリットはカテゴリの数だけ変数を作成して0,1を振っていくため、列が増えてしまいます。また、線形回帰などでは変数同士の相関が高くなり、多重共線性の問題が出てしまうのも特徴です。
【結果】
evaluation number label label_1 label_2 label_3
0 1 100 Math 1 0 0
1 2 50 Science 0 1 0
2 3 20 History 0 0 1
このままでは余計な情報が多いので、「label」列を削除したり、列名を変換して分かりやすくする必要があります。
「get_dummies」→列情報を整理
df_dummy = pd.get_dummies(df[list_cols])
out = pd.concat([df.drop(['label'],axis=1),df_dummy],axis=1)
上記では、label列をダミー変数化して、元のDataFrameに結合しています。
【結果】
evaluation number label_History label_Math label_Science
0 1 100 0 1 0
1 2 50 0 0 1
2 3 20 1 0 0
「label」列の削除と列名を変換できていることが確認できました。次に、多重共線性を回避するために列要素を1つ削除します。これは「get_dummies」で「drop_first = True」とするだけで最初の列を削除できます。
df_dummy = pd.get_dummies(df[list_cols],drop_first = True)
out = pd.concat([df.drop(['label'],axis=1),df_dummy],axis=1)
【結果】
evaluation number label_Math label_Science
0 1 100 1 0
1 2 50 0 1
2 3 20 0 0
削除してもDataの欠損にならない理由としては「label_Math」「label_Science」がともに0の場合は「label_History」であることが分かるからです。
Count Encoding
「Count Encoding」はデータに含まれるカテゴリ変数の出現回数を数えたものになります。
pcategory_encodersの「CountEncoder」を用いて簡単に割り当てることができます。
import pandas as pd
import numpy as np
from category_encoders import CountEncoder
df = pd.DataFrame([
['A', 100, 'Math'],
['B', 50, 'Science'],
['C', 20, 'History'],
['A', 80, 'Math']])
df.columns = ['evaluation', 'number', 'label']
change_eva = {'A': 1, 'B': 2, 'C': 3}
df['evaluation'] = df['evaluation'].map(change_eva)
col = 'label'
encoder = CountEncoder()
encoder.fit(df[col])
df['%s_count'%col] = encoder.transform(df[col])
列を指定して、頻度を算出しています。
デメリットは異なるカテゴリカル変数に同じ値が割り当てられ、情報が失われる場合があることです。今回だったら、ScienceとHistoryが1になっていることです。
【結果】
evaluation number label label_count
0 1 100 Math 2
1 2 50 Science 1
2 3 20 History 1
3 1 80 Math 2
最後に
それぞれEncodingにも特徴があることが分かります。
Dataをよく観察してからEncoding方法を決定する必要がありますね。