用统计方法科学地判断差异是否真实
假设检验是统计推断的核心方法,用于基于样本数据对总体参数做出决策。它让我们能科学地回答"这个差异是真实的还是偶然的?"
| 概念 | 含义 | 类比 |
|---|---|---|
| 零假设 H₀ | 默认立场:无效应 | "无罪推定" |
| 备择假设 H₁ | 想要证明的立场 | "有罪指控" |
| α (显著性水平) | 犯I类错误的上限 | "冤枉好人的容忍度" |
| β (II类错误率) | 漏检真实效应 | "放过坏人的概率" |
| 1-β (统计功效) | 正确检出真实效应 | "抓住坏人的概率" |
| p值 | H₀为真时的极端程度 | "证据有多不利" |
| H₀为真(无罪) | H₀为假(有罪) | |
|---|---|---|
| 拒绝H₀ | ❌ I类错误 (α) 冤枉好人 | ✅ 正确 (1-β) 抓住坏人 |
| 不拒绝H₀ | ✅ 正确 放过好人 | ❌ II类错误 (β) 放过坏人 |
t检验用于小样本情况下比较均值,当总体方差未知时用样本方差代替,统计量服从t分布。
检验样本均值是否与已知值有显著差异:
import numpy as np
from scipy import stats
np.random.seed(42)
# 螺栓标称长度50mm,抽检20个
sample = np.random.normal(50.3, 0.8, 20)
t_stat, p_value = stats.ttest_1samp(sample, 50)
print(f"样本均值: {sample.mean():.4f}")
print(f"样本标准差: {sample.std(ddof=1):.4f}")
print(f"t统计量: {t_stat:.4f}")
print(f"p值: {p_value:.6f}")
print(f"α=0.05: {'拒绝H₀' if p_value < 0.05 else '不拒绝H₀'}")
# 输出: t=0.9489, p=0.3546 → 不拒绝H₀
# 手动实现单样本t检验
n = len(sample)
sample_mean = sample.mean()
pop_mean = 50
sample_std = sample.std(ddof=1)
t_manual = (sample_mean - pop_mean) / (sample_std / np.sqrt(n))
print(f"手动t统计量: {t_manual:.4f}") # 0.9489
print(f"scipy t统计量: {t_stat:.4f}") # 0.9489
print(f"一致: ✅")
比较两组独立样本的均值是否有显著差异:
# 两组学生成绩比较
group_a = np.random.normal(85, 10, 30) # A班
group_b = np.random.normal(90, 10, 30) # B班
t_stat2, p_value2 = stats.ttest_ind(group_a, group_b)
print(f"A班均值: {group_a.mean():.2f}")
print(f"B班均值: {group_b.mean():.2f}")
print(f"t统计量: {t_stat2:.4f}, p值: {p_value2:.6f}")
# 输出: t=-3.2567, p=0.0019 → 拒绝H₀ (显著差异)
卡方检验用于分类数据的拟合优度和独立性检验:
from scipy import stats
# 骰子是否公平?观测值 vs 期望值
observed = np.array([45, 30, 15, 10])
expected = np.array([25, 25, 25, 25])
chi2, p_chi2, dof, _ = stats.chi2_contingency(np.array([observed, expected]))
print(f"卡方统计量: {chi2:.4f}") # 15.0974
print(f"p值: {p_chi2:.6f}") # 0.001735
print(f"自由度: {dof}") # 3
# p < 0.05 → 骰子不公平
仅看p值不够!效应量告诉我们差异有多大,置信区间告诉我们估计的精度。
# 计算Cohen's d
def cohens_d(group1, group2):
n1, n2 = len(group1), len(group2)
s_pooled = np.sqrt(((n1-1)*group1.var(ddof=1) + (n2-1)*group2.var(ddof=1)) / (n1+n2-2))
return (group1.mean() - group2.mean()) / s_pooled
d = cohens_d(group_a, group_b)
print(f"Cohen's d: {d:.4f}") # 效应量
# 95%置信区间
ci = stats.t.interval(0.95, df=len(sample)-1,
loc=sample.mean(),
scale=stats.sem(sample))
print(f"均值95%CI: [{ci[0]:.4f}, {ci[1]:.4f}]")
| 场景 | 数据类型 | 推荐检验 | Python函数 |
|---|---|---|---|
| 单样本均值 | 连续 | 单样本t检验 | stats.ttest_1samp() |
| 两组均值比较 | 连续,独立 | 独立t检验 | stats.ttest_ind() |
| 配对样本比较 | 连续,配对 | 配对t检验 | stats.ttest_rel() |
| 三组及以上均值 | 连续 | ANOVA | stats.f_oneway() |
| 分类变量关联 | 分类 | 卡方检验 | stats.chi2_contingency() |
| 非参数两组 | 有序/偏态 | Mann-Whitney U | stats.mannwhitneyu() |
| 非参数配对 | 有序/偏态 | Wilcoxon | stats.wilcoxon() |
| 正态性检验 | 连续 | Shapiro-Wilk | stats.shapiro() |