【sklearn】LabelEncoderの使い方を丁寧に

はじめに

本記事ではsklearn.preprocessing.LabelEncoder()について丁寧に説明します.

公式ドキュメント:

scikit-learn.org

LabelEncoderの役割

LabelEncoder()は,文字列や数値で表されたラベルを,0~(ラベル種類数-1)までの数値に変換してくれるものです.機械学習で分類系のタスクを扱う場合,正解のラベルが文字列で表されることはよくあります.このようなとき,LabelEncoder()を使うと簡単に数値に変換できるという感じです.

LabelEncoderの基本的な入出力

エンコーダを想定した入出力です.

  • 入力

入力は,各要素がラベルであるような一次元リストです.データ型はpythonの生のリストはもちろん,numpyの'numpy.ndarray',pandasのpandas.core.series.Series も受け付けます.リストの各要素は文字列でも良いですし,数値でも良いです.

例:['positive', 'negative', 'positive']

  • 出力

出力は,入力の各ラベルが0~(ラベル種類数-1)の数値に変換された一次元リストです.

例:[0, 1, 0]

例では,positive0negative1が対応していることが分かります.以降,本記事では入力側をラベル,出力側をラベルIDと呼ぶことにします.

LabelEncoderの宣言

宣言は簡単です.特にオプションも必要ありません.

from sklearn import preprocessing
le = preprocessing.LabelEncoder()

fit()

まずは,ラベルとラベルIDの対応づけを行います.positiveは0にしよう,みたいなことを決めるのです.これはle.fit(ラベルの一次元リスト)で行います.

from sklearn import preprocessing
le = preprocessing.LabelEncoder()

labels = ['positive', 'negative', 'positive']
le.fit(labels)

transform() (ラベル→ラベルID)

ラベルとラベルIDの対応づけができれば,それに従って変換することができます.これはtransform(ラベルの一次元リスト)で行います.fitはle.fit()を書くだけですが,transformはhoge = le.transform()のように返り値を保存する必要があります.

from sklearn import preprocessing
le = preprocessing.LabelEncoder()

labels = ['positive', 'negative', 'positive']
le.fit(labels)
labels_id = le.transform(labels)
print(labels_id) # [1 0 1]

これで変換が完了しました.

fit_transform()

fit()transform()を一気に行う場合,fit_transform()を使うと楽です.

入力がラベルの一次元リスト,出力がラベルIDの一次元リストになっています.

from sklearn import preprocessing
le = preprocessing.LabelEncoder()

labels = ['positive', 'negative', 'positive']
labels_id = le.fit_transform(labels)
print(labels_id) # [1 0 1]

inverse_transform() (ラベルID→ラベル)

transformがエンコードだとすると,これはデコードする処理になります.

入力がラベルIDの一次元リスト,出力がラベルのリストです.

あくまでもエンコードができないとデコードもできないため,必ずfit()した後に行います.

from sklearn import preprocessing
le = preprocessing.LabelEncoder()

labels = ['positive', 'negative', 'positive']
# とりあえずfitしてから
le.fit(labels)
ids = [1, 1, 0]
# デコード
decoded_labels = le.inverse_transform(ids)
print(ids, '->', decoded_labels)
# [1, 1, 0] -> ['positive' 'positive' 'negative']

classes_(どのラベルがどのIDなのかを取得)

le.classes_で, fit()によって各ラベルがどのラベルIDと対応づけられたのかを取得できます.

le.classes_は一次元リストになっていて,各要素について,値がラベル,その添字がラベルIDと解釈できます.例えば['negative' 'positive']という一次元リストであれば,negativeの添字は0なので,negative0が対応していることが分かります.

from sklearn import preprocessing
le = preprocessing.LabelEncoder()

labels = ['positive', 'negative', 'positive']
le.fit(labels)
print(le.classes_)
# ['negative' 'positive']

使い所としては,評価系のライブラリを使った時に,オプションとしてラベル名を渡すような場面が考えられます.例えば,sklearnのclassification_reporttarget_names=引数みたいなものです.

2次元以上のラベル列をエンコードする時

こういう場合があるか分かりませんが,2次元リストの形式で保存されたラベル列をIDに変換したいときの話です.これは間違いの方法ですが,とりあえず,for文で一つずつ処理することを考えます.

# !!! バグが発生しうる例 !!!
from sklearn import preprocessing
le = preprocessing.LabelEncoder()

labels_list = [['positive', 'negative'],
               ['negative', 'positive']]
ids_list = [le.fit_transform(labels).tolist() for labels in labels_list]
print(ids_list)
"""
[[1, 0], 
 [0, 1]]
"""

ただし,上の例は偶然うまくいっているものの,少し安直で,バグを生みます.というのは,変換をfit_transform()で行なっているため,変換するたびにfit()もされてしまいます.これにより,ラベルとIDの対応づけが変換のたびに更新されてしまいます.

バグを生む例を作るとこうなります.

# !!! バグがある例 !!!
from sklearn import preprocessing
le = preprocessing.LabelEncoder()

labels_list = [['a', 'b'], 
               ['b', 'c']]
ids_list = [le.fit_transform(labels).tolist() for labels in labels_list]
print(ids_list)
"""
[[0, 1],
 [0, 1]]

('b'が0と1に変換されているし,'b'と'c'が1に変換されている.本当は
[[0, 1],
 [1, 2]] 
であるはず)
"""

というわけで,2次元以上のラベル列を変換する場合,fit()は全てのラベルが含まれるようなリストで一度だけ行いましょう.どのようなラベルが存在するかは事前に分かるはずなので,そのようなリストは準備できるはずです.

from sklearn import preprocessing
le = preprocessing.LabelEncoder()

# 全てのラベルを並べてfit()だけしておく
all_labels = ['a', 'b', 'c']
le.fit(all_labels)

labels_list = [['a', 'b'], ['b', 'c']]
# 変換処理はfit_transform()ではなくて,transform()で
ids_list = [le.transform(labels).tolist() for labels in labels_list]
print(ids_list)
"""
[[0, 1],
 [1, 2]]
"""