跳到正文
百沐一下百沐一下
本节目录

多重检验校正:Bonferroni、FDR 怎么选

跟完这篇,您能弄清 Bonferroni 与 BH/FDR 到底控的是什么、组学分析该用哪个,并用一行 p.adjust 把成千上万个原始 p 值校正成 padj,再按合适阈值筛出一份经得起追问的显著结果表。

为什么几万次检验必须校正

单独看一次检验,p<0.05 的意思是:如果这个基因其实没变化,纯靠随机也只有 5% 的概率被误判成「显著」。听起来很稳。问题出在「一次」变成「几万次」。

假设您做一次转录组差异分析,一口气对 20000 个基因各做一次检验。就算这 20000 个基因全都没有真实变化,在 p<0.05 这条线下,您预期仍会有约 0.05 × 20000 = 1000 个基因被判成显著——这 1000 个全是假阳性。

这就是多重检验的核心麻烦:检验做得越多,纯靠运气蒙对的假阳性就越多。您筛出来的「差异基因列表」里,可能一大半是噪声。多重检验校正,就是在这么多次检验之后,把「显著」这条线抬高、把假阳性压回可控范围的一套办法。

一句话记住:做一次检验,看原始 p 值就够了;做成百上千次检验,必须校正后再下结论。

先分清两种错误率:FWER 与 FDR

不同校正方法控的不是同一件事。选方法前,先分清两个概念,后面就不会用错。

概念全称控的是什么代表方法
FWER整体错误率(family-wise error rate)至少出现一个假阳性的概率Bonferroni、Holm
FDR假发现率(false discovery rate)在您判为显著的那批结果里,假阳性所占的比例BH(Benjamini-Hochberg)

差别在于严格程度和您能接受的代价:

  • FWER,是要求「几乎不容许犯一个错」。适合结论要非常保守、一个假阳性都代价很大的场景,代价是容易把真信号也一起漏掉。
  • FDR,是允许结果里混入一小比例假阳性,换取抓到更多真信号。组学里通常是先筛出一批候选基因再往下验证,能接受「这 200 个里大概 10 个是假的」,所以 FDR 更实用。

Bonferroni 与 BH 各控什么、谁更严

组学里真正要在这两种之间做选择,先看它们各自怎么算。

Bonferroni(控 FWER,最严):把阈值 α 除以检验次数 m,只有 p < α / m 才算显著。做 20000 次检验、想把整体 α 控在 0.05,单个基因门槛就是 0.05 / 20000 = 0.0000025。等价写法是把每个 p 值乘以 m(超过 1 截成 1)再和 0.05 比。优点是不管检验之间相不相关都成立、逻辑直白;缺点是检验一多门槛低到离谱,极容易连真差异一起漏掉,几万个基因往往只剩个位数过线。

BH / Benjamini-Hochberg(控 FDR,组学最常用):把原始 p 值从小到大排好,找到满足 p(k) ≤ (k / m) × α最大的 k,把它及之前的全判为显著。您不用手排,p.adjust(method = "BH") 会直接给出校正值(常被叫作 q 值 / padj)。它最直观的读法是:按 padj<0.05 筛出 200 个基因,这 200 个里预期约 5%(约 10 个)是假阳性。

用一个 5 个 p 值的小例子,一眼看清三种线的松紧(Bonferroni 乘 5 封顶 1,BH 由 p.adjust 给出):

排序 k原始 pBonferroniBH(padj)
10.0010.0050.005
20.0080.0400.020
30.0200.1000.033
40.0400.2000.050
50.3001.0000.300

在 padj<0.05 这条线下:原始 p 会放行 4 个,BH 放行 3 个,Bonferroni 只放行 2 个。第 4 个基因原始 p 是 0.04(本来过线),BH 把它抬到刚好 0.05 而落选——这正是校正在起作用。同一批数据,严格程度从松到严是:原始 p → BH → Bonferroni。

小结成一句选择:

  • 几十次以内的关键检验、要结论很稳 → Bonferroni(或更常用的改进版 Holm,同控 FWER 但功效更高)。
  • 成百上千次的组学筛选 → BH。DESeq2、edgeR、limma、Seurat 等主流工具输出的 padj / adj.P.Val 列,默认就是 BH 校正过的,别当成原始 p 值再校正一遍
  • 担心检验之间存在任意方向的强相关,可换更保守的 BY(Benjamini-Yekutieli),代价是更严;多数常规组学用 BH 即可。

动手:一行 p.adjust 校正并筛显著

R 里做校正只需一个函数 p.adjust():喂进一批原始 p 值,拿回校正后的值。照下面四步走:

  1. 取出这一批检验的原始 p 值,凑成一个向量。
  2. p.adjust() 做校正,选好 method
  3. 按阈值筛出显著项。
  4. 存成表,方法和阈值一并记清楚。
# 第 1 步:pvals 是一批原始 p 值(比如几万个基因各做一次检验得到的向量)
pvals <- res$pvalue

