3. 等频分箱:原理、实现与优缺点

等频分箱(Equal Frequency Binning)是变量分箱中最基础、最常用的方法之一。其核心思想是:将连续变量划分为若干个区间,使得每个区间内包含大致相同数量的样本。与等距分箱不同,等频分箱关注的是样本量的均匀分布,而非数值范围的均匀划分。

3.1 等频分箱原理

等频分箱通过分位数(Quantile)来确定箱的边界。对于给定的分箱数 \( k \),算法会计算 \( k-1 \) 个分位点,将数据划分为 \( k \) 个区间,每个区间包含约 \( \frac{N}{k} \) 个样本(\( N \) 为总样本数)。

核心公式: 第 \( i \) 个分位点 = 数据中第 \( \frac{i}{k} \times 100\% \) 分位数,其中 \( i = 1, 2, ..., k-1 \)。
💡 直观理解: 假设有 1000 个客户的年龄数据,要分成 5 箱。等频分箱会找到 4 个分位点(20%、40%、60%、80% 分位数),使得每箱恰好包含约 200 个客户。
等频分箱原理示意图(k=5) 原始数据(N=1000) 连续数值分布(年龄:18~80岁) Q1 (20%) Q2 (40%) Q3 (60%) Q4 (80%) 分箱结果(每箱约200个样本) 箱1: [18, 32) 箱2: [32, 42) 箱3: [42, 55) 箱4: [55, 65) 箱5: [65, 80] ⚠️ 注意:箱的宽度(数值范围)不同,但每个箱内的样本数量大致相等 年龄分布不均匀时,年轻段箱窄、年长段箱宽

3.2 实现步骤

等频分箱的实现步骤清晰且易于编程实现,主要包含以下 5 个步骤:

  1. 排序: 将待分箱的连续变量按数值从小到大排序。
  2. 计算分位点: 根据指定的分箱数 \( k \),计算 \( k-1 \) 个分位点的位置:\( pos_i = \frac{i}{k} \times N \),其中 \( i = 1, 2, ..., k-1 \)。
  3. 确定边界: 根据分位点位置,从排序后的数据中取出对应的数值作为箱边界。
  4. 划分区间: 使用这些边界将原始数据划分为 \( k \) 个区间(左闭右开或左闭右闭)。
  5. 映射编码: 将每个样本的原始值替换为所属箱的标签(如箱序号或箱均值)。
等频分箱步骤详解(以年龄变量为例,k=5)
步骤 操作 示例结果
1. 排序 对 1000 个年龄值升序排列 [18, 19, 20, ..., 78, 79, 80]
2. 计算分位点 计算 20%、40%、60%、80% 分位数位置 位置:200, 400, 600, 800
3. 确定边界 取出对应位置的数值 32, 42, 55, 65
4. 划分区间 生成 5 个区间 [18,32), [32,42), [42,55), [55,65), [65,80]
5. 映射编码 将每个年龄替换为箱标签 25岁 → "箱1", 50岁 → "箱3"

3.3 Python 代码实现(pandas.qcut)

在 Python 中,pandas.qcut() 函数是等频分箱的首选工具。它基于分位数将数据划分为大小相近的箱,并支持多种输出格式。

import pandas as pd
import numpy as np

# 生成示例数据:1000个年龄值(18~80岁)
np.random.seed(42)
ages = np.random.randint(18, 81, size=1000)
df = pd.DataFrame({'age': ages})

# 等频分箱:分成5箱
df['age_bin'], bins = pd.qcut(
    df['age'], 
    q=5,                     # 分箱数
    labels=['箱1', '箱2', '箱3', '箱4', '箱5'],  # 箱标签
    retbins=True,            # 返回边界值
    duplicates='drop'        # 处理重复边界
)

print("分箱边界值:", bins)
print("\n各箱样本数量:")
print(df['age_bin'].value_counts().sort_index())

# 查看分箱结果
print("\n前10行数据:")
print(df.head(10))

输出示例:

