sklearn中多種編碼方式——category_encoders(one-hot多種用法)


離散型編碼的Python庫,里面封裝了十幾種(包括文中的所有方法)對於離散型特征的編碼方法,接口接近於Sklearn通用接口,非常實用
可以使用多種不同的編碼技術把類別變量轉換為數值型變量,並且符合sklearn模式的轉換。

  • 官方github:https://github.com/scikit-learn-contrib/category_encoders
  • 官方文檔:http://contrib.scikit-learn.org/category_encoders/#

這個庫的作者將類別編碼分為兩類,無監督和有監督(指用target)

Unsupervised:

 1 Backward Difference Contrast
 2 BaseN
 3 Binary
 4 Count
 5 Hashing
 6 Helmert Contrast
 7 Ordinal
 8 One-Hot
 9 Polynomial Contrast
10 Sum Contrast

 

 

Supervised:

1 CatBoost
2 James-Stein Estimator
3 LeaveOneOut 
4 M-estimator
5 Target Encoding
6 Weight of Evidence

 

 



無監督中有很大一部分是線性模型/回歸模型用的對比編碼,有監督主要是目標編碼和WOE(Weight of Evidence)
利用標簽進行特征編碼是存在特征穿越的風險的,只不過很多時候影響並不大,不會出現極端的情況,利用標簽進行特征編碼例如target encoding、woe encoding或者是catboost encoding本質上都是利用類別和標簽之間的某種統計特征來代替原始的類別,從而使得無法直接處理類別的模型可以在編碼后的結果上正常運行。
woe編碼的穿越問題

文章目錄
1 Ordinal Encoding 序數編碼
2 One-hot Encoding 獨熱編碼
3 Target Encoding 目標編碼
4 BinaryEncoder 編碼
5 CatBoostEncoder編碼
6 WOEEncoder編碼
9 效果對比與使用心得
額外:10 用pandas的get_dummies進行one-hot
額外:11 文本one_hot的方式

 


1 Ordinal Encoding 序數編碼
專欄 | 基於 Jupyter 的特征工程手冊:數據預處理(二)
feature-engineering-handbook/中文版/

這個編碼方式非常容易理解,就是把所有的相同類別的特征編碼成同一個值,例如女=0,男=1,狗狗=2,所以最后編碼的特征值是在[0, n-1]之間的整數。

這個編碼的缺點在於它隨機的給特征排序了,會給這個特征增加不存在的順序關系,也就是增加了噪聲。假設預測的目標是購買力,那么真實Label的排序顯然是 女 > 狗狗 > 男,與我們編碼后特征的順序不存在相關性。

 1 import numpy as np
 2 import pandas as pd
 3 from category_encoders import OrdinalEncoder
 4 # category_encoders 直接支持dataframe
 5 
 6 # 隨機生成一些訓練集
 7 train_set = pd.DataFrame(np.array([['male',10],['female', 20], ['male',10], 
 8 ['female',20],['female',15]]),
 9 columns = ['Sex','Type'])
10 train_y = np.array([False, True, True, False, False])
11 
12 # 隨機生成一些測試集, 並有意讓其包含未在訓練集出現過的類別與缺失值
13 test_set = pd.DataFrame(np.array([['female',20],['male', 20], ['others',15], 
14 ['male',20],['female',40], ['male', 25]]),
15 columns = ['Sex','Type'])
16 test_set.loc[4,'Type'] = np.nan
17 
18 
19 encoder = OrdinalEncoder(cols = ['Sex', 'Type'], 
20 handle_unknown = 'value', 
21 handle_missing = 'value').fit(train_set,train_y) # 在訓練集上訓練
22 # 將 handle_unknown設為‘value’,即測試集中的未知特征值將被標記為-1
23 # 將 handle_missing設為‘value’,即測試集中的缺失值將被標記為-2
24 # 其他的選擇為:‘error’:即報錯;‘return_nan’:即未知值/缺失之被標記為nan 
25 encoded_train = encoder.transform(train_set) # 轉換訓練集
26 encoded_test = encoder.transform(test_set) # 轉換測試集
27 
28 # 以測試集結果為例
29 encoded_test
30 
31 # 在序數編碼中:
32 
33 # 變量Sex中: 'male' => 1.0, 'female' => 2.0, 未知 => -1.0, 缺失值 => -2.0
34 # (事實上,測試集中完全有可能出現未知與缺失情況)
35 # 在我們的例子中, Sex這一變量中的'other' 類別從未在訓練集中出現過
36 
37 # 變量 Type 中: 10 => 1.0, 20 => 2.0, 15 => 3.0, 未知 => -1.0, 缺失值 => -2.0

 

 

 

