簡介
特征離散化指的是將連續特征劃分離散的過程:將原始定量特征的一個區間一一映射到單一的值。離散化過程也被表述成分箱(Binning)的過程。特征離散化常應用于邏輯回歸和金融領域的評分卡中,同時在規則提取,特征分類中也有對應的應用價值。本文主要介紹幾種常見的分箱方法,包括等寬分箱、等頻分箱、信息熵分箱、基于決策樹分箱、卡方分箱等。
分箱原理介紹
數據分箱是一種數據預處理技術,用于減少次要觀察誤差的影響,是一種將多個連續值分為相對較少的若干分組的方法。下面舉個例子來具體說明一下,假設我們有下面一組數據,表示花瓣的寬度,分布如下:- 有助于模型部署和應用,加快模型迭代
- 增強模型魯棒性
- 增加非線性表達能力:連續特征不同區間對模型貢獻或者重要程度不一樣時,分箱后不同的權重能直接體現這種差異,離散化后的特征再進行特征 交叉衍生能力會進一步加強。
- 提升模型的泛化能力
- 擴展數據在不同各類型算法中的應用范圍
當然特征離散化也有其缺點,總結如下:
- 分箱操作必定會導致一定程度的信息損失
- 增加流程:建模過程中加入了額外的的離散化步驟
- 影響模型穩定性: 當一個特征值處于分箱點的邊緣時,此時微小的偏差會造成該特征值的歸屬從一箱躍遷到另外一箱,影響模型的穩定性。
特征離散化(分箱)可以從不同的角度來進行劃分。當分箱方法使用了目標y的信息,那么該分箱方法就屬于有監督的分箱方法,反之為無監督的分箱方法。
這里選擇sklearn中自帶的乳腺癌數據集,下文提及的分箱方法大多是基于此數據集,主要是取其中的‘mean radius’字段,如下:
import seaborn as sns
from sklearn.datasets import load_breast_cancer
bc = load_breast_cancer()
df = pd.DataFrame.from_records(data=bc.data, columns=bc.feature_names)
df['target'] = bc.target
sns.distplot(df['mean radius'], kde=False)
等寬分箱(Equal-Width Binning)
等寬分箱指的是每個分隔點或者劃分點的距離一樣,即等寬。實踐中一般指定分隔的箱數,等分計算后得到每個分隔點。例如將數據序列分為n份,則 分隔點的寬度計算公式為:
這樣就將原始數據劃分成了n個等寬的子區間,一般情況下,分箱后每個箱內的樣本數量是不一致的。使用pandas中的cut函數來實現等寬分箱,代碼如下:
value, cutoff = pd.cut(df['mean radius'], bins=4, retbins=True, precision=2)
cutoff
可以輕易計算得出任意兩個相鄰分隔點之間的距離為5.30。按照上述分隔點對數據進行劃分后,數據的分布如下:
df1 = value.to_frame()
df1.columns = ['bins']
sns.countplot(df1['bins'])
等寬分箱計算簡單,但是當數值方差較大時,即數據離散程度很大,那么很可能出現沒有任何數據的分箱,這個問題可以通過自適應數據分布的分箱方法--等頻分箱來避免
等頻分箱(Equal-Frequency Binning)
顧名思義,等頻分箱理論上分隔后的每個箱內得到數據量大小一致,但是當某個值出現次數較多時,會出現等分邊界是同一個值,導致同一數值分到不同的箱內,這是不正確的。具體的實現可以去除分界處的重復值,但這也導致每箱的數量不一致。如下代碼:
s1 = pd.Series([1,2,3,4,5,6])
value, cutoff = pd.qcut(s1, 3, retbins=True)
sns.countplot(value)
每個區間分別是2個數,這沒有問題,但是如果某個數字出現的次數較多,則可能出現下面的情況:
s1 = pd.Series([1,2,3,4,5,6,6,6,6])
value, cutoff = pd.qcut(s1, 3, duplicates='drop', retbins=True)
sns.countplot(value)
本來是要分成3個箱子的,但是由于出現同一個數值被分到了不同的箱子里,因此被合并了,所以最后只有2個箱子。
同樣,我們對乳腺癌數據進行等頻分箱,該數據分布正常,等頻分箱后每箱數量基本一致。
value, cutoff = pd.qcut(df['mean radius'], 4, duplicates='drop', retbins=True)
sns.countplot(value)
上述的等寬和等頻分箱容易出現的問題是每箱中信息量變化不大。例如,等寬分箱不太適合分布不均勻的數據集、離群值;等頻方法不太適合特定的值占比過多的數據集,如長尾分布。
信息熵分箱
上面介紹的分箱方法對建模的優化有限。如果分箱后箱內樣本對y的區分度好,那么這是一個好的分箱。通過信息論理論,我們可知信息熵衡量了這種區分能力。當特征按照某個分隔點劃分為上下兩部分后能達到最大的信息增益,那么這就是一個好的分隔點。由上可知,信息熵分箱是有監督的分箱方法。其實決策樹的節點分裂原理也是基于信息熵。
首先我們需要明確信息熵和信息增益的計算方式,分別如下:
在二分類問題中,。
信息增益的物理含義表達為:x的分隔帶來的信息對y的不確定性帶來的增益。
對于二值化的單點分隔,如果我們找到一個分隔點將數據一分為二,分成和
兩部分,那么劃分后的信息熵的計算方式為:
下面以一個實例來介紹一下信息熵分箱的計算過程,假如我們有以下數據,我們以特征'x=3'來作為特征x的分隔點,則其帶來的信息增益為0.420,如下:
data = [[1,0],[2,1],[3,0],[4,1],[5,1]]
df = pd.DataFrame(data, columns=['x','y'])
df
接下來計算劃分后的信息熵:
故信息增益為:
類似的可以計算其他分隔點的信息增益,最終選取信息增益最大時對應的分隔點。同時也可以看出,當分箱后,某個箱中的標簽y的類別(0或者1)的比例相等時,其熵值最大,表明此特征劃分幾乎沒有區分度。而當某個箱中的數據的標簽y為單個類別時,那么該箱的熵值達到最小的0,即純度最純,最具區分度。從結果上來看,最大信息增益對應分箱后的總熵值最小。
決策樹分箱
其實上一節中已經說到,由于決策樹的結點選擇和劃分也是根據信息熵來計算的,因此我們其實可以利用決策樹算法來進行特征選擇,具體做法如下:
還是以乳腺癌數據為例,首先取其中‘mean radius’字段,和標簽字段‘target’來擬合一棵決策樹,代碼如下:
from sklearn.tree import DecisionTreeClassifier
dt = DecisionTreeClassifier(criterion='entropy', max_depth=3) # 樹最大深度為3
dt.fit(df['mean radius'].values.reshape(-1, 1), df['target'])
接著我們取出這課決策樹的所有葉節點的分割點的閾值,如下:
qts = dt.tree_.threshold[np.where(dt.tree_.children_left > -1)]
qts = np.sort(qts)
res = [np.round(x, 3) for x in qts.tolist()]
res
注意這里只給出了6個點,但是相當于分了7個箱子,分別設為a-g,我們可以將劃分后的效果繪制出來:
l = df['mean radius'].values.tolist()
r = []
for i in l:
if i < res[0]:
r.append('a')
elif i >= res[-1]:
r.append('g')
else:
for j in range(0, 5):
if i > res[j] and i <= res[j+1]:
r.append(chr(98+j))
break
ax = sns.countplot(r)
ax
卡方分箱
在了解卡方分箱之前,我們需要先了解幾個關鍵概念,比如卡方分布,卡方檢驗等。
卡方分布是概率統計常見的一種概率分布,是卡方檢驗的基礎。
卡方分布定義為:若n個獨立的隨機變量滿足標準正態分布
,則n個隨機變量的平方和
為服從自由度為k的卡方分布,記為
。參數n稱為自由度(樣本中獨立或能自由變化的自變量的個數),不同的自由度是不同的分布。例如,一個標準正態分布的平方就是自由度為1的卡方分布。卡方分布的概率密度函數如下:
- 卡方擬合 優度檢驗:用于檢驗樣本是否來自于某一個分布,比如檢驗某樣本是否為正態分布
- 獨立性卡方檢驗,查看兩組類別變量分布是否有差異或者相關,以列聯表的形式比較。以列聯表形式的卡方檢驗中,卡方統計量由上式給出。
其中,表示觀察到的頻數(即實際出現的次數),
表示期望的頻數。很明顯,越小的卡方值,表明兩者相差越小,反之,越大的卡方值表明兩者差異越大。下面以一個具體的例子來說明卡方值的計算方式。
下面是觀察到的兩組樣本分布:
觀察 | bad | good | 合計 |
---|---|---|---|
組一 | 25 | 50 | 75 |
組二 | 30 | 15 | 45 |
合計 | 55 | 65 | 120 |
接著是期望頻數的計算過程:
期望 | bad | good |
---|---|---|
組一 | 55*(75/120)=34.375 | 65*(75/120)=40.625 |
組二 | 55*(45/120)=20.625 | 65*(45/120)=24.375 |
最后是卡方值的計算過程:
a = [25, 50, 30, 15]
b = [34.375, 40.625, 20.625, 24.375]
r= []
for i in zip(a,b):
r.append((i[0]-i[1])**2 / i[1])
np.sum(r)
科學計算庫SciPy中包含了卡方檢驗的實現,例子如下:
from scipy.stats import chi2_contingency
obs = np.array([[25,50],[30,15]])
chi2, p, dof, ex = chi2_contingency(obs, correction=False)
chi2
其中dof代表自由度,ex代表期望概率。在得到卡方值以后查詢卡方分布表并比較p_value值,繼而做出接受或者拒絕原假設的判斷。下段代碼是輸出卡方分布表的實現:
from scipy.stats import chi2
def chi2_table(freedom=10, alpha=None):
if alpha is None:
alpha = [0.99, 0.95, 0.9, 0.5, 0.1, 0.05, 0.025, 0.01, 0.005]
df = pd.DataFrame([chi2.isf(alpha, df=i) for i in range(1, freedom)])
df.columns = alpha
df.index = df.index + 1
return df
chi2_table()
由表可知,當自由度為1、置信水平為0.05時,對應的卡方值為3.84,而上述例子計算出來的卡方值為12.587,大于3.841。說明在0.05的顯著性水平是可以拒絕原假設的,即觀察頻數與期望頻數有差異。換個角度描述卡方檢驗的物理含義:當兩個分箱中,好壞(正負)分布是一致時,卡方為0,相似是接近0,對應到卡方分箱算法中,應該合并這兩個分箱。
卡方分箱步驟
卡方分箱是自底向上的(即基于合并的)數據離散化方法。它依賴于卡方檢驗:具有最小卡方值的相鄰區間合并在一起,直到滿足確定的停止準則。基本思想: 對于精確的離散化,相對類頻率在一個區間內應當完全一致。因此,如果兩個相鄰的區間具有非常類似的類分布,則這兩個區間可以合并;否則,它們應當保持分開。而低卡方值表明它們具有相似的類分布。
卡方檢驗可以用來評估兩個分布的相似性,因此可以將這個特性用到數據分箱的過程中。