4、最优分箱之卡方分箱:卡方检验原理、卡方分箱算法步骤、Python实现卡方分箱
卡方分箱(Chi-Merge)是一种基于统计检验的监督式分箱方法。它通过卡方检验衡量相邻区间内目标变量分布的相似性,逐步合并最相似的区间,直到满足预设的停止条件。该方法能自动发现特征与目标变量之间的非线性关系,是信用评分、风险建模等场景中最常用的分箱技术之一。
4.1 卡方检验原理
卡方检验(Chi-Square Test)用于判断两个分类变量之间是否独立。在分箱场景中,我们检验“特征区间”与“目标变量(如好坏客户)”是否独立。若两个相邻区间在目标分布上无显著差异(即独立),则合并它们不会损失预测信息。
卡方统计量计算公式:
χ² = Σ ( (O_ij - E_ij)² / E_ij )
其中:
- O_ij:第 i 行第 j 列的观测频数
- E_ij:第 i 行第 j 列的期望频数(假设独立)
- E_ij = (行合计 × 列合计) / 总样本数
卡方检验在分箱中的核心逻辑:
| 概念 | 说明 |
|---|---|
| 原假设 H₀ | 特征区间与目标变量独立(分布无差异) |
| 备择假设 H₁ | 特征区间与目标变量不独立(分布有差异) |
| 卡方值 | 衡量观测频数与期望频数的偏离程度 |
| p 值 | 在原假设成立下,观察到当前卡方值或更极端值的概率 |
| 决策规则 | 若 p 值 > 显著性水平 α(如 0.05),则接受 H₀,合并区间 |
关键理解:卡方值越小,说明观测频数与期望频数越接近,两个区间在目标分布上越相似,越应该合并。卡方分箱本质上是“自底向上”的贪心合并算法。
4.2 卡方分箱算法步骤
卡方分箱(Chi-Merge)是一种自底向上的合并算法,从最细粒度的初始分箱开始,逐步合并最相似的相邻区间。
算法完整步骤:
- 初始化:将特征值排序后,每个唯一值作为一个初始区间(分箱)。
- 构建列联表:对每个相邻区间对 (i, i+1),构建 2×2 列联表,统计好/坏客户频数。
- 计算卡方值:对每个相邻区间对计算 χ² 统计量。
- 合并:找到 χ² 值最小的相邻区间对(即最相似),合并它们。
- 重复:重新计算受影响区间的卡方值,重复步骤 3-4。
- 停止:当满足以下任一条件时停止:
- 当前区间数 ≤ 预设最大分箱数(如 5 箱)
- 所有相邻区间对的 χ² 值均大于卡方阈值(如 α=0.05 对应的临界值)
算法特点:卡方分箱是全局最优的贪心近似,每次合并都选择对整体信息损失最小的操作。它天然处理缺失值(可将缺失单独作为一箱参与合并),且对异常值不敏感。
4.3 Python实现卡方分箱
以下代码实现完整的卡方分箱类,包含卡方计算、合并逻辑和自动分箱功能。
import pandas as pd
import numpy as np
from scipy.stats import chi2_contingency
class ChiMerge:
"""卡方分箱实现"""
def __init__(self, max_bins=5, alpha=0.05):
self.max_bins = max_bins # 最大分箱数
self.alpha = alpha # 显著性水平
self.bins_ = None # 分箱边界
self.chi2_threshold_ = None # 卡方阈值
def _chi2_test(self, crosstab):
"""计算两个区间的卡方统计量"""
chi2, p, _, _ = chi2_contingency(crosstab, correction=False)
return chi2, p
def fit(self, X, y):
"""执行卡方分箱"""
df = pd.DataFrame({'feature': X, 'target': y})
df = df.sort_values('feature').reset_index(drop=True)
# 步骤1:初始化——每个唯一值作为一个区间
unique_vals = sorted(df['feature'].unique())
bins = unique_vals.copy()
# 步骤2-5:迭代合并
while len(bins) > self.max_bins:
chi2_pairs = []
# 计算所有相邻区间的卡方值
for i in range(len(bins) - 1):
left_val = bins[i]
right_val = bins[i+1]
# 获取两个区间的数据
if i == 0:
left_mask = df['feature'] <= left_val
else:
left_mask = (df['feature'] > bins[i-1]) & (df['feature'] <= left_val)
right_mask = (df['feature'] > left_val) & (df['feature'] <= right_val)
left_target = df.loc[left_mask, 'target']
right_target = df.loc[right_mask, 'target']
# 构建2x2列联表
left_good = (left_target == 0).sum()
left_bad = (left_target == 1).sum()
right_good = (right_target == 0).sum()
right_bad = (right_target == 1).sum()
crosstab = np.array([[left_good, left_bad],
[right_good, right_bad]])
chi2, p = self._chi2_test(crosstab)
chi2_pairs.append((chi2, p, i))
# 找到最小卡方值的区间对
chi2_pairs.sort(key=lambda x: x[0])
min_chi2, min_p, merge_idx = chi2_pairs[0]
# 检查停止条件:最小卡方值是否超过阈值
if min_chi2 > self.chi2_threshold_:
break
# 合并区间
bins.pop(merge_idx + 1)
# 保存分箱边界
self.bins_ = bins
self.chi2_threshold_ = chi2_contingency(
np.array([[1, 1], [1, 1]]), correction=False
)[1] # 使用p值阈值
return self
def transform(self, X):
"""将特征映射到分箱"""
return pd.cut(X, bins=[-np.inf] + self.bins_ + [np.inf],
labels=False, right=True)
def fit_transform(self, X, y):
"""拟合并转换"""
self.fit(X, y)
return self.transform(X)
# 使用示例
if __name__ == "__main__":
# 生成示例数据
np.random.seed(42)
n = 1000
X = np.random.randn(n) * 10 + 50 # 年龄特征
# 模拟好坏客户:年龄与坏账率的U型关系
prob = 0.1 + 0.3 * (np.abs(X - 50) / 20)
y = np.random.binomial(1, prob)
# 执行卡方分箱
chi_merger = ChiMerge(max_bins=5, alpha=0.05)
bins = chi_merger.fit_transform(X, y)
print("分箱边界:", chi_merger.bins_)
print("分箱结果分布:")
print(pd.Series(bins).value_counts().sort_index())
注意事项:
- 卡方分箱要求目标变量为二分类(0/1),且每个区间内应同时包含两类样本,否则卡方检验失效。
- 初始分箱时,若某个唯一值对应的样本量过少(如 < 5),建议先合并到相邻区间,避免列联表出现零频数。
- 实际应用中常结合业务约束:如每箱至少包含 5% 样本、单调性校验等,需在合并循环中额外判断。
4.4 卡方分箱 vs 其他分箱方法
| 对比维度 | 卡方分箱 | 等距分箱 | 等频分箱 | 决策树分箱 |
|---|---|---|---|---|
| 监督/无监督 | 监督 | 无监督 | 无监督 | 监督 |
| 目标变量 | 二分类 | 不需要 | 不需要 | 分类/回归 |
| 分箱依据 | 卡方统计量 | 等宽 | 等样本量 | 信息增益/基尼 |
| 处理异常值 | 鲁棒 | 敏感 | 较鲁棒 | 鲁棒 |
| 单调性保证 | 需额外约束 | 不保证 | 不保证 | 需额外约束 |
| 计算复杂度 | 中等 | 低 | 低 | 高 |
4.5 卡方分箱的工程实践要点
生产环境建议:
- 缺失值处理:将缺失值单独作为一个区间参与合并,或先填充再分箱。
- 单调性约束:在合并循环中增加坏账率单调性检查,若不单调则跳过当前合并。
- 最小样本量:每箱至少包含总样本的 5% 或至少 30 个样本,避免过拟合。
- 分箱数选择:信用评分通常 4-6 箱,风控模型 5-8 箱,根据业务可解释性需求调整。
- 性能优化:对大数据量(百万级),可先等频分箱到 100-200 箱作为初始分箱,再执行卡方合并。
卡方分箱是监督分箱中最经典的方法之一,它通过统计检验自动发现特征与目标的最优分段,在信用评分卡、客户流失预测、医疗诊断等二分类场景中广泛应用。掌握卡方分箱的原理和实现,是构建可解释性机器学习模型的重要基础。