一、从WOE分箱到评分卡赋分
1.1 WOE分箱原理回顾
WOE(Weight of Evidence)将类别或数值型变量按照对目标事件(如违约)的分布差异进行分箱:
WOE i = ln 好样本比例 i 坏样本比例 i = ln G o o d i G o o d T B a d i B a d T \text{WOE}_i = \ln\frac{\text{好样本比例}_i}{\text{坏样本比例}_i} = \ln\frac{\frac{Good_i}{Good_T}}{\frac{Bad_i}{Bad_T}} WOEi=ln坏样本比例i好样本比例i=lnBadTBadiGoodTGoodi
其中 i i i表示第 i i i个箱。良好的WOE分箱有助于线性模型更好地捕捉变量与目标间的关系。
1.2 评分卡分数标度校准
评分卡常定义三个参数:基准分数P0、对应Odds0、PDO(分数翻倍点)。
- 基准Odds: O d d s 0 = P ( D = 0 ) P ( D = 1 ) Odds_0 = \frac{P(D=0)}{P(D=1)} Odds0=P(D=1)P(D=0)(开发样本中好/坏的比值)
- 基准分数: S c o r e 0 Score_0 Score0,如600分
- PDO:分数每变化PDO,Odds翻倍(或减半)
由此,可推导:
A
=
S
c
o
r
e
0
+
B
×
ln
O
d
d
s
0
A = Score_0 + B \times \ln Odds_0
A=Score0+B×lnOdds0
B
=
−
P
D
O
ln
2
B = -\frac{PDO}{\ln 2}
B=−ln2PDO
评分与log odds关联:
S c o r e = A + B × ln p ( x ) 1 − p ( x ) Score = A + B \times \ln\frac{p(x)}{1-p(x)} Score=A+B×ln1−p(x)p(x)
1.3 自变量区间赋分
在线性回归或逻辑回归中,变量贡献为系数 β \beta β与WOE乘积。因此,分箱 i i i的部分分数(partial score)为:
S c o r e i = − B × β × W O E i Score_i = -B\times\beta\times WOE_i Scorei=−B×β×WOEi
将所有变量各箱得分相加并加上常数项 A A A,即可得到最终客户评分。
Python示例:计算单变量分箱WOE并生成部分分数
import numpy as np
import pandas as pd
def calc_woe(df, feature, target, bins):
df['bin'] = pd.cut(df[feature], bins=bins)
agg = df.groupby('bin')[target].agg(['count', 'sum'])
agg.columns = ['total', 'bad']
agg['good'] = agg['total'] - agg['bad']
agg['dist_bad'] = agg['bad'] / agg['bad'].sum()
agg['dist_good'] = agg['good'] / agg['good'].sum()
agg['woe'] = np.log(agg['dist_good'] / agg['dist_bad'])
return agg
# 示例:
aggr = calc_woe(df, 'age', 'is_bad', bins=[18,25,35,45,55,65,100])
aggr['part_score'] = -B * beta_age * aggr['woe']
二、特征稳定性指标CSI
2.1 CSI定义
CSI衡量当前样本与开发样本在分箱后的分数分布及其对最终总分影响:
CSI = ∑ i ( p i c u r r − p i d e v ) × S c o r e i \text{CSI} = \sum_i (p_i^{curr} - p_i^{dev}) \times Score_i CSI=i∑(picurr−pidev)×Scorei
- p i c u r r p_i^{curr} picurr:当前监控窗口第 i i i箱占比
- p i d e v p_i^{dev} pidev:开发样本第 i i i箱占比
- S c o r e i Score_i Scorei:第 i i i箱的部分分数
2.2 CSI计算步骤
- 对模型输入变量或模型输出分箱
- 计算开发样本与当前样本各箱占比
- 读取对应箱的部分分数
- 按公式累加计算
2.3 CSI的业务解读
- 正负方向:正值表示当前客户群倾向更高分段,负值则向低分段漂移
- 绝对值:值越大,表示该特征对总分偏移影响越显著
三、PSI与CSI的区别与联系
属性 | PSI(群体) | CSI(特征) |
---|---|---|
分析层级 | 模型得分层 | 单个特征分数层 |
公式差异 | ∑ ( p i c u r r − p i d e v ) ln p i c u r r p i d e v \sum(p_i^{curr}-p_i^{dev}) \ln\frac{p_i^{curr}}{p_i^{dev}} ∑(picurr−pidev)lnpidevpicurr | ∑ ( p i c u r r − p i d e v ) × S c o r e i \sum(p_i^{curr}-p_i^{dev}) \times Score_i ∑(picurr−pidev)×Scorei |
阈值标准 | 通用经验阈值(<0.1稳定,0.1~0.25轻度偏移,>0.25需关注) | 无统一阈值,需结合业务与得分尺度评估 |
作用范围 | 宏观监控模型整体稳定性 | 微观识别单变量波动来源 |
实践建议:优先以PSI判断模型得分稳定性,再结合CSI定位具体特征波动原因。
附:完整Python实现示例
import numpy as np, pandas as pd
def calc_psi(ods, exp, bins):
ods['bin'] = pd.cut(ods['score'], bins=bins)
exp['bin'] = pd.cut(exp['score'], bins=bins)
odf = ods['bin'].value_counts(normalize=True).sort_index()
edf = exp['bin'].value_counts(normalize=True).sort_index()
psi = ((odf - edf) * np.log(odf/edf)).sum()
return psi
def calc_csi(curr, dev, part_scores, bins):
curr['bin'] = pd.cut(curr['score'], bins=bins)
dev['bin'] = pd.cut(dev['score'], bins=bins)
pc = curr['bin'].value_counts(normalize=True).sort_index()
pdv = dev['bin'].value_counts(normalize=True).sort_index()
return ((pc - pdv) * part_scores).sum()
# 示例调用:
bins = np.linspace(300,900,12)
psi_value = calc_psi(current_df, dev_df, bins)
csi_value = calc_csi(current_df, dev_df, part_scores, bins)
print(f"PSI={psi_value:.3f}, CSI={csi_value:.3f}")