2. edgeR介绍:历史背景、核心算法与适用条件

聊差异表达分析,edgeR 是绕不开的坎儿。我记得 2010 年前后,RNA-seq 刚开始火起来,大家还在为「怎么处理那些几百万条的 reads 数」发愁。那时候有个叫 Mark Robinson 的哥们儿写了一篇论文——对,就是 2010 年那篇《edgeR: a Bioconductor package for differential expression analysis of digital gene expression data》。

从那时起,edgeR 就成了处理计数数据的标杆工具。说白了,它最核心的思路就四个字:小样本、多重复。你想想看,生物学重复就那么 2-3 个,传统统计方法根本不敢这么玩。edgeR 却用了一个巧妙的办法——借力打力,借用所有基因的信息来稳定单个基因的方差估计。

2.1 历史背景:为什么会有 edgeR?

在 edgeR 诞生之前,做差异表达主要靠 Microarray 的方法,比如 limma。但 RNA-seq 的数据是离散的整数计数,不是连续的荧光信号强度。强行用 limma 去处理,结果往往很惨——假阳性满天飞。

我刚开始接触生物信息学的时候,导师扔给我一个 RNA-seq 数据集说:「你跑个差异表达看看」。我拿 limma 一跑,出来几百个显著基因。高兴了没两天,一验证,全是假的。那时候我才意识到:计数数据的分布假设完全不一样。

edgeR 的核心贡献在于:它把「负二项分布」引入了这个领域。这背后有个特别直观的物理意义——你想想,同一个基因在同一个条件下测三次,每次的计数不可能一模一样。这种波动既有「技术重复」带来的,也有「生物学重复」带来的。泊松分布只能描述前者,而负二项分布多了一个分散参数,刚好能把生物学变异也包进来。

一句话总结历史: edgeR 是第一个专门为「少量生物学重复 + 离散计数数据」设计的差异表达工具,开创了负二项分布在 RNA-seq 分析中的应用先河。

2.2 核心算法:edgeR 到底干了什么?

你以为 edgeR 只是跑个负二项检验就完事了?不是的,它的算法管线其实相当有层次感。我个人习惯把它分成三个关键步骤:

  1. 计算归一化因子(TMM 归一化)
  2. 估计分散参数(经验贝叶斯 + 条件似然)
  3. 精确检验 / 似然比检验(判断基因是否差异表达)

下面这张图我把它们串起来了,你看完就明白整体的脉络:

edgeR 核心算法流程 原始计数矩阵 (counts) TMM 归一化因子 经验贝叶斯 + 条件似然分散估计 精确检验 / GLM 似然比检验 差异表达基因列表 关键:分散估计好坏直接影响 假阳性率,是 edgeR 的核心

2.2.1 精确检验与经验贝叶斯

edgeR 最初的版本用的是「精确检验」。这名字听着吓人,其实原理很简单——既然我们有负二项分布的假设,那就直接算这个基因在两组间的「计数差异」是随机出现的概率。

为什么需要经验贝叶斯?
因为样本数太少。我举个例子:假设一个基因在对照组测了 3 个样本,在实验组测了 3 个样本。只用这 6 个数据点去估计分散参数,结果会很离谱。edgeR 的做法是:用所有基因的计数信息去「挤压(shrink)」单个基因的分散估计——这就是经验贝叶斯的精髓。

我的经验: 如果在项目中遇到「对照组和实验组各只有 2 个样本」的情况——别慌,edgeR 在这类场景下反而比很多复杂模型更靠谱。因为它从一开始就是为「少样本」设计的。

2.2.2 基于条件似然的分散估计

到了 edgeR 的后期版本(3.x 以后),加入了 estimateDisp() 函数。这里用了一个叫「条件似然」的技术。你想想看,每个基因的计数总和是已知的,在这个总和的约束下,分散参数的估计可以被大大稳定化。