# 第 2 步:一行校正
p_bonf <- p.adjust(pvals, method = "bonferroni")   # 控整体错误率,最严
p_bh   <- p.adjust(pvals, method = "BH")           # 控假发现比例,写 "fdr" 完全等价

# 第 3 步:按 FDR < 0.05 筛出显著项(0.05 是常用惯例,按数据与阶段调,非铁律)
sig_idx <- which(p_bh < 0.05)

# 第 4 步:整理成结果表并落盘
out <- data.frame(gene = names(pvals), p = pvals, padj = p_bh)
out <- out[order(out$padj), ]                      # 按 padj 从小到大排
write.csv(out, "results_fdr.csv", row.names = FALSE)

method 常用取值:"bonferroni""holm""BH"(即 "fdr")、"BY""none"

如果您用 DESeq2 跑差异分析,results() 返回的 padj已经是 BH 校正好的,跳过前两步直接筛即可:

res <- results(dds)                                # res$padj 已是 BH 校正后的值
# padj<0.05 且 |log2FC|>1 是常用惯例(非铁律,按数据与研究目的调)
sig <- subset(res, padj < 0.05 & abs(log2FoldChange) > 1)

一个高频坑:p.adjust 要把同一批检验的 p 值一次性全喂进去才对——它需要知道总检验数 m。逐个基因分别调用、或只把「看起来显著的那几个」拿去校正,都会算错。

校正前先看一眼 p 值直方图

在信任任何 FDR 结果之前,养成一个习惯:把原始 p 值画成直方图看一眼。BH 的假发现率保证,建立在 p 值分布行为正常的前提上;分布不对,padj 也不可信。

hist(pvals, breaks = 50, main = "raw p-value 分布", xlab = "p")

对照下面这张表读形状:

直方图形状通常意味着该怎么办
靠近 0 有尖峰、其余大致平坦健康:有真信号 + 一堆无效假设正常,可放心用 BH
完全平坦、0 附近没有堆积大概率没有真差异别硬筛,回头看分组与效应量
靠近 1 一侧反而堆高偏保守,检验或模型可能设错检查模型设定、方差估计
两端都堆、中间空(双峰)常见于批次效应等系统偏差没处理干净先做批次校正再重跑

期望看到的是第一行那种「0 处尖峰 + 其余平坦」。出现后面几种形状,先别急着解读 padj,回去查数据和模型。

padj 阈值怎么定

padj < 0.05行业惯例,不是铁律。0.05 没有任何生物学或数学上的必然性,只是长期沿用下来的默认线。按研究阶段和风险,可调松或调严:

场景常用 padj 阈值理由
探索 / 发现阶段,后续还会实验验证0.1(有时更松)宁可多留候选,漏掉真信号代价更大
常规差异分析、投稿主图0.05通用惯例,审稿人默认接受
确证性结论、假阳性代价大0.01 或更严结论要更稳,容错更低

几条务实建议:

  1. 阈值要在看结果之前定好,别根据「哪个阈值能让我的目标基因过线」反过来挑,那会引入偏倚。
  2. 写论文时把方法和阈值写清楚:用了哪种校正(如 BH)、padj 卡在多少、差异倍数(如 |log2FC|>1)卡在多少。这几个数直接决定您的差异基因列表,必须可复现。
  3. 差异分析里,padj 阈值常和 log2FoldChange 阈值一起用:前者管「统计上是否可信」,后者管「变化幅度是否够大」,两个维度互补,别只卡一个。

什么时候可以不校正

校正不是越多越好,也不是任何情况都要上。以下情形通常不需要、或要谨慎处理:

  • 只有一个预先设定的主假设:整个研究就围绕一个事先定好的假设做一次检验,看原始 p 值即可,没有「多重」问题。
  • 纯描述、不下显著性结论:只是描述数据、画分布、报告效应量而不宣称「显著」时,校正意义不大。但一旦您开始从一堆结果里挑「显著的」来讲故事,多重检验的问题就回来了。
  • 校正的范围(family)要讲清楚:校正针对「一族相关的检验」做。哪些检验算同一族,需按研究设计事先界定,而不是把八竿子打不着的检验硬凑在一起除以一个大 m。

诚实说,「一个主假设不必校正」在统计学界仍有争论,尤其当您实际上偷偷看了很多结果才挑出这一个来报告时(这叫 p-hacking)。最稳妥的做法:事先想清楚要检验哪些假设、同一族有多少个,再决定校正与否——而不是跑完看到 p 值再补决定。

在平台上跑一遍

不想写 p.adjust、也不想手动排 p 值,可以在「百沐一下」的智能统计里零代码完成:上传数据表,用一句话说清目的(比如「比较两组的差异表达,做多重检验校正,按 FDR<0.05 筛显著基因」),它会自动选好方法、跑出校正后的 padj、给出显著列表、画好 p 值直方图,并附上可复现的 R 代码。跑完还能追问「换成 Bonferroni 再看一遍」「阈值放宽到 0.1」,无需从头再来。

入口与用法见 智能统计使用指南,或直接开始:app.baimuyixia.com

相关