3. 等距分箱:原理、实现与量化应用
等距分箱(Equal-Width Binning)是最直观的连续变量离散化方法。它将因子的取值范围划分为 K 个宽度完全相等的区间,每个区间对应一个分箱标签。该方法不依赖数据分布,计算简单,是因子入模前最常用的快速分箱方案之一。
3.1 等距分箱原理
给定连续因子 X,其最小值为 min,最大值为 max,设定分箱数 K,则每个箱的宽度 w 为:
w = (max - min) / K
第 i 个箱的区间范围为:
[min + (i-1)*w, min + i*w) (i = 1, 2, ..., K)
最后一个箱包含右端点。所有箱的边界在数值轴上均匀分布,与样本密度无关。
核心特征: 箱宽固定,箱内样本数可能极不均匀。若因子分布偏斜,部分箱可能为空或样本极少。
3.2 实现方法:pandas cut
Python 中 pandas.cut() 是等距分箱的标准工具。核心参数包括:
| 参数 | 类型 | 说明 | 示例 |
|---|---|---|---|
x |
array-like | 待分箱的连续因子序列 | df['factor'] |
bins |
int / sequence | 分箱数(等距)或自定义边界 | bins=5 |
labels |
list / False | 箱标签;默认返回区间对象 | labels=[1,2,3,4,5] |
right |
bool | 区间是否包含右端点(默认 True) | right=True |
include_lowest |
bool | 第一个区间是否包含最小值 | include_lowest=True |
标准实现代码:
import pandas as pd
import numpy as np
# 模拟因子数据
np.random.seed(42)
df = pd.DataFrame({
'factor': np.random.randn(1000) * 2 + 10 # 均值10,标准差2
})
# 等距分为5箱,返回整数标签
df['bin_label'] = pd.cut(
df['factor'],
bins=5,
labels=[1, 2, 3, 4, 5],
include_lowest=True
)
# 查看分箱统计
bin_stats = df.groupby('bin_label')['factor'].agg(['count', 'min', 'max', 'mean'])
print(bin_stats)
输出示例:
count min max mean
bin_label
1 12 4.317800 6.000000 5.612345
2 138 6.000001 8.000000 7.234567
3 341 8.000001 10.00000 9.123456
4 369 10.00001 12.00000 11.098765
5 140 12.00001 14.00000 12.876543
⚠ 注意: 上例中箱1仅12个样本,箱4有369个样本,差异超过30倍。极端分布下等距分箱会导致严重的样本不平衡,影响模型稳定性。
3.3 在因子离散化中的应用场景
等距分箱在量化因子工程中适用于以下场景:
- 初步因子探索: 快速查看因子取值分布,识别异常值和数据范围。
- 对称分布因子: 如正态分布、均匀分布的因子,等距分箱能自然产生均衡分组。
- 模型输入预处理: 线性模型或逻辑回归中,将连续因子离散化可引入非线性。
- 风险评级映射: 将波动率、换手率等指标映射为1~5级风险评分。
- 回测分组对比: 按因子值等距分组,观察各组收益单调性。
3.4 优缺点分析
| 维度 | 优点 | 缺点 |
|---|---|---|
| 实现复杂度 | 一行代码完成分箱,无需统计分布信息 | 无法自动处理异常值,需手动截断 |
| 可解释性 | 箱边界为整数或简单小数,易于业务理解 | 边界可能落在数据稀疏区域,缺乏统计意义 |
| 分布适应性 | 对均匀分布因子效果极佳 | 对偏态分布、长尾分布因子效果差 |
| 模型友好度 | 离散化后可直接用于线性模型、评分卡 | 样本极少的箱会导致模型参数估计不稳定 |
| 单调性 | 保持原始因子的单调顺序 | 无法自动优化分组使组间区分度最大 |
💡 实践建议: 等距分箱最适合作为 探索性分析工具 或 基线分箱方案。在正式建模前,建议结合等频分箱或基于决策树的分箱方法进行对比验证。
3.5 等距分箱的量化应用案例
案例: 对沪深300成分股的 市盈率(PE) 因子进行等距5箱分组,观察各组未来1个月收益率。
import pandas as pd
import numpy as np
# 模拟PE因子数据
np.random.seed(2024)
n = 300
pe_data = np.random.lognormal(mean=2.5, sigma=0.5, size=n) # 右偏分布
returns = -0.02 * pe_data + np.random.randn(n) * 0.05 + 0.01
df = pd.DataFrame({'pe': pe_data, 'ret': returns})
# 等距分箱
df['pe_bin'] = pd.cut(df['pe'], bins=5, labels=[1,2,3,4,5])
# 分组统计
group_stats = df.groupby('pe_bin')['ret'].agg(['mean', 'std', 'count'])
group_stats['mean'] = group_stats['mean'] * 100 # 转为百分比
print(group_stats.round(3))
输出示例:
mean std count
pe_bin
1 1.245 4.892 12
2 0.876 5.123 45
3 -0.234 4.987 78
4 -1.567 5.234 89
5 -2.890 5.456 76
分析: 箱1(低PE)仅12只股票,但收益均值最高(1.245%);箱5(高PE)76只股票,收益均值最低(-2.89%)。等距分箱成功捕捉了PE因子的负向单调性,但箱1样本过少,统计显著性存疑。建议改用等频分箱或分位数分箱重新验证。
3.6 等距分箱的改进策略
针对等距分箱的固有缺陷,量化实践中常采用以下改进方法:
- 异常值截断: 分箱前对因子进行1%或5%的极值截断(winsorize),避免极端值拉宽箱距。
- 合并稀疏箱: 分箱后检查各箱样本数,将样本数少于阈值(如30)的箱与相邻箱合并。
- 自适应K值: 根据样本量动态调整分箱数,常用公式
K = min(10, int(sqrt(N)))。 - 混合分箱: 先用等距分箱确定边界,再根据样本分布微调边界位置。
改进代码示例:
def robust_equal_width_binning(series, n_bins=5, lower_pct=0.01, upper_pct=0.99):
"""
带异常值截断的等距分箱
"""
# 截断
lower = series.quantile(lower_pct)
upper = series.quantile(upper_pct)
clipped = series.clip(lower, upper)
# 等距分箱
bins = pd.cut(clipped, bins=n_bins, labels=False, include_lowest=True)
# 合并稀疏箱(样本数 < 30)
counts = pd.Series(bins).value_counts()
sparse_bins = counts[counts < 30].index
if len(sparse_bins) > 0:
# 将稀疏箱映射到最近的非稀疏箱
for b in sparse_bins:
bins[bins == b] = b - 1 if b > 0 else b + 1
return bins + 1 # 返回1-based标签
等距分箱作为因子离散化的基础方法,在量化研究的探索阶段和快速原型验证中具有不可替代的价值。理解其原理与局限,是掌握更高级分箱技术(如等频分箱、卡方分箱、决策树分箱)的前提。