變成序列化:

 

 

 


2 One-hot Encoding 獨熱編碼
專欄 | 基於 Jupyter 的特征工程手冊:數據預處理(二)
feature-engineering-handbook/中文版/

大家熟知的OneHot方法就避免了對特征排序的缺點。對於一列有N種取值的特征,Onehot方法會創建出對應的N列特征,其中每列代表該樣本是否為該特征的某一種取值。因為生成的每一列有值的都是1,所以這個方法起名為Onehot特征。Dummy特征也是一樣,只是少了一列,因為第N列可以看做是前N-1列的線性組合。但是在離散特征的特征值過多的時候不宜使用,因為會導致生成特征的數量太多且過於稀疏。

Scikit-learn中也提供來獨熱編碼函數,其可以將具有n_categories個可能值的一個分類特征轉換為n_categories個二進制特征,其中一個為1,所有其他為0在category_encoders中,它包含了附加功能,即指示缺失或未知的值。在這里,我們繼續使用category_encoders

 1 import numpy as np
 2 import pandas as pd
 3 from category_encoders import OneHotEncoder
 4 # category_encoders 直接支持dataframe
 5 
 6 # 隨機生成一些訓練集
 7 train_set = pd.DataFrame(np.array([['male',10],['female', 20], ['male',10], 
 8 ['female',20],['female',15]]),
 9 columns = ['Sex','Type'])
10 train_y = np.array([False, True, True, False, False])
11 
12 # 隨機生成一些測試集, 並有意讓其包含未在訓練集出現過的類別與缺失值
13 test_set = pd.DataFrame(np.array([['female',20],['male', 20], ['others',15], 
14 ['male',20],['female',40], ['male', 25]]),
15 columns = ['Sex','Type'])
16 test_set.loc[4,'Type'] = np.nan
17 
18 
19 encoder = OneHotEncoder(cols=['Sex', 'Type'], 
20 handle_unknown='indicator', 
21 handle_missing='indicator', 
22 use_cat_names=True).fit(train_set,train_y) # 在訓練集上訓練
23 encoded_train = encoder.transform(train_set) # 轉換訓練集
24 encoded_test = encoder.transform(test_set) # 轉換測試集
25 # 將 handle_unknown設為‘indicator’,即會新增一列指示未知特征值
26 # 將 handle_missing設為‘indicator’,即會新增一列指示缺失值
27 # 其他的handle_unknown/handle_missing 的選擇為:
28 # ‘error’:即報錯; ‘return_nan’:即未知值/缺失之被標記為nan; ‘value’:即未知值/缺失之被標記為0
29 
30 # 以測試集結果為例
31 encoded_test
32 
33 # 在獨熱編碼中:
34 
35 # 變量 Sex => 變為了4個新變量: 'male' => [1 ,0 ,0, 0];
36 # 'female' => [0 ,1 ,0, 0];
37 # 未知 => [0 ,0 ,0, 1];
38 # 缺失 => [0, 0, 1, 0];
39 
40 # 變量 Type => 變為了5個新變量: 10 => [1, 0, 0, 0, 0];
41 # 20 => [0, 1, 0, 0, 0];, 
42 # 15 => [0, 0, 1, 0, 0];
43 # 未知 => [0, 0, 0, 0, 1];
44 # 缺失 => [0, 0, 0, 1, 0];

 

 

 

變成了:

 

 

 

 


3 Target Encoding 目標編碼
專欄 | 基於 Jupyter 的特征工程手冊:數據預處理(二)
feature-engineering-handbook/中文版/

