差异表达分析概述:概念、应用场景与常用工具简介

说实话,我刚接触生物信息学那会儿,对「差异表达分析」这个词很敬畏。听起来特别高大上,对吧?但后来在实验室里跟湿实验的同学聊多了,发现它其实就是回答一个很朴素的问题:两组样本里,哪些基因的表达量真的不一样了。

我个人习惯把这事比作「大海捞针」。RNA-seq 一次能测到两万多个基因,大多数基因在两组之间其实没啥变化。我们真正要找的,是那少数几个在疾病组里飙升或骤降的基因。这才是生物学故事的起点。

一句话定义:差异表达分析,就是通过统计检验,从成千上万个基因中筛选出在特定条件下具有显著表达变化的基因集合。

核心概念:两个维度,三个要素

要理解这个分析,你得先抓住两个维度:表达量和差异倍数。表达量是说一个基因在样本里有多少条读段(counts);差异倍数则是两组均值的比值关系。但光看差异倍数不够——你想想看,一个基因在组 A 有 1 个 counts,组 B 有 10 个 counts,差异倍数 10 倍,但 read 数太少,谁信呢?所以还需要 统计显著性(p 值)来把关。

实际项目中,我刚接手一批肿瘤数据时,有个同事只按差异倍数排序,找了 300 个「显著」基因。结果用 qPCR 验证,一大半都没重现。为什么?因为他忽略了低表达基因的噪音。这就是典型的「只看倍数,不看 counts」的坑。

要素 含义 常见指标
差异倍数(Fold Change) 两组表达量的比值 log₂FC
表达丰度(Abundance) 基因在样本中的绝对或相对计数 CPM, FPKM, TPM
统计显著性 差异是否由随机误差引起 p-value, FDR / adjusted p-value

你可能已经猜到了,大多数差异表达工具都在解决一个核心矛盾:怎么在少量生物学重复下,准确估计基因表达的离散度。嗯,这里要注意,生物学重复太重要了。我见过有人拿 2 个重复就敢跑差异分析,结果一跑出来十有八九是假阳性。这不是工具的问题,是实验设计本身就缺腿。

核心应用场景:你在哪儿会用到它?

差异表达分析几乎是所有转录组项目的「标配第一步」。我从实际项目里总结了几种最常见的场景:

  • 疾病 vs 正常对照:寻找致病机制中的关键基因,比如肿瘤中上调的癌基因。
  • 处理 vs 对照:药物处理前后,哪些基因响应了?这是在药理学里最常见的用法。
  • 时间序列:同一个体在不同时间点的表达变化,常用于发育生物学。
  • 不同组织或细胞类型对比:敲除一个转录因子后,下游哪些靶基因变了。

记得有一次我帮一个做植物抗旱的团队分析数据。他们想知道干旱处理下,某种水稻的根部和叶片分别有哪些基因被诱导。实际上,这类对比分析用到的方法跟肿瘤数据基本上是一样的。只是你的样本数可能更少,所以更需要稳健的统计模型。

常用工具简介:不只是 edgeR 和 DESeq2

你可能会问,为什么要用这么多工具?不能一个算法打天下吗? 我刚开始也有这个疑问。后来跟一个做统计的老兄聊完才明白——不同的工具,对「离散度」的假设不一样,所以适用的场景也不同。

下面这张 SVG 图可以帮你理清这些工具的「家族谱系」和适用边界。

一个习惯: 我一般会先用一种工具跑一遍,再用另一种工具验证。如果两个工具都指向同一个基因,那这个基因的可靠性就高很多。
差异表达分析常用工具概览 差异表达工具 早期工具(两样本t检验) 基于负二项的工具 基于正似然的工具 Cuffdiff edgeR DESeq2 voom+limma 各工具的模型假设与适用场景 • edgeR / DESeq2:均基于负二项分布,适合少量重复(3-6个) • voom+limma:先将 counts 转成 logCPM,再用线性模型,重复较多时效果好 • Cuffdiff:基于 FPKM,处理 reads 比对结果,适合没有 replicate 的初步探索 • 早期 t 检验工具:已不推荐,因为忽略负二项离散度估计 本课程核心对比:edgeR vs DESeq2

从图上可以看到,edgeR 和 DESeq2 都属于「基于负二项分布」的工具。这一底层假设非常关键——RNA-seq 计数数据通常远高于均值(过离散),负二项分布正好能描述这种特性。我见过有人把 counts 直接拿去跑 t 检验,结果到处是假阳性。其实只要理解了这个统计特性,你就不会犯这种错。

几行代码感受一下差异分析的味道

下面是 edgeR 的典型工作流。你大概看一眼就行,后面章节会拆开细讲。

library(edgeR)

# 读入 counts 矩阵,行为基因,列为样本
counts <- read.csv("counts.csv", row.names = 1)
group <- factor(c("control", "control", "treat", "treat"))

# 构建 DGEList 对象
dge <- DGEList(counts = counts, group = group)

# 过滤低表达基因
keep <- filterByExpr(dge)
dge <- dge[keep, , keep.lib.sizes = FALSE]

# 归一化
dge <- calcNormFactors(dge)

# 估计离散度
dge <- estimateDisp(dge)

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

这段代码虽然短,但你仔细看——每一步都有它的道理。过滤是为了去掉那些压根没表达的基因;归一化是修正测序深度和文库组成的偏差;离散度估计是 edgeR 的「秘密武器」。等后面我们讲到参数调整时,你会更清楚为什么这一步这么关键。

我曾经踩过的坑: 有次帮人分析数据,客户说「我的数据已经归一化好了,你直接跑差异分析吧」。我信了,直接拿归一化后的 FPKM 矩阵用 edgeR 跑,结果一跑出来全是不显著的。后来发现,edgeR 的模型必须依赖整数 counts 和内部归一化步骤。用预归一化的连续值,相当于让工具在瞎猜离散度。这个教训我记得特别清楚。

选型初判:什么时候用 edgeR,什么时候用 DESeq2?

在实际项目中,我通常这样选:

  • 样本重复数少(2-4 个):我倾向用 edgeR。它在极小样本下的离散度估计更激进一些,有时能捡回一些被错过的基因。
  • 样本重复数中等(5-12 个):DESeq2 的表现通常更稳健,尤其是当你有多个因素(如处理 + 批次)要一起建模时。
  • 需要处理复杂实验设计:DESeq2 的广义线性模型(GLM)对多因素、交互效应非常友好。edgeR 也能做 GLM,但语法上我更习惯 DESeq2 的设计。
  • 数据量极大(100+ 样本):我试过跑 DESeq2 在 200 个样本上,慢得让人焦虑。edgeR 加上并行计算,快不少。
一个小建议: 如果你刚开始做差异表达分析,不妨先用默认参数跑一遍 edgeR 和 DESeq2,看两个结果的交集。交集里的基因通常是最可靠的。这不只是我的经验,很多生信社区的资深用户都这样操作。