3. PSI 计算步骤详解:从原始分数到分箱、计算实际占比与预期占比、逐项求和的全流程拆解
群体稳定性指标(PSI)的计算并非一个黑盒公式,而是一套标准化的数据分箱与对比流程。本章将拆解从原始模型分数到最终 PSI 值的每一个步骤,确保你不仅能调用函数,更能理解每一步的数学含义与业务逻辑。
3.1 计算全流程概览
PSI 的计算可以归纳为四个核心阶段:数据准备 → 分箱 → 占比计算 → 求和。下图展示了完整的计算流水线:
3.2 步骤一:数据准备与对齐
在开始计算之前,必须明确两个核心数据集:
- 预期分布(Expected, E):模型开发时使用的训练集或验证集分数,作为稳定性的基准。
- 实际分布(Actual, A):模型上线后,在监控周期内(如月度、季度)产生的实时评分结果。
数据对齐要点: 确保两个数据集的分数范围一致(例如都是 300-850 分),且剔除异常值(如分数为 NULL 或超出预设边界的记录)。如果实际分布样本量远小于预期分布,需考虑使用小样本校正方法。
3.3 步骤二:分箱(Binning)
分箱是 PSI 计算中最关键的环节。分箱方式直接影响 PSI 的敏感度和稳定性。常用的分箱策略有两种:
| 分箱方式 | 方法描述 | 适用场景 | 注意事项 |
|---|---|---|---|
| 等频分箱 | 按预期分布的百分位数划分,使每箱样本量大致相等(通常 10 箱) | 评分卡模型、信用评分等分数分布不均匀的场景 | 实际分布可能出现空箱(某箱样本量为 0),需做平滑处理 |
| 等距分箱 | 按分数范围等间隔划分(如每 50 分一箱) | 分数分布均匀、业务解释性要求高的场景 | 可能出现某箱样本过少或过多,影响统计稳定性 |
分箱边界确定规则: 分箱边界必须完全基于预期分布的数据来确定。实际分布的数据只是被“放入”这些预先定义好的箱子中,不能根据实际分布重新调整边界。
实战建议: 信贷风控中默认使用 10 箱等频分箱。如果预期分布样本量小于 10,000,可适当减少箱数(如 5 箱)以避免空箱。对于分数范围较窄的场景(如 600-700 分),等距分箱可能更直观。
3.4 步骤三:计算实际占比与预期占比
分箱完成后,分别计算每个箱子在预期分布和实际分布中的占比:
- 预期占比(%E_i) = 第 i 箱中预期样本数 / 预期总样本数
- 实际占比(%A_i) = 第 i 箱中实际样本数 / 实际总样本数
以下是一个 5 箱等频分箱的示例计算表:
| 箱号 | 分数区间 | 预期样本数 | 预期占比 (%E_i) | 实际样本数 | 实际占比 (%A_i) |
|---|---|---|---|---|---|
| 1 | 300 - 450 | 2,000 | 20.00% | 1,500 | 15.00% |
| 2 | 451 - 550 | 2,000 | 20.00% | 2,200 | 22.00% |
| 3 | 551 - 650 | 2,000 | 20.00% | 2,500 | 25.00% |
| 4 | 651 - 750 | 2,000 | 20.00% | 2,800 | 28.00% |
| 5 | 751 - 850 | 2,000 | 20.00% | 1,000 | 10.00% |
| 合计 | - | 10,000 | 100% | 10,000 | 100% |
空箱处理: 如果实际分布中某箱占比为 0%(%A_i = 0),则 ln(%A_i / %E_i) 会趋向负无穷。此时需要对该箱进行平滑处理,常用方法是将 0 替换为一个极小值(如 0.0001),或将该箱与相邻箱合并。
3.5 步骤四:逐项求和计算 PSI
得到每个箱子的 %E_i 和 %A_i 后,按照 PSI 公式逐箱计算贡献值并求和:
PSI = Σ [ (%A_i - %E_i) * ln(%A_i / %E_i) ] (i = 1, 2, ..., n)
以上述 5 箱数据为例,逐项计算过程如下:
| 箱号 | %E_i | %A_i | %A_i - %E_i | %A_i / %E_i | ln(%A_i / %E_i) | 单项贡献 |
|---|---|---|---|---|---|---|
| 1 | 0.2000 | 0.1500 | -0.0500 | 0.7500 | -0.2877 | 0.0144 |
| 2 | 0.2000 | 0.2200 | 0.0200 | 1.1000 | 0.0953 | 0.0019 |
| 3 | 0.2000 | 0.2500 | 0.0500 | 1.2500 | 0.2231 | 0.0112 |
| 4 | 0.2000 | 0.2800 | 0.0800 | 1.4000 | 0.3365 | 0.0269 |
| 5 | 0.2000 | 0.1000 | -0.1000 | 0.5000 | -0.6931 | 0.0693 |
| 合计 | 1.0000 | 1.0000 | 0.0000 | - | - | PSI = 0.1237 |
计算得到 PSI = 0.1237。根据行业经验阈值,该值介于 0.1 和 0.25 之间,属于中度漂移,需要进一步排查原因。
3.6 步骤五:Python 代码实现
以下是一个完整的 PSI 计算函数,包含等频分箱、空箱平滑和结果输出:
import numpy as np
import pandas as pd
def calculate_psi(expected, actual, bins=10, bucket_type='quantile'):
"""
计算群体稳定性指标 (PSI)
参数:
expected: array-like, 预期分布分数
actual: array-like, 实际分布分数
bins: int, 分箱数量 (默认10)
bucket_type: str, 'quantile'(等频) 或 'uniform'(等距)
返回:
psi_value: float, PSI值
psi_df: DataFrame, 逐箱明细
"""
# 1. 基于预期分布确定分箱边界
if bucket_type == 'quantile':
# 等频分箱:使用百分位数
percentiles = np.linspace(0, 100, bins + 1)
bin_edges = np.percentile(expected, percentiles)
else:
# 等距分箱:在分数范围内均匀划分
bin_edges = np.linspace(min(expected), max(expected), bins + 1)
# 确保边界唯一(处理重复百分位数)
bin_edges = np.unique(bin_edges)
# 2. 对预期和实际分布进行分箱
expected_bins = np.digitize(expected, bin_edges, right=False)
actual_bins = np.digitize(actual, bin_edges, right=False)
# 3. 计算每箱样本数
expected_counts = np.bincount(expected_bins, minlength=len(bin_edges)+1)[1:len(bin_edges)+1]
actual_counts = np.bincount(actual_bins, minlength=len(bin_edges)+1)[1:len(bin_edges)+1]
# 4. 计算占比(加入平滑处理,防止除零)
epsilon = 1e-6
expected_pct = expected_counts / len(expected)
actual_pct = actual_counts / len(actual)
expected_pct = np.clip(expected_pct, epsilon, 1 - epsilon)
actual_pct = np.clip(actual_pct, epsilon, 1 - epsilon)
# 5. 逐项计算PSI贡献
psi_components = (actual_pct - expected_pct) * np.log(actual_pct / expected_pct)
psi_value = np.sum(psi_components)
# 6. 构建明细DataFrame
psi_df = pd.DataFrame({
'bin_start': bin_edges[:-1],
'bin_end': bin_edges[1:],
'expected_count': expected_counts,
'actual_count': actual_counts,
'expected_pct': expected_pct,
'actual_pct': actual_pct,
'psi_component': psi_components
})
return psi_value, psi_df
# 使用示例
expected_scores = np.random.normal(600, 100, 10000)
actual_scores = np.random.normal(620, 110, 10000)
psi, details = calculate_psi(expected_scores, actual_scores, bins=10)
print(f"PSI 值: {psi:.4f}")
print(details.head())
3.7 常见计算陷阱与避坑指南
在实际计算中,以下几个问题最容易导致 PSI 结果失真:
- 分箱边界不一致:预期和实际分布使用了不同的分箱规则,导致对比失去意义。
- 空箱未处理:实际分布中某箱样本为 0,直接计算 ln(0) 导致无穷大。
- 样本量差异过大:预期分布 100 万样本,实际分布仅 1000 样本,占比波动剧烈。
- 分数范围漂移:实际分布出现了预期分布中从未出现过的高分或低分,导致边界外样本被丢弃或归入首尾箱。
最佳实践: 每次计算 PSI 时,务必输出逐箱明细表,检查是否有空箱、边界外样本或占比突变箱。不要只看最终的 PSI 数值,逐箱分析才能定位漂移的具体来源。