分箱边界值: [18. 32. 42. 55. 65. 80.]

各箱样本数量:
箱1    200
箱2    200
箱3    200
箱4    200
箱5    200
Name: age_bin, dtype: int64

前10行数据:
   age age_bin
0   56     箱4
1   46     箱3
2   32     箱2
3   25     箱1
4   77     箱5
5   21     箱1
6   48     箱3
7   64     箱4
8   55     箱3
9   58     箱4
🔧 关键参数说明:
  • q:可以是整数(分箱数)或分位数列表(如 [0, 0.2, 0.4, 0.6, 0.8, 1.0])。
  • labels:自定义箱标签,长度必须等于分箱数。
  • retbins=True:返回箱边界值,便于后续分析或复用。
  • duplicates='drop':当数据中存在大量重复值时,自动合并边界相同的箱。

3.4 优缺点分析

等频分箱优缺点对比
维度 优点 缺点
样本分布 每箱样本量均衡,避免出现空箱或极少数样本的箱 当数据存在大量重复值时,可能无法实现严格等频
模型适配 对树模型(如 XGBoost)友好,每箱都有足够样本训练 箱的数值范围不固定,可能将差异很大的值分到同一箱
可解释性 分箱边界基于分位数,易于理解和沟通 边界值可能不是整数,不够直观(如年龄分箱边界为 32.5)
异常值处理 异常值会被自动分到首尾箱,不影响其他箱的分布 极端异常值可能导致首尾箱范围过大
实现复杂度 实现简单,一行代码即可完成 无法融入业务知识,边界可能不符合实际意义
⚠️ 常见陷阱:
  • 重复值问题: 当某个数值出现频率过高时,qcut 可能报错或产生边界相同的箱。此时应设置 duplicates='drop' 或改用等距分箱。
  • 分箱数选择: 分箱数过多会导致每箱样本太少,模型不稳定;过少则信息损失严重。建议根据样本量选择 4~10 箱。
  • 边界精度: 默认边界为浮点数,可通过 precision 参数控制小数位数。

3.5 等频分箱 vs 等距分箱

为了更清晰地理解等频分箱的特点,下面将其与等距分箱进行对比:

等频分箱 vs 等距分箱
对比维度 等频分箱 等距分箱
划分依据 样本量均匀 数值范围均匀
箱宽度 可变(数据密集处窄,稀疏处宽) 固定(所有箱宽度相同)
每箱样本量 大致相等 可能差异很大
对异常值敏感度 低(异常值被归入首尾箱) 高(异常值可能独占一箱)
适用场景 数据分布偏态严重、树模型 数据分布均匀、线性模型
Python 实现 pd.qcut() pd.cut()
等频分箱 vs 等距分箱对比(偏态分布数据) 等频分箱 每箱宽度不同,样本量相同 数据密集处箱窄,稀疏处箱宽 等距分箱 每箱宽度相同,样本量不同 数据密集处样本多,稀疏处样本少 数据分布曲线(偏态分布) 选择建议:偏态分布用等频分箱,均匀分布用等距分箱

3.6 实战建议

  • 分箱数选择: 对于 XGBoost 建模,建议分箱数在 4~8 之间。过少会丢失信息,过多会导致过拟合。
  • 边界检查: 分箱后务必检查每箱的样本量,确保没有出现样本量过少的箱(如少于总样本的 5%)。
  • 业务对齐: 等频分箱的边界可能不符合业务常识(如年龄分箱边界为 32.5 岁),此时可手动调整边界到整数。
  • 与 WOE 编码配合: 等频分箱后通常结合 WOE(Weight of Evidence)编码,用于逻辑回归或评分卡模型。
📌 核心要点:
  • 等频分箱保证每箱样本量大致相等,适合偏态分布和树模型。
  • Python 中使用 pd.qcut() 一行代码实现。
  • 注意处理重复值问题,避免分箱失败。
  • 等频分箱是后续 WOE 编码和 XGBoost 建模的重要预处理步骤。