目標編碼是一種不僅基於特征值本身,還基於相應因變量的類別變量編碼方法。對於分類問題:將類別特征替換為給定某一特定類別值的因變量后驗概率與所有訓練數據上因變量的先驗概率的組合。對於連續目標:將類別特征替換為給定某一特定類別值的因變量目標期望值與所有訓練數據上因變量的目標期望值的組合。該方法嚴重依賴於因變量的分布,但這大大減少了生成編碼后特征的數量。

公式:

 

 

其中min_samples_leaf和smoothing是用戶定義的參數;
min_samples_leaf:計算類別平均值時的最小樣本數(即若該類別出現次數少,則將被忽略),用以控制過擬合;
smoothing:平衡分類平均值與先驗平均值的平滑系數。其值越高,則正則化越強;
′ 是類別特征X中類別為k的編碼值;
Prior Prob:目標變量的先驗概率/期望;
n:類別特征X中,類別為k的樣本數;
+:不僅在類別特征X中具有類別k,而且具有正結果的樣本數(分類問題);

參考文獻: Micci-Barreca, D. (2001). A preprocessing scheme for high-cardinality categorical attributes in classification and prediction problems. ACM SIGKDD Explorations Newsletter, 3(1), 27-32.

 1 import numpy as np
 2 import pandas as pd
 3 from category_encoders.target_encoder import TargetEncoder
 4 # category_encoders 直接支持dataframe
 5 
 6 # 隨機生成一些訓練集
 7 train_set = pd.DataFrame(np.array([['male',10],['female', 20], ['male',10], 
 8 ['female',20],['female',15]]),
 9 columns = ['Sex','Type'])
10 train_y = np.array([False, True, True, False, False])
11 
12 # 隨機生成一些測試集, 並有意讓其包含未在訓練集出現過的類別與缺失值
13 test_set = pd.DataFrame(np.array([['female',20],['male', 20], ['others',15], 
14 ['male',20],['female',40], ['male', 25]]),
15 columns = ['Sex','Type'])
16 test_set.loc[4,'Type'] = np.nan
17 
18  
19 
20 encoder = TargetEncoder(cols=['Sex','Type'], 
21 handle_unknown='value', 
22 handle_missing='value').fit(train_set,train_y) # 在訓練集上訓練
23 encoded_train = encoder.transform(train_set) # 轉換訓練集
24 encoded_test = encoder.transform(test_set) # 轉換測試集
25 
26 # handle_unknown 和 handle_missing 被設定為 'value'
27 # 在目標編碼中,handle_unknown 和 handle_missing 僅接受 ‘error’, ‘return_nan’ 及 ‘value’ 設定
28 # 兩者的默認值均為 ‘value’, 即對未知類別或缺失值填充訓練集的因變量平均值
29 
30 encoded_test # 編碼后的變量數與原類別變量數一致

 

 

 

 到了

   

 

 

 1 # 驗證一下計算的結果,在測試集中,‘male’類別的編碼值為 0.473106
 2 prior = train_y.mean() # 先驗概率
 3 min_samples_leaf = 1.0 # 默認為1.0
 4 smoothing = 1.0 # 默認為1.0
 5 n = 2 # 訓練集中,兩個樣本包含‘male’這個標簽
 6 n_positive = 1 # 在訓練集中,這兩個包含‘male’標簽的樣本中僅有一個有正的因變量標簽
 7 
 8        = 1 / (1 + np.exp(-(n - min_samples_leaf) / smoothing))
 9 male_encode = prior * (1-      ) +        * n_positive/n
10 male_encode # return 0.4731058578630005,與要驗證的值吻合

 

0.4731058578630005

 

 


4 BinaryEncoder 編碼

 1 # 相關模塊加載
 2 import pandas as pd
 3 import category_encoders as ce
 4 
 5 # 准備數據
 6 df = pd.DataFrame({'ID':[1,2,3,4,5,6],
 7 'RATING':['G','B','G','B','B','G']})
 8 
 9 # 使用binary編碼的方式來編碼類別變量
