跳到主内容
研究主线统计检验与研究可信度 · 第20–23讲3/4模块导览 →
讲义

第22讲:p值挖掘与多重比较偏差 (p-Hacking and Multiple Comparisons Bias)

用随机序列和股票配对展示多重检验的假阳性,比较 Bonferroni、FDR 与样本外验证的取舍。

完成状态保存在当前浏览器,可随时取消。

当我们在同一数据集上进行多次统计检验时,偶然得到显著性结果的概率会大大增加,这就是多重比较偏差的概念。这种现象就像买彩票——买的次数越多,中奖几率越大,但大部分中奖结果只是运气。

生活案例:同时服用多种感冒药,恰巧症状好转时,可能错误归因于某药物有效,而实际是病程自然结束。

一、核心概念解析#

1.1 p 值的正确理解#

  • 定义:假设原假设成立时,观察到当前数据或更极端情况的概率

  • 常见误区:p=0.05不代表有95%的概率结论正确

  • 正确用法:提前设定显著性水平(如0.05),严格作为二值判断标准

code
# 正确使用p值的示例
cutoff = 0.05
p_value = 0.03  # 假设的检验结果

if p_value < cutoff:
    print("拒绝原假设")
else:
    print("未能拒绝原假设")

二、实验演示:随机数据中的虚假关联#

2.1 创建随机数据集#

code
import numpy as np
import pandas as pd
import scipy.stats as stats

N = 20  # 20个独立时间序列
T = 100 # 每个序列100个观测点

df = pd.DataFrame({f'X{i}': np.random.normal(0, 1, T) for i in range(N)})

2.2 执行多重相关性检验#

code
significant_pairs = []
cutoff = 0.05

for i in range(N):
    for j in range(i+1, N):
        _, p = stats.spearmanr(df[f'X{i}'], df[f'X{j}'])
        if p < cutoff:
            significant_pairs.append((i, j))

print(f"发现显著相关对数量:{len(significant_pairs)}")

2.3 理论假阳性计算#

多重比较中预期假阳性(Expected False Positives)的计算公式:

N(N1)2×α\frac{N(N-1)}{2} \times \alpha

其中:

  • N为比较组数

  • α\alpha为显著性水平

code
expected_fp = (20*19/2) * 0.05  # 输出9.5

三、解决方案:Bonferroni校正#

3.1 校正原理#

将显著性水平调整为:

αnew=αNumber of Tests\alpha_{new} = \frac{\alpha}{Number\ of\ Tests}

这是Bonferroni校正的计算公式,其中:

  • αnew\alpha_{new} 为校正后的显著性水平

  • α\alpha 为原始显著性水平

  • Number of TestsNumber\ of\ Tests 为进行检验的总次数

code
total_tests = 20*19//2
new_cutoff = 0.05 / total_tests  # 0.000263

3.2 校正后实验#

code
corrected_pairs = []
for i in range(N):
    for j in range(i+1, N):
        _, p = stats.spearmanr(df[f'X{i}'], df[f'X{j}'])
        if p < new_cutoff:
            corrected_pairs.append((i, j))

print(f"校正后显著对数量:{len(corrected_pairs)}")

3.3 方法对比#

方法优点缺点
不做校正灵敏度高假阳性率高
Bonferroni控制总体错误率过于保守
分阶段验证平衡灵敏度特异性需要额外数据

四、实战案例:股票数据中的虚假相关性#

4.1 获取真实金融数据#

code
import yfinance as yf

# 获取10只科技股数据
symbols = ['AAPL', 'MSFT', 'GOOG', 'AMZN', 'META',
           'TSLA', 'NVDA', 'INTC', 'ADBE', 'CSCO']
data = yf.download(symbols, start='2020-01-01', end='2023-01-01')['Close']

4.2 相关性分析#

code
significant_pairs = []
cutoff = 0.05

for i in range(len(symbols)):
    for j in range(i+1, len(symbols)):
        stock1 = data[symbols[i]].pct_change().dropna()
        stock2 = data[symbols[j]].pct_change().dropna()
        _, p = stats.spearmanr(stock1, stock2)
        if p < cutoff:
            significant_pairs.append((symbols[i], symbols[j]))

print(f"发现显著相关股票对:{significant_pairs}")

五、防御策略与最佳实践#

5.1 事前预防措施#

  • 预先确定假设:基于理论而非数据挖掘提出假设

  • 样本外测试:保留部分数据用于最终验证

  • 实验设计:使用分层验证框架

5.2 事后修正方法#

  • 错误发现率控制(FDR)

  • Holm-Bonferroni方法

  • 置换检验(Permutation Test)

5.3 思考练习#

  1. 当N=50时,使用α=0.05会预期多少个假阳性?

  2. 如果将Bonferroni校正应用于N=100的股票池,新α值是多少?

  3. 设计一个两阶段验证方案来平衡灵敏度和特异度

六、关键要点总结#

  • 多重比较偏差会指数级增加假阳性风险

  • p 值是连续的证据度量,不应被机械切成“有效/无效”;还要同时报告效应量、置信区间、研究设计与先验合理性

  • Bonferroni校正是保守但可靠的修正方法

  • 样本外验证是避免p-Hacking的黄金标准

  • 研究设计比统计修正更重要

七、量化研究中的最低验证标准#

  1. 在研究日志中登记尝试过的全部因子、参数、市场和样本窗口,而不是只报告胜出的组合

  2. 使用按时间滚动的 walk-forward 验证;存在自相关时采用分块 bootstrap,不能随机打乱金融时间序列

  3. 将最终留出集冻结到研究结束,任何查看都会消耗一次“样本外”机会

  4. 同时报告未经修正与经过多重检验修正的结果,并用 Deflated Sharpe Ratio、回测过拟合概率等指标评估筛选偏差

  5. 预先写明经济假设、主指标、淘汰条件和交易成本模型,使统计显著性服务于可证伪的研究命题

通过系统理解这些原理并应用防御策略,研究者可以显著提升分析的可靠性。记住:真正的发现应该经得起时间和新数据的考验。