ML 学习站
跳到正文

非参数检验

符号检验、Wilcoxon 符号秩、Kruskal-Wallis、游程检验。

30 分钟4 / 42,503
加载中...

非参数检验

本章问题: 数据严重偏态 (如收入、点击量), 或者只有"有序"分类 (如满意度), 还能用 t 检验吗? 用 非参数检验, 不依赖正态假设, 适用范围更广。

1. 什么时候用非参数?

数据情况推荐
连续 + 正态t 检验 (效率高)
连续 + 偏态 / 小样本非参数 (Wilcoxon, Mann-Whitney)
有序分类 (1-5 星)非参数 (基于秩)
样本量 < 20非参数 (难判断正态)
极端异常值非参数 (对异常值稳健)

⚠️ 非参数的代价: 在数据真的正态时, 非参数 power 较低 (效率 ≈ 95%)。所以"数据明明正态, 用非参数"是浪费。

2. 核心思想: 用"秩"代替"数值"

秩 (rank) = 把数据从小到大排序, 给每个值的"名次"。

import numpy as np
from scipy.stats import rankdata

data = [3.2, 1.5, 4.8, 2.1, 5.0]
ranks = rankdata(data)  # [3, 1, 4, 2, 5]
print(f"数据: {data}")
print(f"秩: {ranks}")
# 秩的好处: 摆脱具体数值, 只看相对大小
# 不受异常值影响

3. 符号检验 (Sign Test): 最简单的非参数

3.1 单样本符号检验

例: 5 个病人用药后血压变化 (+3, -2, +5, -1, +4), 检验"改善是否有显著"?

H₀: 中位数 = 0 (改善 = 0) H₁: 中位数 ≠ 0

把差值 d 的符号 (+/-) 看作"二项分布", 统计 + 的个数:

from scipy.stats import binom
import numpy as np
diffs = np.array([3, -2, 5, -1, 4])
n_pos = (diffs > 0).sum()    # 3
n_neg = (diffs &lt; 0).sum()    # 2
# 零假设下, + 和 - 各占一半
n = n_pos + n_neg
p = 2 * binom.cdf(min(n_pos, n_neg), n, 0.5)
print(f"符号检验: + = {n_pos}, - = {n_neg}, p = {p:.4f}")
# p = 1.0 (3 vs 2, 完全无法拒绝 H₀)

简单但浪费信息 (只用了符号, 没用大小)。

4. Wilcoxon 符号秩检验: 单样本/配对

既看符号, 又看"秩", 信息利用率更高。

from scipy.stats import wilcoxon
import numpy as np

# 配对: 同一批 20 个病人, 用药前后血压
before = np.array([180, 165, 170, 175, 190, 185, 178, 192, 188, 170,
                   182, 168, 175, 195, 178, 188, 172, 165, 180, 175])
after  = np.array([165, 158, 162, 168, 180, 175, 170, 184, 178, 162,
                   170, 160, 168, 188, 172, 180, 165, 158, 172, 168])

w_stat, p_val = wilcoxon(before - after, alternative="greater")
# "greater" 因为我们预期 before > after (血压降)
print(f"Wilcoxon 符号秩: W = {w_stat}, p = {p_val:.4f}")

4.1 大样本时

n > 25 时, Wilcoxon 的 W 近似正态:

# 小样本: 直接用精确分布
# 大样本: 上面 z 公式 (Python 自动)

5. Wilcoxon 秩和检验 (Mann-Whitney U): 两独立样本

最常用的两样本非参数检验, 替代两样本 t 检验。

from scipy.stats import mannwhitneyu
import numpy as np

# A 组 (老方法), B 组 (新方法), 各 20 个
np.random.seed(42)
group_a = np.random.exponential(2, 20) + 5
group_b = np.random.exponential(2.5, 20) + 5  # B 组期望更大

u_stat, p_val = mannwhitneyu(group_a, group_b, alternative="less")
print(f"Mann-Whitney U: U = {u_stat}, p = {p_val:.4f}")
# p 小 → A 组显著小于 B 组

5.1 U 统计量含义

  • U = A 组里"赢" B 组的对数 + B 组里"赢" A 组的对数
  • H₀ 下 U 期望 = n₁n₂ / 2
  • 偏离越大, p 越小

6. Kruskal-Wallis H 检验: 多组 (3+)

非参数的 ANOVA, 比较 3+ 独立组的分布。

from scipy.stats import kruskal
import numpy as np

# 3 种教学方法的效果
np.random.seed(42)
method_1 = np.random.normal(75, 5, 30)
method_2 = np.random.normal(78, 5, 30)
method_3 = np.random.normal(82, 5, 30)

h_stat, p_val = kruskal(method_1, method_2, method_3)
print(f"Kruskal-Wallis: H = {h_stat:.3f}, p = {p_val:.4f}")
# 显著 → 至少两组有差异

7. 游程检验 (Runs Test): 检验"随机性"

数据是否随机排列? 例如: 抛硬币 HTHTTHHHT, 看是不是真随机。

from scipy.stats import runs
# 例: 抛硬币 20 次
sequence = "HTHHHTHTTHTHHHTHHTHH"
n_h = sequence.count("H")
n_t = sequence.count("T")
r_stat, p_val = runs(np.array([c == "H" for c in sequence]))
print(f"游程数: {r_stat}, p = {p_val:.4f}")
# p 小 → 不随机 (有趋势)