10 encoder = ce.BinaryEncoder(cols=['RATING']).fit(df)
11 
12 # 轉換數據
13 numeric_dataset = encoder.transform(df)
14 
15 df # 轉換前的數據

 

 

到:

 

 


5 CatBoostEncoder編碼


這個跟CatBoost一致,是Catboost中的encode方法,這個方法據說效果非常好,而且可以避免過擬合,可能有些復雜

 1 import pandas as pd
 2 import numpy as np
 3 #from unittest import TestCase # or `from unittest import ...` if on Python 3.4+
 4 
 5 import category_encoders as encoders
 6 
 7 X = pd.DataFrame({'col1': ['A', 'B', 'B', 'C', 'A']})
 8 y = pd.Series([1, 0, 1, 0, 1])
 9 enc = encoders.CatBoostEncoder()
10 obtained = enc.fit_transform(X, y)
11 obtained
12 
13 # For testing set, use statistics calculated on all the training data.
14 # See: CatBoost: unbiased boosting with categorical features, page 4.
15 X_t = pd.DataFrame({'col1': ['B', 'B', 'A']})
16 obtained = enc.transform(X_t)
17 obtained

 

本來:

 

 

現在:

1 col1
2 0 0.6
3 1 0.6
4 2 0.3
5 3 0.6
6 4 0.8

 

其他案例(github):

1 X = pd.DataFrame({'col1': ['fuzzy', 'soft', 'smooth', 'fuzzy', 'smooth', 'soft', 'smooth', 'smooth']})
2 y = pd.Series([4, 1, 4, 3, 6, 0, 7, 5])
3 enc = encoders.CatBoostEncoder()
4 obtained = enc.fit_transform(X, y)
5 prior = 30./8

 


6 WOEEncoder編碼
【數據建模 WOE編碼】WOE(weight of evidence, 證據權重)
一種有監督的編碼方式,將預測類別的集中度的屬性作為編碼的數值

  • 優勢

            將特征的值規范到相近的尺度上。
            (經驗上講,WOE的絕對值波動范圍在0.1~3之間)。
            具有業務含義。

  • 缺點

            需要每箱中同時包含好、壞兩個類別。
當然也會出現標簽穿越的問題:woe編碼的穿越問題

 

 

 

1 X = ['a', 'a', 'b', 'b']
2 y = [1, 0, 0, 0]
3 enc = encoders.WOEEncoder()
4 
5 result = enc.fit_transform(X, y)

 

 1 從:
 2 (['a', 'a', 'b', 'b'], [1, 0, 0, 0])
 3 
 4 變成:
 5 
 6 0
 7 0 0.510826
 8 1 0.510826
 9 2 -0.587787
10 3 -0.587787

 

案例二:

1 cols = ['unique_str', 'underscore', 'extra', 'none', 'invariant', 321, 'categorical', 'na_categorical', 'categorical_int']
2 
3 # balanced label with balanced features
4 X_balanced = pd.DataFrame(data=['1', '1', '1', '2', '2', '2'], columns=['col1'])
5 y_balanced = [True, False, True, False, True, False]
6 enc = encoders.WOEEncoder()
7 enc.fit(X_balanced, y_balanced)
8 X1 = enc.transform(X_balanced)

 

9 效果對比與使用心得
11種離散型變量編碼方式及效果對比
語雀文檔

數據集使用了八個存在離散型變量的數據集,最后的結果加權如下:

不使用交叉驗證的情況:

 1 HelmertEncoder    0.9517
 2 SumEncoder    0.9434
 3 FrequencyEncoder    0.9176
 4 CatBoostEncoder    0.5728
 5 TargetEncoder    0.5174
 6 JamesSteinEncoder    0.5162
 7 OrdinalEncoder    0.4964
 8 WOEEncoder    0.4905
 9 MEstimateEncoder    0.4501
10 BackwardDifferenceEncode0.4128
11 LeaveOneOutEncoder    0.0697

 



