1 IV的用途
IV的全稱是Information Value,中文意思是信息價值,或者信息量。
我們在用邏輯回歸、決策樹等模型方法構建分類模型時,經常需要對自變量進行篩選。比如我們有200個候選自變量,通常情況下,不會直接把200個變量直接放到模型中去進行擬合訓練,而是會用一些方法,從這200個自變量中挑選一些出來,放進模型,形成入模變量列表。那么我們怎么去挑選入模變量呢?
挑選入模變量過程是個比較復雜的過程,需要考慮的因素很多,比如:變量的預測能力,變量之間的相關性,變量的簡單性(容易生成和使用),變量的強壯性(不容易被繞過),變量在業務上的可解釋性(被挑戰時可以解釋的通)等等。但是,其中最主要和最直接的衡量標准是變量的預測能力。
“變量的預測能力”這個說法很籠統,很主觀,非量化,在篩選變量的時候我們總不能說:“我覺得這個變量預測能力很強,所以他要進入模型”吧?我們需要一些具體的量化指標來衡量每自變量的預測能力,並根據這些量化指標的大小,來確定哪些變量進入模型。IV就是這樣一種指標,他可以用來衡量自變量的預測能力。類似的指標還有信息增益、基尼系數等等。
2 對IV的直觀理解
從直觀邏輯上大體可以這樣理解“用IV去衡量變量預測能力”這件事情:我們假設在一個分類問題中,目標變量的類別有兩類:Y1,Y2。對於一個待預測的個體A,要判斷A屬於Y1還是Y2,我們是需要一定的信息的,假設這個信息總量是I,而這些所需要的信息,就蘊含在所有的自變量C1,C2,C3,……,Cn中,那么,對於其中的一個變量Ci來說,其蘊含的信息越多,那么它對於判斷A屬於Y1還是Y2的貢獻就越大,Ci的信息價值就越大,Ci的IV就越大,它就越應該進入到入模變量列表中。
3 IV的計算
前面我們從感性角度和邏輯層面對IV進行了解釋和描述,那么回到數學層面,對於一個待評估變量,他的IV值究竟如何計算呢?為了介紹IV的計算方法,我們首先需要認識和理解另一個概念——WOE,因為IV的計算是以WOE為基礎的。
3.1 WOE
WOE的全稱是“Weight of Evidence”,即證據權重。WOE是對原始自變量的一種編碼形式。
要對一個變量進行WOE編碼,需要首先把這個變量進行分組處理(也叫離散化、分箱等等,說的都是一個意思)。分組后,對於第i組,WOE的計算公式如下:
WOE (weight of Evidence) 字面意思證據權重,對分箱后的每組進行計算。假設 good 為好客戶(未 違約),bad 為壞客戶(違約)。
#goodi 表示每組中標簽為 good 的數量,#goodT 為 good 的總數量,bad 同理。
3.2 IV
IV (information value) 衡量的是某一個變量的信息量,公式如下:
N 為分組的組數,IV 可用來表示一個變量的預測能力。
IV | 預測能力 |
<0.03 | 無預測能力 |
0.03-0.09 | 低 |
0.1-0.29 | 中 |
0.3-0.49 | 高 |
>=0.5 | 極高且可疑 |
可根據 IV 值來調整分箱結構並重新計算 WOE 和 IV。但並不完全是 IV 值越大越好,還需要考慮 分組數量合適,並且當 IV 值大於 0.5 時,我們需要對這個特征打個疑問,因為它過於太好而顯得不夠 真實。通常我們會選擇 IV 值在 0.1~0.5 這個范圍的特征。多數時候分箱都需要手動做一些調整。
python代碼如下:
import numpy as np import pandas as pd import scipy import scipy.stats as st def auto_bin(DF, X, Y, n=5, iv=True, detail=False,q=20): """ 自動最優分箱函數,基於卡方檢驗的分箱 參數: DF: DataFrame 數據框 X: 需要分箱的列名 Y: 分箱數據對應的標簽 Y 列名 n: 保留分箱個數 iv: 是否輸出執行過程中的 IV 值 detail: 是否輸出合並的細節信息 q: 初始分箱的個數 區間為前開后閉 (] 返回值: """ # DF = model_data # X = "age" # Y = "SeriousDlqin2yrs" DF = DF[[X,Y]].copy() # 按照等頻對需要分箱的列進行分箱 DF["qcut"],bins = pd.qcut(DF[X], retbins=True, q=q, duplicates="drop") # 統計每個分段 0,1的數量 coount_y0 = DF.loc[DF[Y]==0].groupby(by="qcut")[Y].count() coount_y1 = DF.loc[DF[Y]==1].groupby(by="qcut")[Y].count() # num_bins值分別為每個區間的上界,下界,0的頻次,1的頻次 num_bins = [*zip(bins,bins[1:],coount_y0,coount_y1)] # 定義計算 woe 的函數 def get_woe(num_bins): # 通過 num_bins 數據計算 woe columns = ["min","max","count_0","count_1"] df = pd.DataFrame(num_bins,columns=columns) df["total"] = df.count_0 + df.count_1 df["percentage"] = df.total / df.total.sum() df["bad_rate"] = df.count_1 / df.total df["woe"] = np.log((df.count_0/df.count_0.sum()) / (df.count_1/df.count_1.sum())) return df # 創建計算 IV 值函數 def get_iv(bins_df): rate = ((bins_df.count_0/bins_df.count_0.sum()) - (bins_df.count_1/bins_df.count_1.sum())) IV = np.sum(rate * bins_df.woe) return IV # 確保每個分組的數據都包含有 0 和 1 for i in range(20): # 初始分組不會超過20 # 如果是第一個組沒有 0 或 1,向后合並 if 0 in num_bins[0][2:]: num_bins[0:2] = [( num_bins[0][0], num_bins[1][1], num_bins[0][2]+num_bins[1][2], num_bins[0][3]+num_bins[1][3])] continue # 其他組出現沒有 0 或 1,向前合並 for i in range(len(num_bins)): if 0 in num_bins[i][2:]: num_bins[i-1:i+1] = [( num_bins[i-1][0], num_bins[i][1], num_bins[i-1][2]+num_bins[i][2], num_bins[i-1][3]+num_bins[i][3])] break # 循環結束都沒有出現則提前結束外圈循環 else: break # 重復執行循環至分箱保留 n 組: while len(num_bins) > n: # 獲取 num_bins 兩兩之間的卡方檢驗的置信度(或卡方值) pvs = [] for i in range(len(num_bins)-1): x1 = num_bins[i][2:] x2 = num_bins[i+1][2:] # 0 返回 chi2 值,1 返回 p 值。 pv = st.chi2_contingency([x1,x2])[1] # chi2 = scipy.stats.chi2_contingency([x1,x2])[0] pvs.append(pv) # 通過 p 值進行處理。合並 p 值最大的兩組 i = pvs.index(max(pvs)) num_bins[i:i+2] = [( num_bins[i][0], num_bins[i+1][1], num_bins[i][2]+num_bins[i+1][2], num_bins[i][3]+num_bins[i+1][3])] # 打印合並后的分箱信息 bins_df = get_woe(num_bins) if iv: print(f"{X} 分{len(num_bins):2}組 IV 值: ",get_iv(bins_df)) if detail: print(bins_df) # print("\n".join(map(lambda x:f"{x:.16f}",pvs))) # 返回分組后的信息 return get_woe(num_bins) #, get_iv(bins_df)