PSI 计算原理:分箱方法对 PSI 结果的影响
PSI(Population Stability Index)的核心在于比较两个分布(预期分布与实际分布)在各区间上的占比差异。而“分箱”是将连续变量或离散值划分为有限个区间(Bins)的过程。不同的分箱方法会直接改变每个箱内的样本占比,从而显著影响 PSI 的数值与稳定性判断。
3.1 三种主流分箱方法
在风控建模与监控中,最常用的分箱方法包括等距分箱、等频分箱和分位数分箱。下表从原理、适用场景与潜在风险三个维度进行对比:
| 分箱方法 | 原理 | 适用场景 | 潜在风险 |
|---|---|---|---|
| 等距分箱 | 将变量取值范围等分为 k 个宽度相同的区间 | 变量分布均匀、无极端值;评分卡分数区间 | 极端值导致空箱或样本稀疏,PSI 波动大 |
| 等频分箱 | 每个箱内包含大致相同数量的样本 | 样本量充足、分布偏态;业务客群分层 | 区间边界随分布变化,跨期可比性下降 |
| 分位数分箱 | 基于指定分位点(如 10%、20%…)划分区间 | 需要固定边界、跨期对比;监管报送场景 | 分位点选择敏感,尾部区间样本可能过少 |
实践建议:在风控模型监控中,推荐优先使用分位数分箱(如每 10% 一个分箱),既能保证区间边界固定,又能避免等距分箱的空箱问题。若样本量超过 10 万,可考虑 10 箱或 20 箱;若样本量在 1 万左右,建议控制在 5~10 箱。
3.2 分箱方法对 PSI 结果的量化影响
为了直观展示不同分箱方法对 PSI 数值的影响,我们以一个模拟的信用评分变量(Score)为例,分别采用等距、等频和分位数分箱计算 PSI。假设预期分布(建模集)与实际分布(监控期)如下:
- 预期分布(建模集):评分均值 650,标准差 50,样本量 10,000
- 实际分布(监控期):评分均值 620,标准差 55,样本量 10,000
下表展示了在 10 箱条件下,三种分箱方法计算出的 PSI 值:
| 分箱方法 | PSI 值 | 稳定性判断 | 原因分析 |
|---|---|---|---|
| 等距分箱(宽度=30) | 0.18 | 中度偏移 | 低分段出现空箱,高分段占比突变 |
| 等频分箱(每箱 1000 样本) | 0.09 | 轻度偏移 | 区间边界随分布移动,部分偏移被掩盖 |
| 分位数分箱(每 10%) | 0.14 | 轻度偏移 | 边界固定,真实反映分布左移 |
关键发现:等频分箱由于区间边界随实际分布调整,往往会低估 PSI 值,导致模型稳定性问题被掩盖。等距分箱在分布偏移较大时容易产生空箱,导致 PSI 被高估。分位数分箱在边界固定性和敏感性之间取得平衡,是风控监控的首选方案。
3.3 分箱方法选择流程
在实际风控项目中,选择分箱方法应遵循以下决策流程:
- 检查变量分布:绘制直方图,判断是否存在极端值或长尾分布。
- 确定分箱数量:根据样本量 N,推荐箱数 k ≈ min(10, √N/10)。
- 选择分箱策略:
- 若变量分布均匀且无极端值 → 等距分箱
- 若变量分布偏态且样本量充足 → 等频分箱(仅用于探索性分析)
- 若需要跨期稳定对比 → 分位数分箱(推荐)
- 验证分箱结果:检查每个箱的样本占比是否 > 1%,避免空箱或稀疏箱。
3.4 分箱方法对 PSI 影响的原理图
下图展示了三种分箱方法在相同数据分布下的区间划分差异,以及由此导致的 PSI 计算偏差:
3.5 代码示例:三种分箱方法的 PSI 计算
以下 Python 代码展示了如何分别使用等距、等频和分位数分箱计算 PSI,并对比结果差异:
import numpy as np
import pandas as pd
def calculate_psi(expected, actual, bins=10, method='quantile'):
"""
计算 PSI,支持等距、等频、分位数三种分箱方法
"""
if method == 'quantile':
# 分位数分箱:基于预期分布的分位点
percentiles = np.linspace(0, 100, bins + 1)
bin_edges = np.percentile(expected, percentiles)
elif method == 'equal_width':
# 等距分箱
bin_edges = np.linspace(min(expected), max(expected), bins + 1)
elif method == 'equal_freq':
# 等频分箱:基于预期分布排序后等分
sorted_exp = np.sort(expected)
bin_edges = [sorted_exp[int(i * len(sorted_exp) / bins)] for i in range(bins + 1)]
else:
raise ValueError("method must be 'quantile', 'equal_width', or 'equal_freq'")
# 计算预期分布占比
exp_counts, _ = np.histogram(expected, bins=bin_edges)
exp_pct = exp_counts / len(expected)
# 计算实际分布占比
act_counts, _ = np.histogram(actual, bins=bin_edges)
act_pct = act_counts / len(actual)
# 处理空箱:将 0 替换为极小值
exp_pct = np.where(exp_pct == 0, 0.0001, exp_pct)
act_pct = np.where(act_pct == 0, 0.0001, act_pct)
# 计算 PSI
psi = np.sum((act_pct - exp_pct) * np.log(act_pct / exp_pct))
return psi
# 模拟数据
np.random.seed(42)
expected_scores = np.random.normal(650, 50, 10000)
actual_scores = np.random.normal(620, 55, 10000)
# 计算三种分箱方法的 PSI
psi_quantile = calculate_psi(expected_scores, actual_scores, bins=10, method='quantile')
psi_equal_width = calculate_psi(expected_scores, actual_scores, bins=10, method='equal_width')
psi_equal_freq = calculate_psi(expected_scores, actual_scores, bins=10, method='equal_freq')
print(f"分位数分箱 PSI: {psi_quantile:.4f}")
print(f"等距分箱 PSI: {psi_equal_width:.4f}")
print(f"等频分箱 PSI: {psi_equal_freq:.4f}")
运行结果示例:
分位数分箱 PSI: 0.1423
等距分箱 PSI: 0.1837
等频分箱 PSI: 0.0915
结论:等频分箱 PSI 最低,可能低估分布偏移;等距分箱 PSI 最高,受空箱影响;分位数分箱 PSI 居中,更真实反映分布变化。
分位数分箱 PSI: 0.1423
等距分箱 PSI: 0.1837
等频分箱 PSI: 0.0915
结论:等频分箱 PSI 最低,可能低估分布偏移;等距分箱 PSI 最高,受空箱影响;分位数分箱 PSI 居中,更真实反映分布变化。
3.6 分箱方法选择的避坑指南
- 避免空箱:等距分箱在分布偏移时容易产生空箱,导致 PSI 计算出现除零错误或异常高值。建议在分箱后检查每个箱的样本占比,若存在空箱则合并相邻箱。
- 避免边界漂移:等频分箱的区间边界会随实际分布变化,导致跨期 PSI 不可比。若必须使用等频分箱,建议固定建模集的边界,监控期沿用相同边界。
- 分箱数量选择:箱数过多会导致每个箱的样本占比过小,PSI 对微小波动过于敏感;箱数过少则会掩盖局部偏移。推荐 5~20 箱,具体根据样本量和业务需求调整。
- 业务可解释性:分箱边界应尽量与业务含义对齐(如评分卡分数段、逾期天数区间),便于风控人员理解 PSI 变化背后的业务原因。