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或市值中性化)。若先标准化再去极值,极端值会污染均值和标准差,降低标准化效果。
数据预处理标准流程 原始因子值 去极值(MAD/百分位) 标准化(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序列更平稳,统计显著性更可靠。

预处理前后IC序列对比(示意) 预处理前 IC波动大,极端值干扰明显 预处理后 IC序列平稳,统计显著性提升 预处理后IC序列方差降低约40%~60%,IC_IR(信息比率)显著改善

4.5 预处理顺序与检查清单

为确保预处理流程可复现且无逻辑漏洞,建议按以下清单执行:

  1. 截面遍历:对每个时间截面(如每月末)独立执行以下步骤。
  2. 去极值:选择MAD法(n=5)或百分位法(1%/99%),记录截断比例。
  3. 标准化:先做Z-score标准化,再对市值做回归取残差(市值中性化)。
  4. 缺失值处理:检查缺失比例,若 < 5% 可删除;若 5%~20% 用截面中位数填充;若 > 20% 考虑删除该股票或该因子。
  5. 质量检查:验证预处理后因子均值接近0、标准差接近1,且与市值相关性接近0。
常见错误: 在全历史数据上统一做去极值和标准化,而不是在每个截面独立操作。这会导致未来数据影响过去截面,产生“前视偏差”,使IC分析结果虚高。务必按截面循环处理。