數據預處理:離散特征編碼方法
Python庫category_encoders將離散特征的編碼方法分為2類:有監督和無監督。
各種編碼方法的原理如何呢?
無監督方法:
1.序號編碼OrdinalEncoder
序號編碼通常用於處理類別間具有大小關系的數據。如產品等級分為高、中、低三檔,存在“高>中>低”的排序關系。序號編碼則按照大小關系對類別型特征賦值一個數值ID,如高表示為3,中表示為2,低表示為1,轉換后依然保持大小關系。
如果實際業務中,明確的是有序的離散特征,嘗試 Ordinal(Integer)。
import category_encoders as ce
import pandas as pd
data = pd.DataFrame({'ID':[1,2,3,4,5,6,7,8],'Sex':['F','M','M','F','M',None,'F','M'],
'BloodType':['A','AB','O','B', None,'O','AB','B'],'Grade':['High', None,'Medium','Low', 'Low','Medium','Low','High'],
# 'Height':[156, None, 167, 175, 164, 180], 'Weight':[50, None, 65, 67, 48, 76],
'Education':['PhD','HighSchool','Bachelor','Master','HighSchool','Master','PhD','Bachelor'],
'Income':[28300, 4500, 7500, 12500, 4200, 15000, 25000, 7200]})
print('\nOriginal Dataset:\n', data)
ce_1 = ce.OrdinalEncoder(cols=['Grade', 'Education'],
mapping=[{'col':'Grade','mapping':{None:0, 'Low':1, 'Medium':2, 'High':3}},{
'col':'Education','mapping':{None:0,'HighSchool':1,'Bachelor':2, 'Master':3,'PhD':4}}]).fit_transform(data)
print('\nOrdinalEncoder Return the transformed dataset:\n', ce_1)
2.獨熱編碼OneHotEncoder
獨熱編碼通常用於處理類別間不具有大小關系的特征。例如血型,一共4個值,獨熱編碼將其變成4維的稀疏向量。獨熱編碼的特征向量只有一維取值為1,其余為0。缺點是它處理不好類別取值多的特征,類別數越大會帶來過很多列的稀疏特征,消耗內存和訓練時間。對於類別取值較多的情況要注意通過特征選擇降低維度。
ce_2 = ce.OneHotEncoder(cols=['BloodType'], use_cat_names=True).fit_transform(data)
print('\nOneHotEncoder Return the transformed dataset:\n', ce_2)
3.二進制編碼BinaryEncoder
二進制編碼分2步,先用序號編碼給每個類別賦予一個類別ID,然后將類別ID對應的二進制編碼作為結果。本質 上是利用二進制對ID進行哈希映射,最終得到0/1特征向量,且維數少於獨熱編碼,節省存儲空間。
優點:
- 容易實現
- 分類很精確
- 可用於在線學習
缺點:
- 計算效率不高
- 不能適應可增長的類別
- 只適用於線性模型
- 對於大數據集,需要大規模的分布式優化
ce_3 = ce.BinaryEncoder(cols=['BloodType']).fit_transform(data)
print('\nBinaryEncoder Return the transformed dataset:\n', ce_3)
4.計數編碼CountEncoder
對於給定的分類特征,按照每個類別分組,統計組計數,將每個類別都映射到該類別的樣本數。清晰地反映了類別在數據集中的出現次數,缺點是忽略類別的物理意義,比如說兩個類別出現頻次相當,但是在業務意義上,模型的重要性也許不一樣。這個編碼可以指示每個類別的“可信度”,例如,機器學習算法可以決定僅考慮其類別計數高於某個閾值的類別所帶來的信息。
ce_4 = ce.CountEncoder(cols=['Sex','BloodType','Grade','Education']).fit_transform(data)
print('\nCountEncoder Return the transformed dataset:\n', ce_4)
5.哈希編碼HashingEncoder
哈希編碼是使用二進制對標簽編碼做哈希映射。好處在於哈希編碼器不需要維護類別字典,且輸出長度是固定的。若后續出現訓練集未出現的類別,哈希編碼還能接受新值。另外,對於類別取值較多的特征,哈希法編碼可以將原始的高維特征向量壓縮成較低維特征向量,且盡量不損失原始特征的表達能力。但按位分開哈希編碼,模型學習相對比較困難。
優點:
- 容易實現
- 模型訓練成本更低
- 容易適應新類別
- 容易處理稀有類
- 可用於在線學習
缺點:
- 只適合線性模型或核方法
- 散列后的特征無法解釋
- 精確度難以保證
ce_5 = ce.HashingEncoder(cols=['Education']).fit_transform(data)
print('\nReturn the transformed dataset:\n', ce_5)
需要注意的是哈希編碼會報錯:
Default value of ‘max_process’ is 1 on Windows because multiprocessing might cause issues, see in : https://github.com/scikit-learn-contrib/categorical-encoding/issues/215 https://docs.python.org/2/library/multiprocessing.html?highlight=process#windows
6.BaseNEncoder
Base-N 編碼器將類別編碼為它們的 base-N 表示的數組。 基數 1 等價於 one-hot 編碼(不是真正的 base-1,但很有用),基數 2 等價於二進制編碼。 N=實際類別的數量,相當於普通的序數編碼。
不太清楚這種編碼的實際應用。
這里以Base-3和Base-4為例。
ce_6_1 = ce.BaseNEncoder(cols=['BloodType'], base=3).fit_transform(data)
print('\nBaseNEncoder Return the transformed dataset 1(base=3):\n', ce_6_1)
ce_6_2 = ce.BaseNEncoder(cols=['BloodType'], base=4).fit_transform(data)
print('\nBaseNEncoder Return the transformed dataset 2(base=4):\n', ce_6_2)
7.Sum Contrast
“對比”編碼是一種方法,通過與分類特征的其他取值比較,為分類特征中的每個不同的值設置列的值。
UCLA:R LIBRARY CONTRAST CODING SYSTEMS FOR CATEGORICAL VARIABLES
介紹了常見的對比編碼方法和公式,並解釋了系數與均值的關系,是category_encoders庫引用的文章。
在回歸分析中,一個有K 個類別的類別變量,通常作為 K-1 個變量的序列輸入,例如作為 K-1 個虛擬變量。 隨后,這些 K-1 個變量的回歸系數對應於一組關於單元均值的線性假設。 在對分類變量進行編碼時,我們可以選擇多種編碼系統來測試不同的線性假設集。 這些編碼設計用於回歸問題時具有特定的行為。換句話說,如果希望回歸系數具有某些特定屬性,則可以使用對比編碼。
Sum Contrast該編碼系統將給定水平(類別)的因變量的平均值與因變量的總體平均值進行比較。通過SumEncoder創建對比度矩陣,在下面的示例中,第一個Education_0將 PhD與所有教育水平進行比較,第二個Education_1將 HighSchool與所有教育水平進行比較,第三個Eductaion_2將 Bachelor與所有教育水平進行比較。編碼是通過將 1 分配給類別PhD進行第一次比較(因為類別PhD是要與所有其他類別進行比較的類別),將 1 分配給類別HighSchool進行第二次比較(因為類別 HighSchool 將與所有其他類別進行比較),1 分配給類別Bachelor進行第三次比較(因為 Bachelor將與所有其他類別進行比較)。 請注意,所有三個比較都將 -1 分配給類別Master(因為它是從未與其他類別進行比較的類別),並且所有其他值都分配給 0。
level of Education | Eductaion_0(PhD vs. mean) | Eductaion_1(HighSchool vs. mean) | Eductaion_2(Bachelor vs. mean) |
---|---|---|---|
PhD | 1 | 0 | 0 |
HighSchool | 0 | 1 | 0 |
Bachelor | 0 | 0 | 1 |
Master | -1 | -1 | -1 |
ce_7 = ce.SumEncoder(cols=['Education']).fit_transform(data)
print('\nSumEncoder Return the transformed dataset:\n', ce_7)
lr = ols('Income ~ Education_0 + Education_1 + Education_2',data=ce_7).fit()
print(lr.summary())
通過一個回歸問題:考慮教育水平與收入的相關性,來查看這種編碼方式的特別效果:回歸系數具有零和。構建Education的對比度(SumEncoder編碼結果)與Income的線性回歸方程。
可以計算Education各類別下因變量Income的均值,即分組求平均值:Bachelor組的Income均值7350,HighSchool組的Income均值4350,Master組Income均值13750,PhD組Income均值26650,4個類別的因變量Income平均值的平均值(后面我們稱之為總平均值)即\((7350+4350+13750+26650)/4=13025\)。
查看這個線性回歸結果:截距等於總平均值13025,其余系數(對比度估計)是當前組的Income均值減去總平均值,如Education_1(對應HightSchool VS mean)的系數:4350-13025=-8675。這個值與零(對比度系數為零的零假設)之間的差異具有統計顯着性 (p = 0.001),並且此檢驗的 t 值為 -9.628。 接下來兩個對比的結果以類似的方式計算。
from statsmodels.formula.api import ols
lr = ols('Income ~ Education_0 + Education_1 + Education_2',data=ce_7).fit()
print(lr.summary())
8.Backward Difference Contrast
在此編碼系統中,將離散變量的一個類別的因變量的平均值與先前相鄰類別的因變量的平均值進行比較。 在下面的示例中,第一個比較比較了 教育水平為HighSchool的收入Income平均值和 PhD的收入平均值。 第二個比較比較Bachelor與HighSchool的的收入平均值,第三個比較Master與Bachelor的收入平均值。這種類型的編碼可能對名義變量(nominal variable,例如性別、民族、專業、國籍等)或有序變量(ordinal variable,例如學生年級、滿意程度、學歷水平等)有用。Backward Difference Coding方法,k 是類別變量的類別數(在本例中,k = 4):
level of variable | level 2 vs. level 1 | level 3 vs. level 2 | level 4 vs. level 3 |
---|---|---|---|
1 | -(k-1)/k | -(k-2)/k | -(k-3)/k |
2 | 1/k | -(k-2)/k | -(k-3)/k |
3 | 1/k | 2/k | -(k-3)/k |
4 | 1/k | 2/k | 3/k |
對於第一個比較,比較PhD和HighSchool,Eductaion_0 為PhD編碼 -3/4,而其他類別編碼為 1/4。 對於第二次比較,將HighSchool與Bachelor進行比較,Eductaion_1編碼為 -1/2 -1/2 1/2 1/2,對於第三次比較,將Bachelor與Master進行比較,Eductaion_2編碼為 -1/4 -1/4 -1/4 3/4。
level of Education | Eductaion_0(HighSchool vs. PhD) | Eductaion_1(Bachelor vs. HighSchool) | Eductaion_2(Master vs. Bachelor) |
---|---|---|---|
PhD | -3/4 | -1/2 | -1/4 |
HighSchool | 1/4 | -1/2 | -1/4 |
Bachelor | 1/4 | 1/2 | -1/4 |
Master | 1/4 | 1/2 | 3/4 |
ce_8 = ce.BackwardDifferenceEncoder(cols=['Education']).fit_transform(data)
print('\nBackwardDifferenceEncoder Return the transformed dataset:\n', ce_8)
lr = ols('Income ~ Education_0 + Education_1 + Education_2',data=ce_8).fit()
print(lr.summary())
使用這種編碼系統,將類別變量的相鄰類別進行比較,每個類別都與前一個類別進行比較。 因此,將類別HighSchool的因變量的平均值與類別PhD的因變量的平均值進行比較:4350 – 26650 = -22300,具有統計顯着性。 對於Bachelor和HighSchool的比較,對比系數的計算為7350 - 4350=3000,不具有統計學意義(P=0.111),這意味着Bachelor的收入平均值與 HighSchool的收入平均值之間沒有明顯差異。 最后,比較 Master和 Bachelor,13750 –7350 = 6400,具有統計學意義的差異(P=0.012)。 從中可以得出結論,有2個相鄰的教育水平在統計上存在顯着差異。
9.Helmert Contrast
HelmertEncoder與BackwardDifferenceEncoder非常相似,但是不僅將其與上一個進行比較,還將每個類別與所有先前的類別進行比較。這種類型的編碼系統對於諸如種族、性別、國籍之類的名義變量(nominal variable)沒有多大意義。
Helmert 編碼(也稱為差異編碼):不僅將類別變量的每個類別與前一個類別的平均值進行比較,還將每個類別與所有先前類別的平均值進行比較。在我們的示例中,第一個對比編碼將教育水平HighSchool的因變量Income的平均值與PhD 的因變量Income的平均值進行比較。第二個比較將Bachelor因變量Income的平均值與HighSchool和PhD 進行比較,第三個比較將Master的因變量Income的平均值與類別 PhD、HighSchool 和 Bachelor 進行比較。
Helmert 編碼如下所示。對於第一個比較,比較PhD和HighSchool,Education_0 被編碼為 -1/2 和 1/2,其他為 0。對於第二次比較,Education_1編碼為 -1/3 -1/3 2/3 和 0。最后,對於第三次比較,Education_2編碼為 -1/4 -1/4 -/14 和 3/4。 Rcategory_encoders中內置的 HelmertEncoder等效於這種編碼方案,每列中都有一個常數。
level of Education | Eductaion_0(HighSchool vs. PhD) | Eductaion_1(Bachelor vs. HighSchool,PhD) | Eductaion_2(Master vs. Bachelor,HighSchool,PhD) |
---|---|---|---|
PhD | -1/2 | -1/3 | -1/4 |
HighSchool | 1/2 | -1/3 | -1/4 |
Bachelor | 0 | 2/3 | -1/4 |
Master | 0 | 0 | 3/4 |
ce_9 = ce.HelmertEncoder(cols=['Education']).fit_transform(data)
print('\nHelmertEncoder Return the transformed dataset:\n', ce_9)
lr = ols('Income ~ Education_0 + Education_1 + Education_2',data=ce_9).fit()
print(lr.summary())
需要注意的是使用HelmertEncoder要求觀測樣本數不少於8個,否則會出現”ValueWarning: omni_normtest is not valid with less than 8 observations; 6 samples were given.“這樣的提示。
查看回歸結果:截距等於總平均值13025,此輸出中顯示的第一個比較的對比估計值是通過從類別變量Education的類別HighSchool 的因變量的平均值中減去類別變量Education的類別 PhD的因變量Income的平均值,再求平均值計算得出的:(4350-26650)/2=-11150,這個結果在統計上是顯著的。第二次比較的對比估計值是通過從Bachelor的平均值中減去類別 PhD 和 HighSchool 的因變量的平均值,再求平均值計算得出的:[7350 – [(4350 + 26650) / 2] ]/3= -2716.6667,該結果具有統計學意義。對於類別Master 和之前類別的比較,先取這些類別的因變量的平均值,然后從類別Master的因變量的平均值中減去它,再求平均值:[13750 – [(7350+ 44350 + 26650) / 3] ]/4= 241.6667,這個結果在統計上是不顯著的。
10.Polynomial Contrast
多項式編碼是趨勢分析的一種形式,因為它正在尋找分類變量中的線性、二次和三次趨勢。 這種類型的編碼系統只能與類別等間距的序數變量一起使用。基於假設: 基礎分類變量具有不僅可觀的而且均等間隔的級別。如果你考慮連續變量與離散變量是否具有線性(或二次、三次)關系,可以謹慎使用它。
這種變量的例子可能是收入或教育。 因為教育水平是有序的,從HighSchool、Bachelor、Master到PhD,通過序號編碼,可以分別設置為1,2,3,4。下面是多項式編碼Education的對比度矩陣:Education_0(Linear),Education_1(Quadratic),Education_2(Cubic)。
level of Education | Eductaion_0(Linear) | Eductaion_1(Quadratic) | Eductaion_2(Cubic) |
---|---|---|---|
PhD | -0.670820 | 0.5 | -0.223607 |
HighSchool | -0.223607 | -0.5 | 0.670820 |
Bachelor | 0.223607 | -0.5 | -0.670820 |
Master | 0.670820 | 0.5 | 0.223607 |
ce_10 = ce.PolynomialEncoder(cols=['Education']).fit_transform(ce_1)
print('\nPolynomialEncoder Return the transformed dataset:\n', ce_10)
lr = ols('Income ~ Education_0 + Education_1 + Education_2',data=ce_10).fit()
print(lr.summary())
回歸結果表明 Education 對結果變量Income 有很強的線性影響(P=0.002),也有顯著的二次效應或三次效應。
有監督方法:
11.TargetEncoder
在貝葉斯架構下,利用要預測的因變量(target variable),有監督地確定最適合這個類別特征的編碼方式。有監督的編碼方式,適用於分類和回歸問題。
假設有2個變量,一個類別變量(x,有k個類別)一個目標變量/因變量(y),將類別變量編碼時希望使用y的信息。一個想法是對x的每個類別取y的平均值,即
這個方法的問題是對x分組時,某些組可能太小或太大,不太穩定。所以有監督的編碼通過選擇y的組均值和全局均值之間的中間方法來解決問題,即:
其中權重\(w_i\)取值在0和1之間。三種有監督的編碼算法(TargetEncoder,MEstimateEncoder,JamesSteinEncoder)基於\(w_i\)的定義不同而不同。
TargetEncoder的基本思想:充分利用已知數據,估算先驗概率和后驗概率,引入權重\(\lambda\)計算編碼所用概率\(\hat{P}\).將類別變量x中的每一個類別\(i\)都表示為它所對應某個目標y值target概率,即后驗概率:\(\hat{P}(y=\text{target}|x=i)\);基於已有數據可估算數據點為某個目標y值target的概率,即先驗概率:\(\hat{P}(y=\text{target})\)。最終編碼\(\hat{P}=\lambda * \hat{P}(y=\text{target}|x=i) + (1-\lambda)*\hat{P}(y=\text{target})\)或\(\hat{P}=\lambda * \text{posterior} + (1-\lambda)*\text{prior}\)。
\(\hat{P}(y=\text{target})=N_{y=\text{target}}/N_{\text{total}}\)
\(\hat{P}(y=\text{target}|x=i)=N_{y=\text{target} \;\text{and}\; x=i}/N_{x=i}\)
定義權重函數,輸入是特征類別在訓練集中出現的次數n,輸出是對於這個特征類別的先驗概率的權重\(\lambda\)。假設一個特征類別出現次數是n,則:
TargetEncoder計算公式:
如果\(y\)是連續值,則將各個樣本量N換成相應的均值即可。
分類特征的目標編碼。
分類特征的目標編碼。
支持的目標變量類型:二項式和連續式。
對於分類目標的情況:特征被替換為給定特定分類值的目標后驗概率和目標在所有訓練數據上的先驗概率的混合。
對於連續目標的情況:特征被替換為給定特定分類值的目標期望值和目標在所有訓練數據上的期望值的混合。
Income_grand_mean = data['Income'].mean()
data['Income_grand_mean'] = [Income_grand_mean]*len(data)
Income_group = data.groupby('Education')['Income'].mean().rename('Income_level_mean').reset_index()
data_new = pd.merge(data, Income_group)
# data_new.sort_values(by=['ID'],inplace=True)
# data_new.reset_index(drop=True, inplace=True)
print('New Dataset:\n', data_new)
features = list(data_new.columns)
features.remove('Income')
ce_11_1 = ce.TargetEncoder(cols=['Education'], smoothing=0).fit_transform(data_new[features], data_new['Income'])
print('\nTargetEncoder Return the transformed dataset(smoothing=0):\n', ce_11_1)
ce_11_2 = ce.TargetEncoder(cols=['Education'], smoothing=1).fit_transform(data_new[features], data_new['Income'])
print('\nTargetEncoder Return the transformed dataset(smoothing=1):\n', ce_11_2)
ce_11_3 = ce.TargetEncoder(cols=['Education'], smoothing=2).fit_transform(data_new[features], data_new['Income'])
print('\nTargetEncoder Return the transformed dataset(smoothing=2):\n', ce_11_3)
計算數據集因變量Income的總平均值,以及分組平均值,如下所示。
查看TargetEncoder設置不同的平滑參數(smothing=0,1,2)下類別變量Education的編碼表示。平滑為0(smothing=0)時,僅用組均值。隨着平滑的增加,全局平均權重越來越大,從而導致更強的正則化。
12.MEstimateEncoder
MEstimateEncoder支持的目標類型:二項式和連續式。
這是目標編碼器的簡化版本,其名稱為 m 概率估計(m-probability estimate)或已知發生率的加性平滑(additive smoothing with known incidence rates)。 與目標編碼器相比,m 概率估計只有一個可調參數(m),而目標編碼器有兩個可調參數(min_samples_leaf 和smoothing)。該參數設置全局平均值應按絕對值加權的大小。
它是TargetEncoder的一個特例(n是特征類別在訓練集中出現的次數,即這個分類特征\(x\)的某一特征取值\(x_i\)下的所有樣本),\(p_c\)是指先驗概率,\(n_c\)是屬於目標y=target且分類特征取值為c的樣本量:
MEstimateEncoder計算公式:
如果\(y\)是連續值,則將各個樣本量N換成相應的均值即可。\(m\)越大,過擬合程度越小。
ce_12_1 = ce.MEstimateEncoder(cols=['Education'], random_state=10, m=0).fit_transform(data_new[features], data_new['Income'])
print('\nMEstimateEncoder Return the transformed dataset(m=0):\n', ce_12_1)
ce_12_2 = ce.MEstimateEncoder(cols=['Education'], random_state=10, m=1).fit_transform(data_new[features], data_new['Income'])
print('\nMEstimateEncoder Return the transformed dataset(m=1):\n', ce_12_2)
ce_12_3 = ce.MEstimateEncoder(cols=['Education'], random_state=10, m=2).fit_transform(data_new[features], data_new['Income'])
print('\nMEstimateEncoder Return the transformed dataset(m=2):\n', ce_12_3)
13.JamesSteinEncoder
詹姆斯-斯坦估計器。
支持的目標類型:二項式和連續式。
對於特征值 i,James-Stein 估計器返回以下加權平均值:
- 觀察到的特征值 i 的平均目標值。
- 平均目標值(與特征值無關)。
可以表示為:\(JS_i=(1-B)*mean(y_i)+B*mean(y)\)
問題是,權重 B 應該是多少? 如果我們過分強調條件均值,我們就會過擬合。 如果我們過分強調全局均值,我們就會欠擬合。 機器學習中的典型解決方案是執行交叉驗證。 然而,Charles Stein 為這個問題提供了一個封閉形式的解決方案。 一個直覺是:如果對\(mean(y_i)\)的估計不可靠(\(y_i\) 具有高方差),我們應該對全局均值\(mean(y)\)賦予更多的權重。 Stein 將其代入方程為:\(B = var(y_i) / (var(y_i)+var(y))\).
剩下的唯一問題是我們不知道\(var(y)\),更不用說 \(var(y_i)\)。 因此,我們必須估計方差。 但是,當我們已經在努力估計平均值時,我們如何才能可靠地估計方差呢? 有多種解決方案:
- 如果我們對每個特征值 i 有相同的觀察計數並且所有 $y_i $彼此接近,我們可以假設所有 \(var(y_i)\) 都是相同的。 這稱為池化模型。
- 如果觀察計數不相等,用平方標准誤差代替方差是有意義的,這會懲罰小的觀察計數:\(SE^2 = var(y)/count(y)\).這稱為獨立模型。
然而,James-Stein 估計器有一個實際限制 - 它僅針對正態分布定義。 如果要將其應用於僅允許值 {0, 1} 的二分類,最好先將平均目標值從有界區間 <0,1> 轉換為無界區間,方法是將 \(mean(y)\) 替換為對數優勢比:\(\text{log-odds\;ratio}_i = \log(mean(y_i)/mean(y_{\text{not}_i}))\).這稱為二元模型。 然而,這個模型的參數估計很棘手,有時它會導致致命的失敗。 在這些情況下,最好使用 beta 模型,它的准確度通常比二元模型稍差,但不會出現致命失敗。
TargetEncoder和MEstimateEncoder既取決於組計數,也取決於用戶設置的參數值(分別是平滑smoothing和m)。這不方便,因為設置這些權重是一項手動任務。
這就引申出一個自然的問題:是否有一種方法可以在不需要任何人工干預的情況下設置最佳權重?JamesSteinEncoder嘗試以統計為基礎的方式執行此操作。一個直覺是,具有較高方差的組均值應被較少信任。因此,組方差越高,權重越低。
JamesSteinEncoder具有兩個顯着優點:與最大似然估計器相比,它提供了更好的估計,並且不需要任何參數設置。
ce_13 = ce.JamesSteinEncoder(cols=['Education'],).fit_transform(data_new[features], data_new['Income'])
print('\nJamesSteinEncoder Return the transformed dataset:\n', ce_13)
14.Generalized Linear Mixed Model Encoder
廣義線性混合模型。
支持的目標類型:二項式和連續式。
這是一個類似於 TargetEncoder 或 MEstimateEncoder 的監督編碼器,但有一些優點:1)該技術背后有扎實的統計理論。混合效應模型是統計學的一個成熟分支。 2) 沒有要調整的超參數。收縮量是通過估計過程自動確定的。簡而言之,一個類別的觀察數越少,(和/或)一個類別的結果變化越大,那么對“先驗”或“總體均值(grand mean)”的正則化就越高。 3) 該技術適用於連續目標和二項式目標。如果目標是連續的,則編碼器返回觀測類別與全局平均值的正則化差異。如果目標是二項式,則編碼器返回每個類別的正則化對數賠率(log odds)。
與 JamesSteinEstimator 相比,此編碼器利用 statsmodels 庫中的廣義線性混合模型。
這種方法利用了以下事實:線性混合效應模型是專為處理同類觀察組而設計的。因此,該想法是使模型不具有回歸變量(僅包含截距),並將類別用作組。就是截距和組的隨機效應之和。
ce_14 = ce.GLMMEncoder(cols=['Education']).fit_transform(data_new[features], data_new['Income'])
print('\nGeneralized Linear Mixed Model Encoder Return the transformed dataset:\n', ce_14)
15.WOEEncoder
WOE(Wieght of Evidence)Encoder只能用於二進制目標變量,即0/1的目標變量。
WOE describes the relationship between a predictive variable and a binary target variable.
WOE(Weight of Evidence)叫做證據權重,那么WOE在業務中常有哪些應用呢?
- 處理缺失值:當數據源沒有100%覆蓋時,那就會存在缺失值,此時可以把null單獨作為一個分箱。這點在分數據源建模時非常有用,可以有效將覆蓋率哪怕只有20%的數據源利用起來。
- 處理異常值:當數據中存在離群點時,可以把其通過分箱離散化處理,從而提高變量的魯棒性(抗干擾能力)。例如,age若出現200這種異常值,可分入“age > 60”這個分箱里,排除影響。
- 業務解釋性:我們習慣於線性判斷變量的作用,當x越來越大,y就越來越大。但實際x與y之間經常存在着非線性關系,此時可經過WOE變換。
計算WOE步驟:
- 對於連續型變量,進行分箱(binning),可以選擇等頻、等距,或者自定義間隔;對於離散型變量,如果分箱太多,則進行分箱合並。
- 統計每個分箱里的好人數(bin_goods)和壞人數(bin_bads)。
- 分別除以總的好人數(total_goods)和壞人數(total_bads),得到每個分箱內的邊際好人占比(margin_good_rate)和邊際壞人占比(margin_bad_rate)。
- 計算每個分箱里的 [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-JXrIQFuC-1642905395291)(https://www.zhihu.com/equation?tex=WOE+%3D+ln(\frac{margin\_badrate}{margin\_goodrate}))]
- 檢查每個分箱(除null分箱外)里woe值是否滿足單調性,若不滿足,返回step1。注意⚠️:null分箱由於有明確的業務解釋,因此不需要考慮滿足單調性。
- 計算每個分箱里的IV,最終求和,即得到最終的IV。
在WOEEncoder里:只有兩種分布:
- 1的分布(每組y=1的數量/所有y=1的數量)
- 0的分布(每組y=0的數量/所有y=0的數量)
算法核心:對每個分組,將1的分布除以0的分布;這個值越高,越有信心偏向該組的1,反之亦然。
WOEEncoder的計算公式如下,每個特征取值\(x_i\)表示為\(WoE_i\):
from sklearn.datasets import load_boston
bunch = load_boston()
y = bunch.target > 22.5
X = pd.DataFrame(bunch.data, columns=bunch.feature_names)
ce_15 = ce.WOEEncoder(cols=['CHAS', 'RAD']).fit_transform(X, y)
print('\nOriginal Dataset (Boston):\n', X)
print('\nWOEEncoder Return the transformed dataset:\n', ce_15)
16.Leave One Out Encoder
以上15個編碼器都具有唯一的映射。
Leave One Out Encoder與Target Encoder非常相似,但在計算一個類別的平均目標時會排除當前行的目標,以減少異常值的影響。
如果您打算將編碼用作預測模型的輸入(例如,梯度增強),則可能會出現問題。實際上,假設您使用TargetEncoder。這意味着您將在X_train中引入有關y_train的信息,這可能會導致嚴重的過度擬合風險。
關鍵是:如何在限制過度擬合風險的同時保持監督編碼?LeaveOneOutEncoder提供了一個出色的解決方案。它執行原始目標編碼,但是對於每一行,它不考慮對該行觀察到的y值。這樣,避免了行泄漏。
LeaveOneOutEncoder計算公式:
ce_16 = ce.LeaveOneOutEncoder(cols=['Education']).fit_transform(data_new[features], data_new['Income'])
print('\nLeaveOneOutEncoder Return the transformed dataset:\n', ce_16)
y_level表示的是每個Education類別包含的所有y值。如ID為1的Education類別為PhD被編碼為25000,是這樣做的:原始對應的目標變量Income的值(y)為28300,該類別下所有的y值有2個(28300, 25000),排除當前行的y值28300,對剩下的y值求平均值。
17.CatBoost Encoder
CatBoostEncoder支持的目標類型:二項式和連續式。
這與留一法編碼非常相似,但會“即時”計算值。 因此,這些值在訓練階段自然會發生變化,並且沒有必要添加隨機噪聲。
請注意,訓練數據必須隨機排列。 例如。:
# 隨機排列
perm = np.random.permutation(len(X))
X = X.iloc[perm].reset_index(drop=True)
y = y.iloc[perm].reset_index(drop=True)
這是必要的,因為某些數據集是根據目標值排序的,並且此編碼器在單次傳遞中即時對特征進行編碼。
CatBoost是一種梯度增強算法(例如XGBoost或LightGBM),在各種問題上都表現得非常出色。
CatboostEncoder的工作原理基本上與LeaveOneOutEncoder相似,但是遵循一種在線方法。
但是,如何在離線設置中模擬在線行為?假設您有一張桌子。然后,在桌子中間的某處划一排。CatBoost的行為是假裝當前行上方的行先前已被及時觀察到,而下方行尚未被觀察到(即將來會被觀察到)。然后,該算法僅根據已經觀察到的行進行留一法編碼。
ce_17 = ce.CatBoostEncoder(cols=['Education']).fit_transform(data_new, data_new['Income'])
print('\nCatBoostEncoder Return the transformed dataset:\n', ce_17)
y_level_before表示的是每個Education類別的當前行上方的所有y值。如ID為1的Education類別為PhD被編碼為13025,是這樣做的:原始對應的目標變量Income的值(y)為28300,該行上方的行為空即[],編碼為y的全局平均值13025;ID為2的Education類別為PhD被編碼為20662.5,是這樣計算的:改行上方行包含的y值列表為[28300],編碼為(13025+28300)/2=20662.5,即y的全局平均值和當前行的上方行的所有y值的平均值。
實驗效果分析
利用Boston的房價預測數據,使用以上的方法來處理2個離散特征(CHAS, RAD),看看GradientBoostingRegressor回歸模型的擬合效果如何?
首先是不使用交叉驗證,對離散特征編碼,再擬合得到的模型\(R^2\):
使用交叉驗證后,對離散特征編碼,再擬合得到的模型\(R^2\):
可見在這個回歸問題上各種編碼方法得到的效果差異不大。
總結
關於這17個編碼方法的使用建議:
-
OrdinalEncode比較直觀,最適用於類別特征的類別之間存在順序的相關性;如果沒有大小關系,如男、女,貿然使用會增加噪聲,即隨機增加不存在的順序關系。
-
對於類別特征的類別數過多的情況不宜使用OneHot Encoder,因為會導致生成特征的數量太多且過於稀疏。
-
SumEncoder比較某一類別特征取值\(x_i(i=1,...,k)\)下對應目標\(y\)的均值與目標\(y\)的全局平均值之間的差別來對特征進行編碼。因為這個編碼導致這個類別特征攜帶過多的目標變量信息(\(y\)),容易過擬合,需要配合留一法或者交叉驗證進行。
-
HelmetEncoder與SumEncoder不同的是,比較某一類別特征取值\(x_i(i=1,...,k)\)下對應目標\(y\)的均值與它之前特征取值\(x_j(j=1,2,..,i-1)\)下的均值之間的差別,不是和所有特征的均值比較。這個方法也容易出現過擬合。
-
TargetEncoder有監督地選擇某一類別特征取值\(x_i(i=1,...,k)\)下對應目標\(y\)的均值與目標\(y\)的全局平均值,結合已知數據同時考慮先驗概率和后驗概率,可以忽略每一類別特征取值下的樣本數量大小。這個方法同樣會引起過擬合,防止過擬合可以考慮:使用交叉驗證、訓練集中加入噪聲、調整正則項大小等。
-
MEstimateEncoder是TargetEncoder的一個特例,對權重函數做了簡化,只需要調整一個參數\(m\)即可。\(m\)越大過擬合程度越小。
-
Jame-Stein Encoder也是基於TargetEncoder的,不過有一個實際限制:目標\(y\)必須符合正態分布。不同的是權重函數考慮了方差估計。
-
Leave-One-Out Encoder考慮消除過擬合,即在計算每個特征取值\(x_i\)的編碼時把該樣本剔除。
-
對於有序離散特征,可以嘗試使用OrdinalEncoder,BinaryEncoder,OneHotEncoder,LeaveOneOutEncoder,TargetEncoder。
-
HelmetEncoder、SumEncoder、BackwarDifferenceEncoder、PloynominalEncoder不輕易使用,一是因為容易過擬合,二是適配的場景要求嚴苛(如果是很明確的業務理解)。
-
對於回歸問題,TargetEncoder和LeaveOneOutEncoder效果可能不太好,可能是因為離散變量編碼后過多攜帶了因變量的信息。
-
對於離散特征包含的類別數很高的情況,LeaveOneOutEncoder、WOEEncoder、James-SteinEncoder、MEstimateEncoder會更適合。
-
WOEEncoder只能用於二分類問題。
Reference
[1] Beyond One-Hot. 17 Ways of Transforming Categorical Features Into Numeric Features, https://towardsdatascience.com/beyond-one-hot-17-ways-of-transforming-categorical-features-into-numeric-features-57f54f199ea4
[2] https://stats.oarc.ucla.edu/r/library/r-library-contrast-coding-systems-for-categorical-variables/
[3] http://www.ultravioletanalytics.com/blog/using-category-encoders-library-in-scikit-learn
[4] https://www.yuque.com/hshtpy/mlblog/kaggle
[5] https://zhuanlan.zhihu.com/p/441258095
[6] https://copyfuture.com/blogs-details/20200815213500577wghtmumgery2gi9
[7] https://bigdata.51cto.com/art/202012/637918.htm
[8] https://zhuanlan.zhihu.com/p/361140784
[9] https://zhuanlan.zhihu.com/p/80134853
[10] https://github.com/aksenov7/categorical-encoding
[11] https://github.com/scikit-learn-contrib/category_encoders