4. 数据清洗与预处理:去极值、标准化与缺失值处理
原始因子数据中常包含极端值、量纲差异和缺失记录,这些噪声会严重扭曲IC序列的统计特征,导致因子有效性误判。本章系统讲解三项核心预处理操作:去极值(MAD法与百分位法)、标准化(Z-score与市值中性化)以及缺失值处理(填充与删除),并给出实战中的操作顺序与注意事项。
4.1 去极值:消除尾部噪声
极端值(outliers)会使IC计算中个别截面相关系数被过度拉高或拉低,破坏IC序列的平稳性。常用方法有两种:MAD法(中位数绝对偏差)和百分位法(分位数截尾)。
| 方法 | 原理 | 阈值设定 | 适用场景 |
|---|---|---|---|
| MAD法 | 基于中位数与绝对中位差,对异常值进行截断或缩尾 | 通常取 3×MAD 或 5×MAD | 因子分布偏态较强时更稳健 |
| 百分位法 | 直接按分位数截断,如上下1%或5% | 1%/99% 或 2.5%/97.5% | 分布对称、样本量大时计算简单 |
实战建议: 对于市值因子、波动率因子等尾部极厚的因子,优先使用MAD法;对于分布相对对称的估值类因子,百分位法效率更高。两种方法均需在截面内(每个时间点)独立执行,避免未来信息泄漏。
# Python示例:MAD法去极值(缩尾处理)
import numpy as np
def winsorize_mad(series, n=5):
median = np.median(series)
mad = np.median(np.abs(series - median))
upper = median + n * mad
lower = median - n * mad
return np.clip(series, lower, upper)
# 示例:对某截面因子值做5倍MAD缩尾
factor_clean = winsorize_mad(raw_factor, n=5)
关键区别: MAD法使用中位数而非均值,对异常值本身不敏感,因此比基于均值的“3σ法”更稳健。当因子分布存在厚尾时,MAD法不会因极端值而大幅偏移截断阈值。
4.2 标准化:消除量纲与风格暴露
标准化使不同因子处于同一量纲,便于IC横向比较。但仅做Z-score标准化会保留因子与市值等风格变量的相关性,因此需要进一步做市值中性化。
4.2.1 Z-score标准化
将因子值转换为均值为0、标准差为1的分布:
def zscore_standardize(series):
return (series - series.mean()) / series.std()
标准化后的因子IC可直接比较:IC绝对值越大,因子预测能力越强。但Z-score未剔除市值等风格变量的影响,可能导致IC中包含“伪相关性”。
4.2.2 市值中性化
通过回归或分组正交,剔除因子中与市值相关的部分,使因子在大小市值股票间“公平”选股。
# 市值中性化:对因子值做横截面回归,取残差
import statsmodels.api as sm
def market_neutralize(factor, market_cap):
X = sm.add_constant(np.log(market_cap)) # 市值取对数
model = sm.OLS(factor, X).fit()
residual = model.resid
return residual
注意: 市值中性化必须在每个截面独立进行。若使用全历史数据回归,会引入未来信息。中性化后的因子IC反映的是“剔除市值影响后”的纯选股能力,常用于多因子组合构建。
标准化顺序: 先做去极值,再做标准化(Z-score或市值中性化)。若先标准化再去极值,极端值会污染均值和标准差,降低标准化效果。
4.3 缺失值处理:填充与删除
因子数据中缺失值(NaN)的来源包括:停牌、新股上市、数据源不完整等。处理策略分为删除和填充两大类,选择依据是缺失比例与缺失模式。
| 策略 | 方法 | 适用条件 | 风险 |
|---|---|---|---|
| 删除 | 删除缺失样本(行)或缺失截面(时间点) | 缺失比例 < 5%,且随机缺失 | 样本量减少,可能引入生存偏差 |
| 填充 | 截面均值/中位数填充 | 缺失比例 5%~20%,因子分布稳定 | 压缩方差,降低因子区分度 |
| 填充 | 行业均值填充 | 因子存在行业效应(如估值因子) | 需确保行业分类稳定 |
| 填充 | 前向填充(时间序列) | 因子值随时间缓慢变化(如市值) | 不适用于突变型因子 |
# 缺失值处理示例
import pandas as pd
# 截面中位数填充
factor_filled = factor_raw.fillna(factor_raw.median())
# 行业均值填充(假设有行业分类列 industry)
factor_filled = factor_raw.groupby('industry').transform(lambda x: x.fillna(x.mean()))
# 删除缺失比例超过20%的股票
valid_stocks = factor_raw.dropna(thresh=0.8 * len(factor_raw.columns))
实战经验: 对于IC分析,建议优先使用截面中位数填充,因为它对异常值不敏感,且能保留样本量。若某只股票在多个因子中同时缺失,应考虑直接删除该股票,避免填充带来的虚假信号。
4.4 预处理对IC的影响
未经预处理的因子IC往往被极端值扭曲,表现为IC序列方差过大、正负交替频繁。经过完整预处理后,IC序列更平稳,统计显著性更可靠。
4.5 预处理顺序与检查清单
为确保预处理流程可复现且无逻辑漏洞,建议按以下清单执行:
- 截面遍历:对每个时间截面(如每月末)独立执行以下步骤。
- 去极值:选择MAD法(n=5)或百分位法(1%/99%),记录截断比例。
- 标准化:先做Z-score标准化,再对市值做回归取残差(市值中性化)。
- 缺失值处理:检查缺失比例,若 < 5% 可删除;若 5%~20% 用截面中位数填充;若 > 20% 考虑删除该股票或该因子。
- 质量检查:验证预处理后因子均值接近0、标准差接近1,且与市值相关性接近0。
常见错误: 在全历史数据上统一做去极值和标准化,而不是在每个截面独立操作。这会导致未来数据影响过去截面,产生“前视偏差”,使IC分析结果虚高。务必按截面循环处理。