数据预处理基础:缺失值处理、异常值识别与处理、数据标准化与归一化
在量化研究与因子挖掘流程中,原始数据几乎不可能直接用于建模。缺失值、异常值以及量纲不统一是三大最常见的“数据污染”来源。本章系统讲解这三类问题的诊断方法与处理技术,确保后续变量衍生与分箱操作建立在干净、稳定的数据基础之上。
2.1 缺失值处理
缺失值产生的原因包括:数据采集设备故障、传输丢包、人工录入遗漏、或某些指标在特定条件下天然不存在(如停牌期间的交易量)。处理缺失值前,必须先判断其缺失机制。
| 缺失机制 | 定义 | 典型场景 | 处理策略 |
|---|---|---|---|
| 完全随机缺失(MCAR) | 缺失与否完全独立于任何观测值 | 传感器随机故障 | 删除或简单插补 |
| 随机缺失(MAR) | 缺失依赖于其他已观测变量 | 高波动率日更容易缺失报价数据 | 模型插补(回归/多重插补) |
| 非随机缺失(MNAR) | 缺失依赖于缺失变量本身 | 亏损严重的股票更可能不披露财务细节 | 需引入外部信息或专用模型 |
2.1.1 缺失值诊断
使用 pandas 进行快速诊断:
import pandas as pd
import numpy as np
# 生成示例数据
df = pd.DataFrame({
'close': [100, 102, np.nan, 105, 107],
'volume': [1e6, np.nan, 1.2e6, 1.1e6, np.nan],
'pe_ratio': [15, 16, 15.5, np.nan, 14.8]
})
# 缺失值统计
missing_count = df.isnull().sum()
missing_ratio = df.isnull().mean() * 100
print("缺失数量:\n", missing_count)
print("\n缺失比例(%):\n", missing_ratio)
2.1.2 常用处理方法
- 删除法:适用于 MCAR 且缺失比例 < 5% 的场景。使用
df.dropna()按行或按列删除。 - 固定值填充:对价格类数据可用前一日收盘价(
ffill);对比率类数据可用行业均值或中位数。 - 统计量插补:均值、中位数、众数填充。注意:均值对异常值敏感,金融数据优先使用中位数。
- 模型插补:KNN、线性回归、随机森林等利用其他特征预测缺失值。
量化实战建议:对于时间序列因子(如动量、波动率),优先使用前向填充(ffill)或线性插值(interpolate),避免引入未来信息。
# 前向填充与线性插值示例
df['close_ffill'] = df['close'].ffill()
df['volume_interp'] = df['volume'].interpolate(method='linear')
print(df[['close', 'close_ffill', 'volume', 'volume_interp']])
2.2 异常值识别与处理
异常值(Outliers)是指显著偏离数据整体分布的观测点。在量化因子中,异常值可能来自数据录入错误、极端市场事件(如闪崩),或真实的稀有事件。不加处理直接建模,会严重扭曲因子与收益之间的关系。
2.2.1 异常值识别方法
| 方法 | 原理 | 适用场景 | 参数 |
|---|---|---|---|
| Z-Score 法 | 假设正态分布,超过均值±3σ视为异常 | 近似正态分布的因子 | 阈值通常取 3 |
| IQR 四分位距法 | Q1 - 1.5×IQR 以下或 Q3 + 1.5×IQR 以上 | 偏态分布、含厚尾的金融数据 | 系数 1.5(极端用 3) |
| MAD 法 | 基于中位数绝对偏差,稳健性强 | 存在多个异常值或分布严重偏斜 | MAD 乘数通常取 3~5 |
# IQR 法识别异常值
def detect_outliers_iqr(series, multiplier=1.5):
Q1 = series.quantile(0.25)
Q3 = series.quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - multiplier * IQR
upper_bound = Q3 + multiplier * IQR
return (series < lower_bound) | (series > upper_bound)
# 示例
data = pd.Series([10, 12, 11, 13, 100, 12, 11, 14, 9, 200])
outliers = detect_outliers_iqr(data)
print("异常值索引:", data[outliers].index.tolist())
print("异常值:", data[outliers].values)
2.2.2 异常值处理策略
- 截尾(Winsorization):将异常值替换为边界值(如 99% 分位数),保留样本量且减少极端值影响。
- 删除:仅当确认异常值来自数据错误且比例极低时使用。
- 缺失化后插补:先将异常值标记为缺失,再用缺失值处理方法填充。
注意:不要对训练集和测试集分别计算分位数边界。应在训练集上计算截尾阈值,并应用到测试集,避免数据泄露。
# 截尾处理示例
def winsorize_series(series, lower=0.01, upper=0.99):
q_low = series.quantile(lower)
q_high = series.quantile(upper)
return series.clip(lower=q_low, upper=q_high)
df['pe_winsorized'] = winsorize_series(df['pe_ratio'], lower=0.05, upper=0.95)
量化实战对比:Z-Score 法对极端值敏感(均值和标准差本身会被异常值拉偏),而 IQR 和 MAD 法基于分位数或中位数,稳健性更强。在因子预处理中,推荐优先使用 IQR 法或 MAD 法。
2.3 数据标准化与归一化
不同因子往往具有不同的量纲和数值范围(如市盈率 10~50,成交量 10^6~10^9)。标准化与归一化将因子映射到统一尺度,避免量纲差异主导模型训练,同时提升梯度下降类算法的收敛速度。
2.3.1 核心方法对比
| 方法 | 公式 | 输出范围 | 对异常值敏感度 | 适用模型 |
|---|---|---|---|---|
| Z-Score 标准化 | \( z = \frac{x - \mu}{\sigma} \) | 无固定范围,均值为 0,标准差为 1 | 高 | 线性回归、SVM、PCA、神经网络 |
| Min-Max 归一化 | \( x' = \frac{x - x_{min}}{x_{max} - x_{min}} \) | [0, 1] | 极高 | 距离类模型(KNN、K-Means) |
| Robust 标准化 | \( x' = \frac{x - median}{IQR} \) | 无固定范围,中位数为 0 | 低 | 含较多异常值的金融因子 |
| Rank 排序归一化 | 将原始值替换为百分位排名 | [0, 1] 或 [1, N] | 不敏感 | 非参数模型、因子组合 |
from sklearn.preprocessing import StandardScaler, MinMaxScaler, RobustScaler
# 示例数据
factor_data = [[100], [200], [150], [300], [1000]] # 含异常值 1000
# Z-Score 标准化
scaler_std = StandardScaler()
z_scores = scaler_std.fit_transform(factor_data)
print("Z-Score:\n", z_scores)
# Robust 标准化
scaler_robust = RobustScaler()
robust_scores = scaler_robust.fit_transform(factor_data)
print("Robust:\n", robust_scores)
量化实战建议:在因子分箱前,推荐使用 Rank 排序归一化或 Robust 标准化。这两种方法对异常值不敏感,且能保留因子的相对排序信息,非常适合截面因子处理。
2.4 数据预处理流程总览
下图展示了从原始数据到干净、标准化因子的完整预处理流水线:
2.5 预处理顺序与注意事项
- 先处理缺失值,再识别异常值:缺失值会导致统计量(均值、标准差、分位数)失真,影响异常值判断。
- 标准化/归一化放在最后:缺失值填充和异常值截尾会改变数据分布,应在分布稳定后再进行尺度变换。
- 截面与时间序列的区别:截面因子(如市值、市盈率)可在每个时间截面上独立标准化;时间序列因子(如过去 20 日收益率)应使用滚动窗口标准化,避免未来信息。
- 保存变换参数:对训练集拟合的标准化参数(均值、标准差、分位数等)必须保存,用于后续测试集和线上推理。
实战检查清单:
✅ 缺失比例 < 30% 时优先插补,否则考虑删除该特征。
✅ 异常值截尾边界在训练集上计算,固定后应用到全量数据。
✅ 标准化方法选择与模型类型匹配:树模型不需要标准化,线性模型和神经网络必须标准化。
✅ 每次预处理后绘制分布直方图或箱线图,确认数据形态变化。
✅ 缺失比例 < 30% 时优先插补,否则考虑删除该特征。
✅ 异常值截尾边界在训练集上计算,固定后应用到全量数据。
✅ 标准化方法选择与模型类型匹配:树模型不需要标准化,线性模型和神经网络必须标准化。
✅ 每次预处理后绘制分布直方图或箱线图,确认数据形态变化。
完成本章的数据预处理后,因子数据将具备完整性、稳健性和可比性,为后续的变量衍生与因子分箱奠定坚实基础。