一、問題描述:

在實際建模問題中,衍生加工許多特徵變數之後,一般而言,首先需要對衍生變數的預測能力做一個快速、初步的評估。針對二分類問題,如信貸風險模型的好壞客戶預測與評分,我們一般用IV值(Information Value)來衡量特徵變數的預測能力,然後再篩選出IV值高於某個閾值的一籃子特徵來進行下一步的建模工作。

為了計算某個變數的IV,首先需要對其進行分箱。如果強制變數分箱的WOE單調性,這樣可能就會低估某些非線性變數的IV值,如U型變數。因此,為了儘可能使得IV值計算最大,同時儘可能保證分箱的單調性(讓預測變數對目標變數有更好的解釋性),我們利用決策樹的信息增益最大化思想來實現變數的最優分箱。

註:本文主要討論最優分箱與IV值計算的實現過程,對EDA分析、異常值處理等方面不做詳細探討。


二、實現思路:

  1. 利用sklearn決策樹,DecisionTreeClassifier的.tree_屬性獲得決策樹的節點劃分值;
  2. 基於上述得到的劃分值,利用pandas.cut函數對變數進行分箱;
  3. 計算各個分箱的WOE、IV值。

三、數據說明:

測試數據是kaggle案例的訓練數據 - Give Me Some Credit;

Give Me Some Credit?

www.kaggle.com

該案例數據總共有150000條樣本,11個變數,其中1個目標變數,10個特徵變數;

其中,目標變數為SeriousDlqin2yrs:表示未來是否為逾期90天+,1表示逾期90天+,即通常意義上的壞客戶,0則表示沒有逾期90天+的好客戶。


四、代碼部分:

0. import相關模塊:

from sklearn.tree import DecisionTreeClassifier
import pandas as pd
import numpy as np

  1. 讀入數據:

data = pd.read_csv(cs-training.csv)
print(data.shape)
data.head()

部分數據展示

2. 獲得最優分箱邊界值函數的實現:

def optimal_binning_boundary(x: pd.Series, y: pd.Series, nan: float = -999.) -> list:

利用決策樹獲得最優分箱的邊界值列表

boundary = [] # 待return的分箱邊界值列表

x = x.fillna(nan).values # 填充缺失值
y = y.values

clf = DecisionTreeClassifier(criterion=entropy, #「信息熵」最小化準則劃分
max_leaf_nodes=6, # 最大葉子節點數
min_samples_leaf=0.05) # 葉子節點樣本數量最小佔比

clf.fit(x.reshape(-1, 1), y) # 訓練決策樹

n_nodes = clf.tree_.node_count
children_left = clf.tree_.children_left
children_right = clf.tree_.children_right
threshold = clf.tree_.threshold

for i in range(n_nodes):
if children_left[i] != children_right[i]: # 獲得決策樹節點上的劃分邊界值
boundary.append(threshold[i])

boundary.sort()

min_x = x.min()
max_x = x.max() + 0.1 # +0.1是為了考慮後續groupby操作時,能包含特徵最大值的樣本
boundary = [min_x] + boundary + [max_x]

return boundary

測試optimal_binning_boundary函數:

optimal_binning_boundary(x=data[RevolvingUtilizationOfUnsecuredLines],
y=data[SeriousDlqin2yrs])

輸出:

[0.0,
0.11458224803209305,
0.21776090562343597,
0.49497613310813904,
0.6981423199176788,
0.8596274554729462,
50708.1]

3. 獲得某個變數各個分箱的WOE、IV值函數的實現:

def feature_woe_iv(x: pd.Series, y: pd.Series, nan: float = -999.) -> pd.DataFrame:

計算變數各個分箱的WOE、IV值,返回一個DataFrame

x = x.fillna(nan)
boundary = optimal_binning_boundary(x, y, nan) # 獲得最優分箱邊界值列表
df = pd.concat([x, y], axis=1) # 合併x、y為一個DataFrame,方便後續計算
df.columns = [x, y] # 特徵變數、目標變數欄位的重命名
df[bins] = pd.cut(x=x, bins=boundary, right=False) # 獲得每個x值所在的分箱區間

grouped = df.groupby(bins)[y] # 統計各分箱區間的好、壞、總客戶數量
result_df = grouped.agg([(good, lambda y: (y == 0).sum()),
(bad, lambda y: (y == 1).sum()),
(total, count)])

result_df[good_pct] = result_df[good] / result_df[good].sum() # 好客戶佔比
result_df[bad_pct] = result_df[bad] / result_df[bad].sum() # 壞客戶佔比
result_df[total_pct] = result_df[total] / result_df[total].sum() # 總客戶佔比

result_df[bad_rate] = result_df[bad] / result_df[total] # 壞比率

result_df[woe] = np.log(result_df[good_pct] / result_df[bad_pct]) # WOE
result_df[iv] = (result_df[good_pct] - result_df[bad_pct]) * result_df[woe] # IV

print(f"該變數IV = {result_df[iv].sum()}")

return result_df

測試feature_woe_iv函數:

feature_woe_iv(x=data[RevolvingUtilizationOfUnsecuredLines],
y=data[SeriousDlqin2yrs])

輸出:

上述結果可以複製到Excel中做數據條上色處理,提升可視化效果:

如上圖所示,變數RevolvingUtilizationOfUnsecuredLines,分箱WOE趨勢單調,bad_rate風險排序性較好,IV值>1.0則說明該變數預測能力很強。


推薦閱讀:
相关文章