嗯,这里要注意一个细节:edgeR 实际上是针对基因的「均值-方差关系」做了半参数建模。它假设分散参数随着基因表达水平的变化而变化——低表达的基因本身波动就大,高表达的基因波动就小。这个假设在实践中是非常合理的。

# edgeR 核心估计步骤
library(edgeR)

y <- DGEList(counts = count_matrix, group = group_vector)
y <- calcNormFactors(y)            # TMM 归一化
y <- estimateDisp(y)               # 分散估计(经验贝叶斯)

# 检验差异
et <- exactTest(y)
topTags(et, n = 10)

2.2.3 TMM 归一化

为什么要单独拎出 TMM 来说?因为在 RNA-seq 分析中,归一化是决定成败的第一步。传统的 RPM/CPM 归一化忽略了「组成效应」——如果一个样本中很多高表达基因都被上调了,那低表达基因的 RPM 会被错误地压低。

TMM 的思路其实很朴素:在所有基因中找一个「参考样本」,然后对每个样本计算一个缩放因子。这个缩放因子不是简单的总 reads 比值,而是去掉高表达和低表达的基因后,用中间的基因来估算。

我曾经帮一个临床合作者排查差异表达结果不一致的问题。他拿了两批数据,一批用 RPKM 归一化,一批用 TMM 归一化,结果 top 差异基因几乎没有重叠。我建议他统一用 edgeR 的 TMM 归一化再跑一次,这次结果就对上了。
——归一化的选择真的不能随便。

注意: TMM 默认假设绝大多数基因没有差异表达。如果整个表达谱都被系统性地改变了(比如宏基因组跨物种比较),TMM 就不适用了。这时候得考虑其他归一化方法。

2.3 适用条件:什么场景选 edgeR?

没有万能的工具。edgeR 有自己明确的能力边界。我用一张表把它的适宜条件和雷区分清楚:

维度 edgeR 擅长 edgeR 不太擅长
样本量 每组 2-10 个样本 每组大于 50 个样本(有更高效的模型)
数据特性 离散计数(RNA-seq、CAGE、ChIP-seq 等) 连续数据(Microarray、蛋白质组学强度值)
实验设计 两组比较、简单因子设计 复杂多因子交互效应(此时用 edgeR 的 GLM 也可)
基因筛选 筛选少量高置信差异基因 需要排序所有基因做 GSEA 时(DESeq2 的效应量估计更稳定)
数据量 万级基因轻松跑 基因组级别(太大时内存消耗需要留意)

从我个人的项目经验来看,我特别喜欢在以下场景用 edgeR:

  • 早期探索分析: 想快速跑一遍,看看哪些基因有信号。edgeR 的 exactTest 跑一次也就几秒钟。
  • 样本质量参差不齐: 有些样本的测序深度差很多。TMM 归一化能很好地处理这种深度不一致。
  • 只关注 top 差异基因: 如果需要 p 值最显著的前几十个基因做 PCR 验证,edgeR 的假阳性控制是比较严格的。
一句话选型原则: 如果你的数据是 RNA-seq 计数,每组样本数在 3-10 之间,且目标是找少量显著性差异基因——优先用 edgeR。它轻量、稳定、经过十几年的检验。

2.4 一个避坑指南

我曾经接手过一个项目,上游给的 count 矩阵里有很多基因只有 1-2 个总计数。直接丢进 edgeR 跑,那些基因全变成了显著差异——因为极端低表达的基因,一分之一的波动都会被放大。后来我在预处理阶段加了一个简单过滤:必须至少在 min.count > 10 的样本中才能保留。从此假阳性就下来了。

# 我常用的过滤标准
keep <- rowSums(cpm(y) > 1) >= 2
y <- y[keep, , keep.lib.sizes = FALSE]

另外,别忽略 prior.count 参数。edgeR 默认加了 0.125 的 prior count,就是为了防止零计数导致的对数倍数变化炸裂。这个参数我一般不动,但在处理某些超稀疏数据时,稍微加大到 0.5 反而更稳定。