7.1 游程检验的 ML 应用

  • 特征选择: 检验特征取值是否随机
  • 时间序列: 检验残差是否独立 (白噪声)
  • A/B 测试: 检验用户分配是否真的随机 (SRM check)

8. 检验方法选择速查

数据多少组? ─┬─ 1 组 (跟已知值比)
             │     ├─ 正态 → 单样本 t
             │     └─ 偏态 → Wilcoxon 符号秩
             │
             ├─ 2 组 (独立)
             │     ├─ 正态 → 独立 t (Welch)
             │     └─ 偏态 → Mann-Whitney U
             │
             ├─ 2 组 (配对)
             │     ├─ 正态 → 配对 t
             │     └─ 偏态 → Wilcoxon 符号秩
             │
             └─ 3+ 组
                  ├─ 正态 → ANOVA (下章)
                  └─ 偏态 → Kruskal-Wallis

9. 实战: ML 模型对比 (非参数)

import numpy as np
from scipy import stats

# 模拟 5 个随机种子下, 模型 A 和 B 的 F1
np.random.seed(42)
f1_a = np.random.normal(0.85, 0.03, 5)  # A
f1_b = np.random.normal(0.88, 0.02, 5)  # B 略高

# 1. 配对 t 检验 (正态, 但 n=5 太依赖正态假设)
t_stat, p_t = stats.ttest_rel(f1_b, f1_a)
print(f"配对 t 检验: t = {t_stat:.3f}, p = {p_t:.4f}")

# 2. Wilcoxon 符号秩 (不依赖正态, n=5 更稳)
w_stat, p_w = stats.wilcoxon(f1_b - f1_a)
print(f"Wilcoxon 符号秩: W = {w_stat}, p = {p_w:.4f}")

# 3. 配对 Bootstrap (更稳)
n_boot = 10000
diffs = f1_b - f1_a
boot_means = [np.mean(np.random.choice(diffs, size=len(diffs), replace=True))
              for _ in range(n_boot)]
ci_low, ci_high = np.percentile(boot_means, [2.5, 97.5])
print(f"Bootstrap 95% CI: [{ci_low:.4f}, {ci_high:.4f}]")
# CI 不包含 0 → 显著

💡 学术报告: 论文里 ML 模型对比, Wilcoxon 符号秩 + Bootstrap CI 是当前最佳实践 (Demšar, 2006)。

10. Python 一行检验大全

import scipy.stats as stats

def auto_test(x, y=None, paired=False, alternative="two-sided"):
    """自动选择参数/非参数检验"""
    if y is None:
        # 单样本
        if len(x) >= 30 and stats.shapiro(x).pvalue > 0.05:
            return stats.ttest_1samp(x, 0, alternative=alternative)
        else:
            return stats.wilcoxon(x - (x.mean() if y is None else 0), alternative=alternative)
    
    # 两样本
    if paired:
        # 配对
        if len(x) >= 30 and stats.shapiro(x - y).pvalue > 0.05:
            return stats.ttest_rel(x, y, alternative=alternative)
        else:
            return stats.wilcoxon(x - y, alternative=alternative)
    else:
        # 独立
        if len(x) >= 30 and stats.shapiro(x).pvalue > 0.05 and stats.shapiro(y).pvalue > 0.05:
            return stats.ttest_ind(x, y, equal_var=False, alternative=alternative)
        else:
            return stats.mannwhitneyu(x, y, alternative=alternative)

11. 小结

你学到了关键点
非参数 vs 参数数据不满足正态, 用非参数
用"名次"代替"数值", 摆脱分布假设
符号检验最简单, 只看符号
Wilcoxon 符号秩单/配对, 看符号 + 秩
Mann-Whitney U两独立样本, 替代 t 检验
Kruskal-Wallis多组 (3+), 替代 ANOVA
游程检验检验随机性
ML 实战模型对比用 Wilcoxon + Bootstrap CI

12. 习题

  1. 两组数据, 各 15 个, 偏态:

    • Mann-Whitney U 检验 p 值?
    • 如果用独立 t 检验, 会有什么后果?
  2. 5 个 ML 模型在同一数据集上的 F1: [0.82, 0.85, 0.84, 0.83, 0.86]

    • 跟 0.80 基准比, 单样本 Wilcoxon 符号秩 p 值?
    • Bootstrap 95% CI?
👉 查看参考答案
  1. Mann-Whitney U 更稳, 因为不假设正态。强行用 t 检验, 在偏态数据下 p 值会显著错 (假阳性或假阴性都可能)。

  2. 计算:

    • 差值 d = [0.02, 0.05, 0.04, 0.03, 0.06], 全部 > 0
    • Wilcoxon: W = 0 (秩和), p ≈ 0.0625 (双侧) 或 0.031 (单侧)
    • 单侧显著 (5 个都超过 0.80, 但样本量小)
    • Bootstrap 10000 次差值均值, 95% CI ≈ [0.026, 0.054] (不含 0)

13. 下一章


📚 本章来源: 改编自 Triola《基础统计学》第 14 版 第 13 章, 加入 ML 模型对比实战。

讨论区(0)

加载评论中...