4. 等距分箱方法:原理与实现

等距分箱,说白了就是把特征的取值范围切成等宽的几段。你想想看,给年龄分箱,0~100岁,切5箱,那每箱就是20岁。0~20、20~40、40~60、60~80、80~100。简单粗暴,对吧?

我在项目里碰到过不少新手,一上手就想用复杂的卡方分箱或决策树分箱。其实很多时候,等距分箱就是那个最省事、最不容易出错的基线方案。尤其是特征分布比较均匀、业务上又没明显自然分割点时,我建议先用等距分箱试试水。

4.1 原理:等距的核心逻辑

等距分箱的数学定义就一句话:把特征值范围 [min, max] 等分成 k 个区间,每个区间宽度 = (max - min) / k。

听起来像废话?但这里面有个隐藏假设:特征值本身是有实际意义的,而且业务场景允许你忽略数据稀疏问题。比如年龄、额度、月份这些连续量,天然适合等距。但如果你要对「用户评分」这种偏态分布用等距分箱……嗯,那很可能大部分样本都挤在同一个箱子里,其他箱子近乎空箱。那样分箱就失去意义了。

4.2 实现步骤

我个人习惯按下面几步走:

  1. 找出特征的最小值和最大值。记得检查是否有异常值,尤其是那些离谱的极大值。
  2. 确定箱数 k。没有硬性规则,一般取5~20箱。信用评分卡里常见的是10~15箱。
  3. 计算区间宽度:width = (max - min) / k。
  4. 生成分割点:min + width、min + 2*width……直到 max。
  5. 用分割点将样本映射到各个箱子。

步骤很简单,但我曾经踩过一个坑:直接用 cut() 时,如果特征值正好等于某个分割点,pandas 默认是左闭右开区间,容易把边界值分到错误的箱子里。后面我会给你展示怎么处理。

小经验:边界处理可以用参数 right=False 或 include_lowest=True。我更喜欢显式自己定义 bins 列表,然后手动控制左闭右闭。

4.3 Python 实现

直接上代码。下面这个函数实现了等距分箱,并返回每个样本的箱号、箱区间标签以及箱的统计信息。

import numpy as np
import pandas as pd

def equal_width_binning(data, feature, k=10, right=True, precision=2):
    """
    等距分箱
    :param data: DataFrame
    :param feature: 特征名称
    :param k: 箱数
    :param right: 区间是否右闭
    :param precision: 标签小数位数
    :return: DataFrame (增加分箱标签列), bins 列表
    """
    s = data[feature].dropna()
    min_val, max_val = s.min(), s.max()
    # 避免因数值精度导致的边界问题,略扩展一点
    eps = 1e-10
    bins = np.linspace(min_val - eps, max_val + eps, k + 1)
    # 生成标签
    labels = []
    for i in range(k):
        left = round(bins[i], precision)
        right_b = round(bins[i+1], precision)
        if right:
            labels.append(f'({left}, {right_b}]')
        else:
            labels.append(f'[{left}, {right_b})')
    # 分箱
    data['bin_' + feature] = pd.cut(data[feature], bins=bins, labels=labels, right=right, include_lowest=True)
    # 统计每箱样本数(可选)
    stats = data.groupby('bin_' + feature)[feature].agg(['min', 'max', 'count']).reset_index()
    return data, bins, stats

# 示例使用
df = pd.DataFrame({'age': [22, 35, 48, 55, 64, 72, 81, 19, 27, 33, 45, 59, 68, 77, 85]})
df_result, bins, stats = equal_width_binning(df, 'age', k=5)
print(stats)

上面的代码里,我用 eps 把最小值往左挪了一点、最大值往右挪了一点,这样就避免了样本落在 bins 边界之外。你想想看,假如最小年龄正好是22,而左边界的 min_val 也是22,不加 eps 的话 22 这个值就会被 cut 排除在外(因为默认左开)。这个小技巧是我从业务上线后排查坏账时发现的,当时亏了我整整一下午。

4.4 等距分箱的优缺点

优点缺点
实现简单,代码就几行 对异常值敏感,一个超大离群点会把整个区间拉宽
分箱结果可解释性强 数据分布不均匀时有些箱子可能极稀疏
计算效率高,适合大样本 无法自动适应数据的分割点(比如业务天然拐点)
与其他分箱方法比较的天然基线 信用评分场景下通常不是最优分箱方式
避坑指南:我曾经在一个信用卡拒贷模型里直接用等距分箱处理「收入」特征,结果因为少数几个富豪的收入把区间拉到了数百万,导致绝大多数普通人全部挤在第一箱。后面换成分位数分箱才解决问题。所以等距分箱适合分布均匀、无离群点或已剔除离群点的特征。

4.5 等距分箱过程图解

下面这张图把整个流程走了一遍,从原始数据到分箱结果。你看着它,再对照代码,应该就能理解了。

等距分箱流程 原始特征值 计算 min / max 设定箱数 k width = (max-min)/k 生成 k+1 个分割点 pd.cut 映射 分箱结果

4.6 什么时候用等距分箱?

我个人的经验是:在信用评分卡项目初期,特征勘探阶段,我会用等距分箱快速看每个连续变量与坏账率的单调性关系。

  • 如果等距分箱后每个箱的样本数都大于500,而且坏账率单调递增或递减,那这个特征基本合格。
  • 如果某个箱子样本数小于100,我会标记为「等距不适用」,换成等频或卡方分箱试试。

这么做的好处是你不用花时间调参,五分钟就能筛一遍所有候选特征。等确定候选集以后,再对每个特征用更精细的分箱方法去优化。

一句话总结:等距分箱是信用评分卡特征工程里的「板手」——简单、可靠、在哪都能用,但不是所有螺丝都能用它拧。