跳到主要内容

常见统计陷阱

问题

数据分析中有哪些常见的统计误区和陷阱?如何避免得出错误结论?

答案

1. 相关不等于因果

陷阱:看到两个变量相关就下因果结论。

伪相关示例
import pandas as pd
import numpy as np

# 两个完全无关的时间序列可能高度相关
months = pd.date_range('2020-01', periods=36, freq='M')
ice_cream_sales = np.array([10,12,20,35,50,60,55,45,30,15,10,8] * 3) # 季节性
drowning_deaths = np.array([5,6,12,20,30,35,32,25,15,8,5,4] * 3) # 也有季节性

r = np.corrcoef(ice_cream_sales, drowning_deaths)[0,1]
print(f"相关系数: {r:.3f}") # 很高!
# 但冰淇淋不会导致溺水 → 共同受"气温"影响
避免方法
  • 寻找混淆变量(温度、季节等)
  • 随机对照实验(A/B 测试)验证因果
  • 使用因果推断方法(DID、RDD、PSM)

2. 辛普森悖论(Simpson's Paradox)

陷阱:总体趋势和分组趋势方向相反

辛普森悖论
import pandas as pd

# 两个医院的手术成功率
data = {
'医院': ['A', 'A', 'B', 'B'],
'病情': ['轻症', '重症', '轻症', '重症'],
'手术数': [800, 200, 200, 800],
'成功数': [720, 140, 175, 560],
}
df = pd.DataFrame(data)
df['成功率'] = df['成功数'] / df['手术数']
print(df)
# A 医院轻症 90%, 重症 70% → 各组都比 B 好
# B 医院轻症 87.5%, 重症 70%

# 但总体成功率:
# A: (720+140) / 1000 = 86%
# B: (175+560) / 1000 = 73.5% ← 等等...

# 换一组数据让悖论出现:
# A 接收更多重症 → 总体被拉低
# B 接收更多轻症 → 总体被拉高
print("→ 分组看 A 更好,但总体可能 B 更好(取决于病人分布)")
避免方法
  • 按维度下钻分析,不要只看总数
  • 业务分析中先看用户构成是否变化
  • 留存率、转化率等指标,先看各群体的变化再看总体

3. 幸存者偏差(Survivorship Bias)

陷阱:只分析"存活"的数据,忽略已流失/失败的样本。

场景偏差正确做法
"成功企业都做了 X"忽略了做 X 但失败的企业对比成功/失败企业
"活跃用户满意度高"不满意的已经流失包含流失用户的调研
"二战飞机加固机翼"返航飞机机翼中弹→修机翼没返航的才是关键部位

4. 多重比较问题

陷阱:同时做多次假设检验,假阳性率急剧上升。

P(至少一个误报)=1(1α)nP(\text{至少一个误报}) = 1 - (1 - \alpha)^n

做 20 次测试(α=0.05)→ 10.9520=64%1 - 0.95^{20} = 64\% 的概率出现至少一个假阳性!

多重比较校正
from statsmodels.stats.multitest import multipletests
import numpy as np

# 假设同时跑了 20 个指标的 A/B 测试
p_values = [0.03, 0.08, 0.01, 0.15, 0.04, 0.52, 0.02, 0.23,
0.07, 0.91, 0.005, 0.33, 0.06, 0.44, 0.02, 0.11,
0.85, 0.04, 0.09, 0.67]

# Bonferroni 校正(最保守):α' = α / n
reject_bonf, pvals_bonf, _, _ = multipletests(p_values, alpha=0.05, method='bonferroni')
print(f"Bonferroni 校正后显著个数: {sum(reject_bonf)}")

# BH 校正(FDR 控制,推荐)
reject_bh, pvals_bh, _, _ = multipletests(p_values, alpha=0.05, method='fdr_bh')
print(f"BH 校正后显著个数: {sum(reject_bh)}")

5. 基数率谬误(Base Rate Neglect)

陷阱:忽略先验概率(基数率),被条件概率误导。

例:某广告"精准人群"点击率 5%,远高于全站 1%。但如果这个精准人群只有 100 人(5 次点击),而全站有 100 万人(1 万次点击),5% 可能只是小样本波动

避免方法
  • 关注绝对量,不只看比率
  • 小样本的比率不可靠,计算置信区间
  • 结合贝叶斯思维评估概率

6. 选择偏差

类型说明示例
自选择偏差参与者自愿加入调查问卷只有热心用户回复
时间偏差不同时间段的用户不同早期用户 vs 最新用户
覆盖不全数据没覆盖所有群体只分析 App 用户,遗漏 Web
删失偏差数据被截断只看已完成订单,忽略取消的

7. 过度拟合已有数据

(数据挖掘中的"事后诸葛亮")

过度拟合分析
# 反面教材:在同一份数据上反复筛选条件,总能找到"显著"结果
import numpy as np
np.random.seed(0)

# 纯随机数据
data = np.random.randn(1000, 50) # 1000 行, 50 个特征
target = np.random.randint(0, 2, 1000) # 随机标签

# 如果对 50 个特征逐一做 t 检验,p<0.05 的约有 2-3 个
# 但这些都是假阳性!→ 需要用训练集/测试集分离验证

8. 数据分析常见错误清单


常见面试问题

Q1: 如何避免 A/B 测试中的假阳性?

答案

  1. 提前确定样本量,不能"跑到显著就停"(p-hacking)
  2. 多指标测试时进行多重比较校正(BH/Bonferroni)
  3. 设定主要指标和次要指标,只对主要指标做显著性判断
  4. 使用序贯检验(Sequential Testing)如果确实需要提前查看

Q2: 辛普森悖论在业务中有什么例子?

答案

  • DAU 增长但留存下降 → 新用户大量涌入(低留存)稀释了老用户的高留存
  • 总转化率提升但各渠道都下降 → 高转化渠道占比降低
  • 对策:始终按渠道/人群/时段下钻分析,不只看 Total

Q3: p-hacking 是什么?如何避免?

答案

  • p-hacking:反复尝试不同分析方法/子集/指标,直到找到 p < 0.05
  • 常见手法:删除异常值直到显著、增加/删除控制变量、选择性报告
  • 避免方法
    • 提前注册分析计划(预注册)
    • 报告所有分析过的指标
    • 使用校正后的 p 值
    • 区分探索性分析和验证性分析

Q4: 平均值陷阱有哪些?

答案

  • 均值 vs 中位数:收入分布右偏,均值被极端值拉高,中位数更代表"普通人"
  • 平均数悖论:A 和 B 两组合并后均值可能误导(辛普森悖论)
  • 百分比的均值:不能直接对百分比求均值(需加权)
  • 建议:同时报告均值、中位数、分位数,展示分布而非单一数字

Q5: 面对一份分析结论,你会如何质疑/检验?

答案(框架):

  1. 数据来源:数据采集是否有偏差?覆盖面是否完整?
  2. 样本量:是否足够大?置信区间有多宽?
  3. 混淆变量:是否控制了关键变量?
  4. 因果方向:相关 ≠ 因果,有没有反向因果?
  5. 子群分析:总体结论在各子群中是否一致?(辛普森悖论)
  6. 时间因素:是否考虑了季节性、趋势变化?

相关链接