[ML]LabelEncoder will order with alphabet(Sklearn)

Posted by John on 2018-09-16
Words 824 and Reading Time 3 Minutes
Viewed Times

最近沉在kaggle浮不上來,快要被淹死了,哀…

扯遠了,今天要講的是一個在練習的時候遇到的坑(其實遇到很多坑,之後再慢慢補吧,因為我很懶)。

在做Machine Learning時,大家都知道對於Categorical features要做preprocess,將分類的資料轉成數值型的資料才能丟到model去train。基本上有兩種轉換的方式,One-hot Encoding和Label Encoding,先簡單簡介下兩者的差異:

差異簡介

  • One-hot Encoding: 如果資料有N個類別,就會轉換成N維度空間,使得每個類別對應到特定的維度。例如{‘Apple’, ‘Banana’, ‘CClemon’} -> {(1, 0, 0), (0, 1, 0), (0, 0, 1)}。
  • Label Encoding: 將N個類別用數值代替,維度只有一維。例如{‘Apple’, ‘Banana’, ‘CClemon’} -> {1, 2, 3}。

One-hot Encoding,在還是Dataframe的時候可以直接用pd.get_dummies(),或是可以使用sklearn的OneHotEncoder(),又或者可以使用keras.util的to_categorical();Label Encoding則是可以自己寫或是透過sklearn的LabelEncoder()進行轉換。

使用時機

同樣都是Categorical卻有不同的做法,這兩個差在哪裡呢?這要取決於feature的性質:

  • 如果Categorical資料是Nominal類型的,也就是說類別之間並沒有一個次序性的關係,像是{‘貓’, ‘狗’},只是不同的種類,但狗在貓後面並不代表狗比較厲害還是怎樣的,這時候就會比較適合用One-hot encoding。
  • 如果Categorical資料是Ordinal類型的,類別之間有一定的順序性或是關聯,例如{‘小’, ‘中’, ‘大’},那就比較適合用Label encoding,因為轉換後變成{1, 2, 3},我們仍然可以透過數字的關係(3>2, 2>1)來取得原本類別之間的資訊。

好啦嚴格說起來也不是坑,就是我在用sklearn的LabelEncoder做Encoding的時候,卻發現出來的次序都不是我想要的關係,例如我原始的資料是

{‘first’, ‘second’, ‘third’, ‘fourth’}

我想要得到得到的對應數字是{1, 2, 3, 4},可是我做出來的結果卻是

{1, 4, 2, 3} #{‘first’, ‘fourth’, ‘second’, ‘third’}

???

這一定哪裡有問題

當時花了不少時間再找問題,然後再stackoverflow上找到有人有跟我一樣的問題,我用中文簡述一下原因:

以下片段是sklearn的LabelEncoder()的sourcecode片段(在transform()這個function裡面)

1
2
3
4
5
6
...
classes = np.unique(y)
if len(np.intersect1d(classes, self.classes_)) < len(classes):
diff = np.setdiff1d(classes, self.classes_)
raise ValueError("y contains new labels: %s" % str(diff))
return np.searchsorted(self.classes_, y)

裡面實際上是怎麼運作的就不闡述了(其實我也沒有仔細看),不過我們可以看到裡面用到了setdiff1d()這個函式去比較兩個class的差異,問題就在這個setdiff1d()return的是sorted list,也就是說如果我們的資料是英文字串,回傳的list就會是按照英文字母(alphabet)排序後的list。

結論

所以說,如果今天想要對資料進行Label Encoding的時候,如果我們想避免上述問題,也就是轉換後仍然保留類別之間的次序性,我們可能要用其他的方式來轉換,例如pd.map(),以下給個簡單的例子。

1
dataset_df[feature] = dataset_df[feature].map({'Ex':5, 'Gd':4, 'TA':3, 'Fa':2, 'Po':1, 'None':0})

>