3. 等距分箱实战:使用 pandas 的 cut 函数进行等距分箱,处理边界值与异常值

等距分箱(Equal-Width Binning)是最直观的连续变量离散化方法。它将数值范围划分为 宽度完全相等 的区间,每个区间包含的样本量可能不同。本实战将聚焦 pandas.cut 的核心用法、边界值处理策略以及异常值对分箱结果的破坏性影响。

3.1 等距分箱的核心逻辑

等距分箱的数学定义:给定特征 x 的最小值 min 与最大值 max,若分为 k 箱,则每个箱的宽度 w = (max - min) / k。分箱边界为:[min, min+w, min+2w, ..., max]。

特性 说明
箱宽一致 所有区间长度相同,便于业务解释(如年龄每10岁一组)
样本分布不均 数据密集区箱内样本多,稀疏区可能为空箱
对异常值敏感 单个极大/极小值会拉伸边界,导致大部分样本挤入少数箱
边界处理 默认左开右闭,需关注最小值是否被包含
适用场景: 分布相对均匀、无极端异常值、需要固定间隔解释(如收入区间、温度区间)的特征。

3.2 pandas.cut 函数详解

cut 是 pandas 提供的等距分箱主力函数。其核心参数如下:

参数 类型 作用
x array-like 待分箱的连续数据
bins int / sequence 整数表示箱数;序列表示自定义边界
right bool 区间是否右闭合(默认 True)
labels array / False 自定义箱标签;False 返回整数索引
include_lowest bool 第一个区间是否包含最小值(默认 False)
duplicates str 边界重复时的处理方式('raise' / 'drop')

3.3 基础实战:等距分箱代码

以下代码演示对模拟的“用户年龄”特征进行 5 箱等距分箱:

import pandas as pd
import numpy as np

# 生成模拟数据(含一个异常值)
np.random.seed(42)
ages = np.random.randint(18, 70, size=200)
ages = np.append(ages, [200])  # 故意加入异常值 200 岁

df = pd.DataFrame({'age': ages})

# 等距分箱:5箱
df['age_bin'] = pd.cut(df['age'], bins=5, right=True, include_lowest=True)

# 查看分箱结果分布
print(df['age_bin'].value_counts().sort_index())

输出示例(受异常值影响):

(17.7, 54.0]    152
(54.0, 90.4]     44
(90.4, 126.8]     3
(126.8, 163.2]    0
(163.2, 200.0]    1
异常值破坏性: 由于存在 200 岁的异常值,箱宽被拉伸至 (200-18)/5 ≈ 36.4,导致 18~54 岁区间挤占了 76% 的样本,而 126~163 岁区间完全为空。等距分箱对异常值极其敏感,分箱前必须做异常值截断或剔除。

3.4 边界值处理策略

等距分箱的边界值处理是实操中的关键细节,直接影响分箱的完整性与可解释性。

  • 最小值包含问题: 默认 right=True 且 include_lowest=False 时,最小值可能被排除。设置 include_lowest=True 确保第一个区间包含最小值。
  • 边界重叠: 当数据中存在重复边界值时,设置 duplicates='drop' 自动合并重复边界。
  • 自定义边界: 使用 bins=[0, 20, 40, 60, 100] 替代整数,可精确控制分箱节点。
  • 标签映射: 使用 labels=['青年','中年','中老年','老年'] 提升可读性。
# 推荐做法:先截断异常值,再等距分箱
age_cap = df['age'].clip(upper=80)  # 将大于80的值截断为80
df['age_bin_clean'] = pd.cut(age_cap, bins=4, 
                              labels=['青年','中年','中老年','老年'],
                              include_lowest=True)
print(df['age_bin_clean'].value_counts().sort_index())

3.5 等距分箱流程与异常值处理

下图展示了从原始数据到最终分箱的完整流程,重点突出异常值检测与截断环节:

等距分箱实战流程(含异常值处理) 原始连续特征 异常值检测(IQR/截断) 截断 / 剔除 / 盖帽法 确定箱数 / 边界 pd.cut() 执行分箱 关键参数设置 • include_lowest=True • right=True(默认) • duplicates='drop' 异常值处理后 重新计算边界

3.6 常见陷阱与避坑指南

  • 空箱问题: 等距分箱后出现样本数为 0 的区间。解决方案:减少箱数或改用分位数分箱。
  • 边界漂移: 训练集与测试集分别分箱导致边界不一致。解决方案:在训练集上拟合 bins,用 pd.cut(test, bins=train_bins) 统一映射。
  • 标签顺序: labels 参数必须与 bins 数量一致,且按升序排列。
  • 缺失值处理: cut 默认丢弃 NaN,需提前填充或单独编码为“缺失”箱。
生产环境建议: 将分箱边界保存为 JSON 或 pickle 文件,在模型部署时加载同一组边界进行转换,确保线上线下一致性。

3.7 等距分箱 vs 其他分箱方法

分箱方法 箱宽 样本分布 异常值敏感度 业务可解释性
等距分箱 相等 不均匀 高 高(固定间隔)
分位数分箱 不等 均匀 低 中(百分位)
聚类分箱 不等 依聚类 中 低(需解释簇)
自定义分箱 自定义 自定义 可控 最高