问题描述
如果因子处理顺序是:
中位数去极值-》中性化-》标准化
对因子去极值时
winsorize_med(df_x,scale=1,inclusive=True,inf2nan=False,axis=1)
scale取1的话,会导致大量因子都是边界值。
后面传给 analyze_factor 做因子分析就报错了。
错误 'Requested level (factor_quantile) does not match index name (None)'
请问 scale 一般设置多少?
除了修改 scale 大小外,有没有其他办法能解决个问题?
解决方案
报错原因分析
在传给 analyze_factor 做单因子分析时,底层(通常是 Alphalens)会使用 pandas.qcut 将因子值划分为 N 个分位数(Quantiles)。
当你使用 winsorize_med 且 scale=1 时,去极值的边界非常窄,导致大量因子被强制赋值为相同的边界值。当大量相同的值堆积在边界时,qcut 无法生成唯一的分位数边界(即分位数切分点重叠),从而引发底层的索引错误:Requested level (factor_quantile) does not match index name (None)。
问题一:scale 一般设置多少?
在量化金融中,使用中位数绝对偏差(MAD,Median Absolute Deviation)去极值时,scale 通常设置为 3 或 5(即常说的 3倍MAD法 或 5倍MAD法)。
- scale = 3:较为常用,能剔除明显的异常值,同时保留绝大部分有效分布。
- scale = 5:相对宽松,只剔除极端离谱的异常值。
- scale = 1:极其严格,会把大量正常波动的数据视为“极值”并强行拉平,这不仅会导致分位数划分报错,还会严重破坏因子本身的有效信息和分布特征。
建议修改代码为:
winsorize_med(df_x, scale=3, inclusive=True, inf2nan=False, axis=1)
问题二:除了修改 scale 大小外,还有没有其他解决办法?
如果由于某些特殊原因,因子本身就存在大量重复值,或者你不想单纯依赖调整 scale,可以尝试以下几种替代方案:
1. 改用分位数去极值(推荐)
相比于 MAD 法,分位数去极值(Percentile Clipping)更容易控制边界值的比例,不会因为分布的偏度导致单侧堆积过多数据。可以使用聚宽的 winsorize 函数:
from jqfactor import winsorize
# 将上下 2.5% 的数据拉平到边界
winsorize(df_x, qrange=[0.025, 0.975], inclusive=True, inf2nan=False, axis=1)
2. 将极值设为 NaN 而不是边界值
如果你认为极值是错误数据(如财务造假或数据异常),而不是“表现极好/极差”的股票,可以将 inclusive 设置为 False。这样极值会被替换为 NaN,在后续处理中被丢弃,从而避免大量相同边界值的堆积:
winsorize_med(df_x, scale=3, inclusive=False, inf2nan=False, axis=1)
3. 减少 analyze_factor 的分位数数量
如果因子值重复较多,切分 5 组或 10 组容易导致边界重叠。你可以尝试在调用 analyze_factor 时,减少 quantiles 的数量(例如从默认的 5 降到 3):
far = analyze_factor(factor=df_x, quantiles=3, ...)
4. 添加微小的随机噪音(Jitter)
如果因子本身离散度极低(例如某些哑变量因子或打分因子只有几个固定整数),可以通过给因子值加上极小的随机数来打破平局(Tie-breaking),使 qcut 能够成功切分:
import numpy as np
# 添加极小的噪音,不影响因子原本的排序逻辑
noise = np.random.normal(0, 1e-8, df_x.shape)
df_x = df_x + noise
总结
最标准且最合理的做法是将 scale 调整为 3 或 5。scale=1 破坏了因子的截面分布,是导致该报错的根本原因。