特征工程基础:单因子分析、多因子共线性处理、标准化与归一化

各位同学,咱们今天聊点实在的。

做量化因子挖掘,说白了就是跟数据打交道。你挖出来的因子好不好,能不能用,第一步就看特征工程做得怎么样。我见过太多人,因子逻辑听起来挺漂亮,一上实盘就崩——十有八九是特征工程这步没走稳。

今天这一章,咱们把三个核心问题讲透:单因子怎么分析、多因子共线性怎么处理、数据标准化归一化怎么做。嗯,都是基本功,但基本功往往最要命。

一、单因子分析:先看看你的因子有没有“料”

我个人习惯,拿到一个新因子,第一件事不是跑模型,而是做单因子分析。说白了,就是看看这个因子单独跟收益率有没有关系。

怎么做?我一般看三个维度:

  • IC(信息系数):因子值与未来收益的秩相关性。IC均值绝对值大于0.02,就算有点意思了。
  • IR(信息比率):IC均值除以IC标准差。IR大于0.5,说明因子比较稳定。
  • 分组收益:把股票按因子值分成10组,看多空组合的收益曲线。

核心经验:IC高但IR低,说明因子时灵时不灵,容易过拟合。我宁愿要一个IC=0.03但IR=0.8的因子,也不要IC=0.08但IR=0.2的。

举个代码例子,咱们用Python算一下IC:

import pandas as pd
import numpy as np
from scipy.stats import spearmanr

# 假设 factor_df 是因子值,return_df 是未来收益率
def calc_ic(factor_df, return_df):
    ic_series = pd.Series(index=factor_df.index)
    for date in factor_df.index:
        # 每个截面做秩相关
        ic, _ = spearmanr(factor_df.loc[date], return_df.loc[date])
        ic_series[date] = ic
    return ic_series

# 使用示例
ic_values = calc_ic(factor_data, forward_returns)
print(f"IC均值: {ic_values.mean():.4f}")
print(f"IC标准差: {ic_values.std():.4f}")
print(f"IR: {ic_values.mean() / ic_values.std():.4f}")

我在项目中遇到过,有个因子IC均值0.05,看着不错,但IR只有0.1。后来一查,原来是某几个月IC突然变负,把整体拉垮了。所以,单因子分析一定要看时间序列,别只看均值。

小技巧:画个IC的累积曲线,比看数字直观得多。如果曲线像心电图一样乱跳,那这个因子基本靠不住。

二、多因子共线性处理:别让你的因子“打架”

你想想看,如果你同时用市盈率和市净率,这两个因子相关性通常很高。模型会困惑:到底该信谁?这就是共线性问题。

共线性的危害很明显:

  • 模型系数不稳定,换一个样本区间结果就大变
  • 解释力下降,你分不清哪个因子真正起作用
  • 泛化能力差,实盘容易翻车

怎么检测?我一般看两个指标:

指标 阈值 说明
相关系数矩阵 |r| > 0.7 两两相关性过高,需要处理
VIF(方差膨胀因子) VIF > 10 该因子与其他因子存在严重共线性

处理方式,我常用的有三种:

  1. 直接剔除:保留逻辑更清晰的那个因子。比如市盈率和市净率,我一般留市盈率。
  2. PCA降维:把相关因子合成几个主成分。但缺点是解释性变差。
  3. 正则化:用Lasso或Ridge回归,自动压缩不重要因子的系数。

注意:千万不要为了降低共线性而盲目剔除因子。我曾经犯过这个错,把一些有独立信息但相关性稍高的因子删了,结果模型收益直接腰斩。先做业务理解,再做技术处理。

代码实现VIF计算:

from statsmodels.stats.outliers_influence import variance_inflation_factor
from sklearn.preprocessing import StandardScaler

def calc_vif(df):
    # 标准化后计算VIF
    scaler = StandardScaler()
    df_scaled = scaler.fit_transform(df)
    vif_data = pd.DataFrame()
    vif_data["feature"] = df.columns
    vif_data["VIF"] = [variance_inflation_factor(df_scaled, i) 
                       for i in range(df_scaled.shape[1])]
    return vif_data

# 假设 factor_matrix 是多个因子的DataFrame
vif_result = calc_vif(factor_matrix)
print(vif_result[vif_result["VIF"] > 10])

三、标准化与归一化:让因子站在同一起跑线

这个问题其实很简单。你的因子有的在0-1之间,有的在几百万的量级,直接扔进模型,量级大的因子会主导训练过程。这不公平。

我常用的方法:

  • Z-score标准化:减去均值,除以标准差。适合数据近似正态分布的情况。
  • Min-Max归一化:缩放到[0,1]区间。适合有明确上下界的数据。
  • Rank归一化:把因子值转为百分位排名。对异常值最鲁棒,我个人最常用。

为什么我偏爱Rank归一化?

因为金融数据里异常值太多了。某一天一个股票因为乌龙指暴涨,因子值直接飞上天。如果用Z-score,这个异常值会严重扭曲分布。但Rank归一化只关心排序,异常值最多排到第1名,影响可控。

我的经验:做截面标准化时,一定要按时间分组处理。也就是每个交易日单独做标准化,不要跨时间做。否则会引入未来信息,造成严重的过拟合。

代码实现:

def rank_normalize(series):
    """Rank归一化,将因子值转为0-1之间的百分位"""
    return series.rank(pct=True)

def zscore_normalize(series):
    """Z-score标准化"""
    return (series - series.mean()) / series.std()

# 截面处理示例
def cross_sectional_normalize(df, method='rank'):
    """对每个时间截面做标准化"""
    df_norm = df.copy()
    for date in df.index:
        if method == 'rank':
            df_norm.loc[date] = rank_normalize(df.loc[date])
        elif method == 'zscore':
            df_norm.loc[date] = zscore_normalize(df.loc[date])
    return df_norm

本章知识体系

下面这张图,把咱们今天讲的内容串起来了:

特征工程基础:知识体系 特征工程 单因子分析 IC分析 IR分析 分组收益 共线性处理 相关系数 VIF检测 PCA/正则化 标准化与归一化 Z-score Min-Max Rank归一化 核心原则 截面处理 · 避免未来信息 · 业务理解优先

嗯,以上就是特征工程基础的三个核心模块。单因子分析帮你筛选好因子,共线性处理让因子之间不打架,标准化归一化让数据更规整。这三步走扎实了,后面的模型训练才能事半功倍。

最后提醒一句:特征工程没有银弹。每个数据集都有自己的脾气,多试、多验证、多复盘,才是正道。


公众号:蓝海数据掘金营,微信deep3321