使用交叉驗證的情況:

 1 CatBoostEncoder    0.9726
 2 OrdinalEncoder    0.9694
 3 HelmertEncoder    0.9558
 4 SumEncoder    0.9434
 5 WOEEncoder    0.9326
 6 FrequencyEncoder    0.9315
 7 BackwardDifferenceEncode0.9108
 8 TargetEncoder    0.8915
 9 JamesSteinEncoder    0.8555
10 MEstimateEncoder    0.8189
11 LeaveOneOutEncoder    0.0729

 


下面是Kaggle上大佬們給出的一些建議,具體原因尚未分析,希望有大神在評論區可以給出解釋。

對於無序的離散特征,實戰中使用 OneHot, Hashing, LeaveOneOut, and Target encoding 方法效果較好,

但是使用OneHot時要避免高基類別的特征以及基於決策樹的模型,理由如下圖所示。

 

 


但是在實戰中,我發現使用Xgboost處理高維稀疏的問題效果並不會很差。例如在IJCAI-18商鋪中用戶定位比賽中,一個很好的baseline就是把高維稀疏的wifi信號向量直接當做特征放到Xgboost里面,也可以獲得很好的預測結果。不知道是不是因為Xgboost對於稀疏特征的優化導致。

  • 對於有序離散特征,嘗試 Ordinal (Integer), Binary, OneHot, LeaveOneOut, and Target. Helmert, Sum, BackwardDifference and Polynomial 基本沒啥用,但是當你有確切的原因或者對於業務的理解的話,可以進行嘗試。
  • 對於回歸問題而言,Target 與 LeaveOneOut 方法可能不會有比較好的效果。
  • LeaveOneOut、 WeightOfEvidence、 James-Stein、M-estimator 適合用來處理高基數特征。
  • Helmert、 Sum、 Backward Difference、 Polynomial 在機器學習問題里的效果往往不是很好(過擬合的原因)

額外:10 用pandas的get_dummies進行one-hot
參考:pandas.get_dummies 的用法

 1 pandas.get_dummies(data, prefix=None, prefix_sep='_', dummy_na=False, columns=None, sparse=False, drop_first=False)  2 
 3 import pandas as pd
 4 df = pd.DataFrame([ 
 5 ['green' , 'A'], 
 6 ['red' , 'B'], 
 7 ['blue' , 'A']])
 8 
 9 df.columns = ['color', 'class'] 
10 pd.get_dummies(df) 

 


get_dummies 前:

 

 


get_dummies 后:

 

 

 

上述執行完以后再打印df 出來的還是get_dummies 前的圖,因為你沒有寫
df = pd.get_dummies(df)

可以對指定列進行get_dummies
pd.get_dummies(df.color)

 

 


額外:11 文本one_hot的方式

 1 from sklearn.feature_extraction.text import CountVectorizer 
 2 #from sklearn.feature_extraction.text import TfidfTransformer
 3 import pandas as pd
 4 
 5 def text_one_hot(tag_list,prefix = ''):
 6 '''
 7 Parameters
 8 ----------
 9 tag_list : TYPE
10 常規分詞,以空格隔開.[['格式','不一定'],['空格','隔開','常規']]
11 prefix : TYPE, optional
12 前綴. The default is ''.
13 
14 Returns
15 -------
16 data : TYPE
17 dataframe,完整的TF的頻次.
18 
19 '''
20 vectorizer = CountVectorizer() #將文本中的詞語轉換為詞頻矩陣 
21 X = vectorizer.fit_transform(tag_list) #計算個詞語出現的次數
22 
23 data = pd.DataFrame(X.toarray(),columns = [prefix +'_'+ si for si in sorted(vectorizer.vocabulary_)])
24 return data
25 
26 tag_list = ['青年 吃貨', 
27 '少年 游戲 叛逆', 
28 '少年 吃貨 足球'] 
29 data = text_one_hot(tag_list)

 

返回的結果是:

1 _叛逆 _吃貨 _少年 _游戲 _足球 _青年
2 0 0 1 0 0 0 1
3 1 1 0 1 1 0 0
4 2 0 1 1 0 1 0

 


————————————————
版權聲明:本文為CSDN博主「悟乙己」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/sinat_26917383/article/details/107851162


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM