4. 因子相关性分析:皮尔逊相关系数、斯皮尔曼相关系数、因子共线性问题、VIF检验

各位好,欢迎来到第四章。这一章我们聊聊因子之间的「关系」。

做多因子模型,最怕什么?最怕你辛辛苦苦挖了50个因子,结果它们之间高度相关。说白了,就是它们都在讲同一个故事。你想想看,如果两个因子几乎一模一样,那它们同时放进模型,不仅浪费,还会搞乱你的参数估计。

我个人习惯是,在因子合成之前,先做一轮「关系排查」。这就像相亲之前先查查对方有没有血缘关系——嗯,有点夸张,但道理差不多。

核心观点: 因子相关性分析不是为了剔除所有相关因子,而是为了识别冗余、诊断共线性,为后续的因子合成和模型训练打好基础。
因子相关性分析知识体系 因子相关性分析 相关系数计算 皮尔逊相关系数 斯皮尔曼相关系数 共线性诊断方法 VIF检验 条件数 处理策略 因子剔除 因子合成 目标:识别冗余 → 诊断共线性 → 决定保留/剔除/合成 最终提升模型稳定性与解释力

4.1 皮尔逊相关系数:最常用的线性关系度量

皮尔逊相关系数,大家应该都不陌生。它衡量的是两个变量之间的线性相关程度。取值范围在[-1, 1]之间,0表示无线性关系,±1表示完全线性相关。

我在项目中遇到过一件事:有次我构建了20个技术因子,跑完皮尔逊矩阵一看,好家伙,有3对因子的相关系数超过了0.9。仔细一查,原来是同一个指标的不同变形——说白了就是换了个参数,本质没变。

经验之谈: 我个人习惯把阈值设在0.7。超过0.7的因子对,我会标记为「高相关」,需要进一步处理。当然,这个阈值不是死的,如果你做的是高频策略,阈值可以放宽到0.8甚至0.85。
import pandas as pd
import numpy as np
from scipy.stats import pearsonr

# 假设 factor_df 是因子数据,index为时间,columns为因子名
def calc_pearson_matrix(factor_df):
    """计算因子间的皮尔逊相关系数矩阵"""
    return factor_df.corr(method='pearson')

# 找出高相关因子对
def find_high_corr_pairs(corr_matrix, threshold=0.7):
    high_pairs = []
    cols = corr_matrix.columns
    for i in range(len(cols)):
        for j in range(i+1, len(cols)):
            if abs(corr_matrix.iloc[i, j]) >= threshold:
                high_pairs.append((cols[i], cols[j], corr_matrix.iloc[i, j]))
    return high_pairs

# 示例
# corr_mat = calc_pearson_matrix(factor_df)
# pairs = find_high_corr_pairs(corr_mat, 0.7)
# for p in pairs:
#     print(f"{p[0]} vs {p[1]}: {p[2]:.3f}")

4.2 斯皮尔曼相关系数:非线性关系的侦察兵

皮尔逊有个硬伤——它只认线性关系。如果两个因子是单调非线性关系(比如指数关系),皮尔逊可能给出一个很低的数值,让你误以为它们不相关。

这时候就该斯皮尔曼出场了。它基于秩次计算,说白了就是把原始数据换成排名,再算皮尔逊。这样一来,只要两个因子的排名变化趋势一致,斯皮尔曼就能捕捉到。

我记得有一次做基本面因子分析,发现市盈率和市净率的皮尔逊只有0.3,但斯皮尔曼高达0.78。为什么?因为这两个指标存在明显的非线性关系——低估值区间相关性强,高估值区间相关性弱。如果只看皮尔逊,你可能会误判。

避坑指南: 我曾经只依赖皮尔逊做因子筛选,结果模型在极端行情下表现很差。后来加入斯皮尔曼检查,才发现有几个因子在尾部区域高度相关。所以我的建议是:两个相关系数都算,对比着看。如果皮尔逊低但斯皮尔曼高,说明存在非线性关系,需要警惕。
from scipy.stats import spearmanr

def calc_spearman_matrix(factor_df):
    """计算因子间的斯皮尔曼相关系数矩阵"""
    return factor_df.corr(method='spearman')

def compare_corr(factor1, factor2):
    """对比皮尔逊和斯皮尔曼"""
    p_pearson, _ = pearsonr(factor1, factor2)
    p_spearman, _ = spearmanr(factor1, factor2)
    print(f"皮尔逊: {p_pearson:.4f}")
    print(f"斯皮尔曼: {p_spearman:.4f}")
    if abs(p_spearman) > abs(p_pearson) + 0.2:
        print("⚠️ 存在潜在非线性关系,建议深入检查")

4.3 因子共线性问题:为什么它是个麻烦?

因子共线性,简单说就是两个或多个因子之间存在强相关关系。你可能会问:「相关就相关呗,有什么大不了的?」

嗯,问题大了。共线性会带来三个主要麻烦:

  • 参数估计不稳定: 回归系数的方差会变得很大,稍微换点数据,系数就天翻地覆
  • 模型解释力下降: 你分不清哪个因子真正起作用,因为它们的效应混在一起
  • 过拟合风险增加: 模型会「记住」训练数据中的噪声模式,泛化能力变差

我见过最夸张的一个案例:有人用50个因子做线性回归,结果VIF最高的因子达到了120。模型在训练集上R²高达0.95,但一到测试集就崩到0.2。这就是典型的共线性导致的过拟合。

4.4 VIF检验:量化共线性的标尺

VIF(方差膨胀因子)是诊断共线性的标准工具。它的逻辑很简单:对每个因子,用其他所有因子做回归,看这个因子的方差被「膨胀」了多少。

公式不复杂:VIF = 1 / (1 - R²)。其中R²是用其他因子预测该因子的拟合优度。

如果VIF=1,说明该因子与其他因子完全不相关。VIF越大,共线性越严重。我个人习惯的阈值是:

VIF值 共线性程度 建议操作
1 - 5 轻度或无共线性 可保留
5 - 10 中度共线性 需关注,考虑处理
10以上 严重共线性 必须处理
注意: VIF阈值不是绝对的。如果你做的是预测型模型(如XGBoost),对共线性的容忍度会高一些。但如果是解释型模型(如线性回归做因子归因),VIF超过5就要警惕了。
from statsmodels.stats.outliers_influence import variance_inflation_factor
from statsmodels.tools.tools import add_constant

def calc_vif(factor_df):
    """
    计算所有因子的VIF值
    注意:factor_df中不应包含目标变量,只包含因子列
    """
    # 添加截距项
    X = add_constant(factor_df)
    
    vif_data = pd.DataFrame()
    vif_data['factor'] = factor_df.columns
    vif_data['VIF'] = [variance_inflation_factor(X.values, i+1) 
                       for i in range(len(factor_df.columns))]
    return vif_data.sort_values('VIF', ascending=False)

# 示例
# vif_result = calc_vif(factor_df)
# print(vif_result)
# 
# # 如果发现VIF > 10的因子,建议剔除
# high_vif_factors = vif_result[vif_result['VIF'] > 10]['factor'].tolist()
# print(f"需要处理的因子: {high_vif_factors}")

4.5 实战中的处理策略

当你发现因子间存在共线性时,有几种处理方式:

  1. 剔除冗余因子: 保留解释力更强、逻辑更清晰的因子,剔除相关性高的冗余因子
  2. 因子合成: 将高度相关的因子合成为一个综合因子(如PCA、等权合成)
  3. 正则化: 使用Lasso或Ridge回归,自动处理共线性问题
  4. 分层处理: 先对因子做聚类,再从每个簇中选代表因子

我个人最常用的是「先剔除、再合成」的策略。具体来说:

  • 第一步:计算皮尔逊和斯皮尔曼矩阵,标记高相关对
  • 第二步:计算VIF,标记高VIF因子
  • 第三步:结合业务逻辑,决定保留哪些因子
  • 第四步:对保留的因子做PCA或等权合成,进一步降维
一个小技巧: 如果你发现两个因子相关性很高但都有很强的逻辑支撑,可以试试把它们合成一个「复合因子」。比如,把「市盈率倒数」和「市净率倒数」等权合成一个「估值综合因子」。这样既保留了信息,又解决了共线性。

好了,这一章的内容就到这里。因子相关性分析是构建稳健多因子模型的基础,千万别跳过。下一章我们会聊因子标准化和中性化处理,到时候见。


专注资料整理