3. PSI计算原理:分箱方法、比例计算与对数求和
群体稳定性指标(PSI)的核心思想是量化两个分布之间的差异。其计算过程可拆解为三个关键步骤:分箱、比例计算与对数求和。每一步的选择与实现都直接影响PSI值的敏感度和可靠性。
3.1 分箱方法:等距与等频
分箱是将连续变量或离散变量划分为若干区间(桶)的过程。PSI对分箱方式敏感,常用方法有两种:等距分箱和等频分箱。
| 分箱方法 | 定义 | 适用场景 | 注意事项 |
|---|---|---|---|
| 等距分箱 | 将变量取值范围均匀划分为N个宽度相同的区间 | 变量分布均匀,无极端离群点 | 若数据稀疏,部分箱可能样本量为0,导致计算异常 |
| 等频分箱 | 将变量按分位数划分,使每个区间包含大致相等的样本量 | 长尾分布、存在离群点、评分卡模型 | 需保证期望分布(训练集)分箱边界与当前分布一致 |
实践建议:在风控模型监控中,推荐使用等频分箱,且分箱数通常设为10(即按十分位数划分)。这能确保每个箱都有足够的样本支撑比例计算,避免因零样本导致的PSI无穷大问题。
分箱完成后,需记录每个箱的边界值。PSI计算要求期望分布(通常为训练集或基准期)与实际分布(当前监控期)使用完全相同的分箱边界。
3.2 实际分布与期望分布的比例计算
在每个箱内,分别计算两个分布的样本占比:
- 期望占比(Expected Proportion, Ei):基准期落在第i箱的样本数 / 基准期总样本数
- 实际占比(Actual Proportion, Ai):监控期落在第i箱的样本数 / 监控期总样本数
计算时需注意两个关键处理:
- 零占比处理:若Ei或Ai为0,直接代入会导致对数计算失败。通常做法是给每个箱的计数加一个极小常数(如0.5或1e-6),或将该箱与相邻箱合并。
- 比例归一化:确保所有箱的Ei之和为1,Ai之和为1。
核心公式:PSI = Σ[(Ai - Ei) × ln(Ai / Ei)],其中i从1到N(N为分箱总数)。
3.3 自然对数与求和:PSI的最终聚合
得到每个箱的Ei和Ai后,按以下步骤完成PSI计算:
- 计算比例差:(Ai - Ei)
- 计算比例比的自然对数:ln(Ai / Ei)
- 箱内乘积:将步骤1与步骤2的结果相乘
- 全局求和:对所有箱的乘积结果求和,得到最终PSI值
自然对数ln的作用是放大差异:当Ai与Ei接近时,ln(Ai/Ei)趋近于0;当两者偏离较大时,对数值会快速增大,使PSI对分布偏移更敏感。
常见误区:部分实现使用log10而非自然对数ln,这会导致PSI数值整体缩小约2.3倍。在设定阈值时,必须明确所使用的对数底数。行业通用标准基于自然对数ln。
3.4 PSI计算流程总览
下图展示了从原始数据到PSI值的完整计算链路:
3.5 计算示例与代码实现
以下是一个完整的PSI计算Python实现,包含等频分箱与零占比处理:
import numpy as np
import pandas as pd
def calculate_psi(expected, actual, bins=10):
"""
计算群体稳定性指标(PSI)
Parameters:
expected: array-like, 期望分布(基准期数据)
actual: array-like, 实际分布(监控期数据)
bins: int, 分箱数量(默认10)
Returns:
float: PSI值
"""
# 1. 基于期望分布确定分箱边界(等频分箱)
percentiles = np.linspace(0, 100, bins + 1)
bin_edges = np.percentile(expected, percentiles)
# 确保边界唯一
bin_edges = np.unique(bin_edges)
# 2. 分别统计两个分布在各箱的样本数
exp_counts, _ = np.histogram(expected, bins=bin_edges)
act_counts, _ = np.histogram(actual, bins=bin_edges)
# 3. 计算占比(加入平滑因子避免零值)
smooth = 0.5
exp_prop = (exp_counts + smooth) / (np.sum(exp_counts) + smooth * len(exp_counts))
act_prop = (act_counts + smooth) / (np.sum(act_counts) + smooth * len(act_counts))
# 4. 计算PSI
psi = np.sum((act_prop - exp_prop) * np.log(act_prop / exp_prop))
return psi
# 使用示例
np.random.seed(42)
expected_data = np.random.normal(0, 1, 10000)
actual_data = np.random.normal(0.2, 1.1, 10000) # 均值偏移0.2,标准差增大
psi_value = calculate_psi(expected_data, actual_data)
print(f"PSI = {psi_value:.4f}") # 输出示例: PSI = 0.0452
PSI阈值参考:
- PSI < 0.1:分布无显著变化,模型稳定
- 0.1 ≤ PSI < 0.25:分布轻度偏移,需关注
- PSI ≥ 0.25:分布显著偏移,需立即排查
3.6 分箱方法对比与选择指南
为帮助在实际项目中快速决策,下表总结了两种分箱方法的优劣与典型应用:
| 对比维度 | 等距分箱 | 等频分箱 |
|---|---|---|
| 对离群点的鲁棒性 | 弱,离群点可能导致大量空箱 | 强,离群点被自动归入首尾箱 |
| 对分布形状的敏感性 | 能反映分布密度变化 | 各箱样本量均衡,但可能掩盖局部密度变化 |
| 计算稳定性 | 低,空箱需额外处理 | 高,每个箱都有样本 |
| 推荐场景 | 变量值域有限且分布均匀 | 评分卡、信用风险模型、长尾分布 |
进阶技巧:在监控多个特征时,建议对每个特征单独进行等频分箱,而非使用全局统一分箱。因为不同特征的分布形态差异较大,统一分箱可能导致部分特征的分箱效果极差。