4. 因子标准化

任务目标

对单期截面因子数据完成三条预处理链路:去极值(MAD法)、标准化(z-score)与缺失值填充(截面中位数),并验证处理后因子分布的稳定性。本案例使用随机生成的模拟截面数据,聚焦于处理逻辑本身,不涉及具体选股策略。

实现代码

import numpy as np
import pandas as pd

def mad_winsorize(series: pd.Series, n: float = 3.0) -> pd.Series:
    """MAD去极值:将超出中位数±n*1.4826*MAD的值截断至边界"""
    median = series.median()
    mad = (series - median).abs().median()
    if mad == 0:
        return series.copy()
    upper = median + n * 1.4826 * mad
    lower = median - n * 1.4826 * mad
    return series.clip(lower, upper)

def zscore_standardize(series: pd.Series) -> pd.Series:
    """z-score标准化:去均值后除以标准差"""
    return (series - series.mean()) / series.std()

def process_cross_section(df: pd.DataFrame, factor_col: str) -> pd.DataFrame:
    """
    对单期截面因子执行完整预处理
    流程:去极值 → 缺失值填充(中位数) → 标准化
    返回含原始列与处理列的新DataFrame
    """
    result = df.copy()
    raw = result[factor_col]

    # 1. 去极值(基于原始非缺失值计算边界)
    winsorized = mad_winsorize(raw.dropna())

    # 2. 缺失值填充:用去极值后的中位数填充
    fill_value = winsorized.median()
    filled = raw.fillna(fill_value)

    # 3. 标准化(对填充后的完整序列)
    standardized = zscore_standardize(filled)

    result[f'{factor_col}_winsorized'] = winsorized.reindex(result.index)
    result[f'{factor_col}_processed'] = standardized
    return result

# ========== 最小可复现示例 ==========
if __name__ == '__main__':
    # 构造含缺失值与极端值的模拟截面数据(50只股票)
    np.random.seed(42)
    n = 50
    raw_factor = np.random.normal(0, 1, n)
    raw_factor[5] = 15.0      # 人为极端值
    raw_factor[10] = -12.0    # 人为极端值
    raw_factor[[15, 20, 35]] = np.nan  # 人为缺失值

    df = pd.DataFrame({'stock_id': range(n), 'factor': raw_factor})

    # 执行预处理
    out = process_cross_section(df, 'factor')

    # 验证输出
    processed = out['factor_processed']
    print(f"处理前缺失数: {df['factor'].isna().sum()}")
    print(f"处理后缺失数: {processed.isna().sum()}")
    print(f"处理后均值: {processed.mean():.4f}")
    print(f"处理后标准差: {processed.std():.4f}")
    print(f"处理后最小值: {processed.min():.4f}")
    print(f"处理后最大值: {processed.max():.4f}")
    print("\n前5行结果:")
    print(out.head())

运行与验证

运行方式:直接执行脚本(需安装 numpy 与 pandas):

python factor_preprocess.py

通过条件:输出应满足以下全部判定:

  • 处理后缺失数为 0(缺失值已被中位数填充);
  • 处理后均值接近 0(|mean| < 0.05),标准差接近 1(0.9 ~ 1.1);
  • 处理后最大/最小值落在约 [-3, 3] 区间内(极端值已被截断至MAD边界);
  • 原始极端值(索引5、10)与缺失值(索引15、20、35)所在行均存在有效的 factor_processed 值。

关键说明

  • MAD系数1.4826:使MAD与正态分布下的标准差一致,保证去极值边界具有统计可比性。
  • 填充顺序:先基于非缺失值计算去极值边界,再用去极值后的中位数填充缺失,避免极端值污染填充值。
  • 标准化时机:必须在填充完成后进行,否则缺失值会导致均值和标准差计算偏差。
  • 多期截面扩展:本案例处理单期截面;若需处理多期,应按日期分组后逐期调用 process_cross_section,避免未来数据泄漏。
截面因子预处理流程 原始截面因子 含缺失与极端值 步骤1:MAD去极值 中位数 ± 3×1.4826×MAD 超出边界值截断至边界 仅基于非缺失值计算 步骤2:缺失值填充 用去极值后的中位数 填充NaN 避免极端值污染填充值 步骤3:z-score (x - mean) / std 填充后完整序列 均值≈0,标准差≈1 输出:稳定因子分布 无缺失 | 无极端值 | 均值0标准差1 验证通过条件 ✓ 缺失数 = 0 ✓ |均值| < 0.05 ✓ 标准差 ∈ [0.9, 1.1] ✓ 值域 ≈ [-3, 3]