一、定义
1.1 WOE(Weight of Evidence)定义与原理
WOE 是“Weight of Evidence”的缩写,直译为“证据权重”,用来衡量某个特征分箱中“坏人”与“好人”之间的分布差异性。
WOE 的基本定义如下:
WOEi=ln(badi/total badgoodi/total good)WOE_i = \ln \left( \frac{\text{bad}_i / \text{total bad}}{\text{good}_i / \text{total good}} \right)WOEi=ln(goodi/total goodbadi/total bad)
其含义可以进一步展开为:
WOEi=ln(badigoodi)−ln(total badtotal good)WOE_i = \ln \left( \frac{\text{bad}_i}{\text{good}_i} \right) - \ln \left( \frac{\text{total bad}}{\text{total good}} \right)WOEi=ln(goodibadi)−ln(total goodtotal bad)
即:每个分箱中的坏好比(Odds)相对于总体的坏好比之间的差异性。
换句话说,WOE 描述了每个分箱的观测结果相较于总体分布的“证据强度”差异。当 WOE 为正时,表示该分箱中坏人比重较大;为负时表示好人比重较大;接近 0 时说明该分箱与总体无明显差异。
1.2 IV(Information Value)定义与变换
IV 是“Information Value”的缩写,是在风控建模中用来度量特征对目标变量(如好/坏用户)预测能力的指标。
IV 的定义如下:
IV=∑i=1n(bad_ratei−good_ratei)×WOEiIV = \sum_{i=1}^{n} (\text{bad\_rate}_i - \text{good\_rate}_i) \times WOE_iIV=i=1∑n(bad_ratei−good_ratei)×WOEi
其中:
- bad_ratei=baditotal bad\text{bad\_rate}_i = \frac{\text{bad}_i}{\text{total bad}}bad_ratei=total badbadi
- good_ratei=gooditotal good\text{good\_rate}_i = \frac{\text{good}_i}{\text{total good}}good_ratei=total goodgoodi
IV 本质上是 WOE 的加权和,其形式与信息论中的“相对熵”(KL散度)一致,表示总体坏好人分布在每个分箱上的差异程度。
IV 可以改写为:
IV=∑i=1n(baditotal bad)ln(badi/total badgoodi/total good)IV = \sum_{i=1}^{n} \left( \frac{\text{bad}_i}{\text{total bad}} \right) \ln \left( \frac{\text{bad}_i / \text{total bad}}{\text{good}_i / \text{total good}} \right)IV=i=1∑n(total badbadi)ln(goodi/total goodbadi/total bad)
这就将其与 WOE 的公式连接起来,进一步揭示了 IV 的信息含义:衡量每个分箱的坏人分布相对于好人分布的信息增益。分箱之间差异越大,IV 越高,说明该变量具有更强的区分能力。
二、计算步骤
-
分箱(Binning)
- 连续变量:等频、等距或基于业务自定义。
- 离散变量:合并稀疏类别。
-
统计分箱好坏数
- bin_good_i、bin_bad_i,及总体 good_total、bad_total。
-
计算边际占比
- dist_goodi=bin_goodi/good_totaldist\_good_i = bin\_good_i / good\_totaldist_goodi=bin_goodi/good_total
- dist_badi=bin_badi/bad_totaldist\_bad_i = bin\_bad_i / bad\_totaldist_badi=bin_badi/bad_total
-
计算 WOE
- WOEi=ln(dist_badi)−ln(dist_goodi)WOE_i = \ln(dist\_bad_i) - \ln(dist\_good_i)WOEi=ln(dist_badi)−ln(dist_goodi)
-
检查单调性与合并
- 若 WOE 曲线不单调,需调整分箱或合并相邻箱。
-
计算 IV
- IV=∑i(dist_badi−dist_goodi)×WOEiIV = \sum_i (dist\_bad_i - dist\_good_i) \times WOE_iIV=∑i(dist_badi−dist_goodi)×WOEi
-
跨集稳定性检验
- 在验证集/测试集上重复计算,确保单调性与IV值稳定。
三、公式来源与理论支撑
3.1 贝叶斯视角
从贝叶斯定理:
lnP(Y=1∣X)P(Y=0∣X)=lnP(Y=1)P(Y=0)+lnP(X∣Y=1)P(X∣Y=0) \ln\frac{P(Y=1 \mid X)}{P(Y=0 \mid X)} = \ln\frac{P(Y=1)}{P(Y=0)} + \ln\frac{P(X \mid Y=1)}{P(X \mid Y=0)} lnP(Y=0∣X)P(Y=1∣X)=lnP(Y=0)P(Y=1)+lnP(X∣Y=0)P(X∣Y=1)
- 先验项:lnP(Y=1)P(Y=0)\ln\frac{P(Y=1)}{P(Y=0)}lnP(Y=0)P(Y=1)
- 证据项(WOE):lnP(X∣Y=1)P(X∣Y=0)\ln\frac{P(X \mid Y=1)}{P(X \mid Y=0)}lnP(X∣Y=0)P(X∣Y=1)
WOE 正是特征对先验 Odds 的信息增量。
3.2 信息论视角
IV 等价于两分布间的相对熵(KL 散度):
DKL(P1∥P0)=∑iP1(i)lnP1(i)P0(i)
D_{KL}(P_1 \parallel P_0) = \sum_i P_1(i) \ln \frac{P_1(i)}{P_0(i)}
DKL(P1∥P0)=i∑P1(i)lnP0(i)P1(i)
- 令P1=P(X∣Y=1)P_1 = P(X\mid Y=1)P1=P(X∣Y=1),P0=P(X∣Y=0)P_0 = P(X\mid Y=0)P0=P(X∣Y=0),即可看出 IV 从信息量角度衡量分布差异。
四、与评分卡(Logistic 回归)的关系
- Logistic 回归模型:
lnP(Y=1∣X)P(Y=0∣X)=β0+∑jβj Xj\ln\frac{P(Y=1\mid X)}{P(Y=0\mid X)} = \beta_0 + \sum_j \beta_j \, X_jlnP(Y=0∣X)P(Y=1∣X)=β0+j∑βjXj
- 若用 WOE 变量代替原始XjX_jXj,那么系数βj\beta_jβj即衡量该分箱的证据权重与评分点的映射。
- 单调的 WOE 曲线可确保评分点随特征单调变化,提升可解释性与业务落地。
五、风控中的业务应用
- 缺失值与异常值:将 null 或极端值单独成箱,保证无信息丢失。
- 特征选择:利用 IV 快速筛除弱变量,降低维度。
- 稳健建模:WOE 变换后,对噪声与尺度敏感性弱,LR 收敛更快。
- 可解释报表:通过 WOE 曲线与 IV 分析,向业务部门展示变量效力。
六、Python 计算示例
import pandas as pd
import numpy as np
def compute_woe_iv(df, feature, target, bins=10):
# 分箱
df['bin'] = pd.qcut(df[feature], q=bins, duplicates='drop')
# 统计
grouped = df.groupby('bin')[target].agg(['count', 'sum'])
grouped.columns = ['total', 'bad']
grouped['good'] = grouped['total'] - grouped['bad']
# 总体
total_bad = grouped['bad'].sum()
total_good = grouped['good'].sum()
# 边际分布
grouped['dist_bad'] = grouped['bad'] / total_bad
grouped['dist_good'] = grouped['good'] / total_good
# WOE 与 IV
grouped['woe'] = np.log(grouped['dist_bad'] / grouped['dist_good'])
grouped['iv'] = (grouped['dist_bad'] - grouped['dist_good']) * grouped['woe']
# 返回
iv_value = grouped['iv'].sum()
return grouped[['total','bad','good','dist_bad','dist_good','woe','iv']], iv_value
# 示例用法
df = pd.read_csv('data.csv')
woe_table, iv = compute_woe_iv(df, feature='age', target='is_bad', bins=5)
print(woe_table)
print(f"IV = {iv:.4f}")