1. DESeq2简介:RNA-seq差异表达分析背景与DESeq2的设计哲学

大家好,我是这次课的主讲。做RNA-seq数据分析这些年,我接触过不少工具。但说实话,DESeq2一直是我工具箱里的首选。为什么?因为它稳。嗯,咱们先从背景聊起。

1.1 RNA-seq差异表达分析,到底在解决什么问题?

你想想看,一个RNA-seq实验,最终拿到的是一堆测序读数。经过比对和定量,我们得到一个计数矩阵——行是基因,列是样本,每个格子是一个整数,代表某个基因在某个样本里检测到的片段数。

问题是:这些计数值,能直接拿来比大小吗?

不能。原因有三个:

  • 测序深度不一样——有的样本总reads多,有的少。直接比计数,深度高的样本所有基因看起来都上调。
  • 基因长度不一样——长基因更容易被测到,但这不代表它表达量更高。
  • 技术噪声大——生物学重复之间的波动,比普通实验大得多。低表达基因尤其明显。

所以,差异表达分析的核心任务就三个:

  1. 标准化——消除测序深度和基因长度带来的偏差
  2. 建模——用合适的统计模型描述计数数据的分布
  3. 假设检验——判断某个基因在处理组和对照组之间,表达量变化是否显著

我刚开始做RNA-seq时,用过老式的倍数变化直接筛选。结果呢?假阳性率高得吓人。后来才明白,统计建模这一步省不了。

1.2 DESeq2的设计哲学:稳健、严谨、可解释

DESeq2是Love等人2014年在Genome Biology上发表的。它脱胎于早期的DESeq,但做了大量改进。我个人习惯把它的设计哲学归纳为三点:

设计原则 具体做法 为什么重要
负二项分布建模 用负二项分布而非泊松分布描述计数数据 泊松要求均值等于方差,但RNA-seq数据方差远大于均值。负二项多了一个离散度参数,能更好拟合真实情况。
收缩估计 利用所有基因的信息,对每个基因的离散度进行收缩 低表达基因的方差估计极不稳定。收缩相当于借力——用全局趋势稳定局部估计。我在项目中遇到过一个小样本实验,没有收缩的话,差异基因列表几乎全是噪声。
对数倍数变化的收缩 对低表达、高变异的基因,强行拉低其倍数变化 这步很多人不理解。确实,它会降低部分基因的fold change。但换来的是排名更可靠,假阳性更少。说白了,我们宁可得不到一个巨大的倍数变化,也要确保这个变化是真的。

核心一句话:DESeq2不做简单的标准化,它用统计模型来"理解"数据的结构,然后把真实信号从噪声中剥离出来。

1.3 从计数矩阵到差异结果:一条清晰的流水线

下面这张图,是我每次讲DESeq2必画的。它把整个分析流程串起来了。你先看,我再解释。

DESeq2 标准分析流程 输入:计数矩阵 DESeqDataSet 对象构建 大小因子估计 + 离散度估计 + 收缩 负二项分布拟合 + Wald检验 输出:结果表 + 可视化数据 注意:参数调整主要影响第3步和第4步,从而改变最终的可视化结果

我解释一下这张图:

  • 第一步:输入原始计数矩阵。注意,DESeq2要求整数计数,不接受FPKM或TPM。我曾经被人问过能不能用RPKM跑DESeq2,答案是不行——那个标准化方式不一样。
  • 第二步:构建DESeqDataSet对象。这一步你提供counts、实验设计公式和样本信息。实验设计这一步很关键,写错了公式,后面所有结果都跑偏。
  • 第三步:核心建模。估计大小因子做标准化,然后估计每个基因的离散度,最后做收缩。这里有几个参数可以调,比如fitType、minReplicatesForReplace,会影响后面的结果。
  • 第四步:拟合负二项模型,做Wald检验,给出p值和校正后的padj。
  • 第五步:输出结果表。你用它来做火山图、MA图、热图,都是基于这里产出的log2FoldChange和padj。

1.4 为什么参数调整会影响可视化?

你做一张火山图,x轴是log2FoldChange,y轴是-log10(padj)。那问题来了:

  • 如果收缩强度不同,同一个基因的log2FoldChange可能差0.5甚至1倍。它在火山图上的位置就变了——可能从显著下调变成不显著。
  • 如果离散度估计的方法不同,p值分布会整体偏移,显著基因的数量可能翻倍或腰斩。
  • 如果独立过滤阈值不同,padj校正后的基因列表可以差几十到上百个。

所以,参数调整不是单纯的数学游戏。它直接决定了你看到的图长什么样,以及你最后汇报的差异基因列表是哪1000个还是哪500个。

我个人习惯:做正式分析前,先跑一遍默认参数,看看数据的整体质量。然后再根据实验设计调一两处关键参数,比较输出表的变化。千万不要一次调七八个参数,到时候变了都不知道哪个参数引起的。

1.5 一段最简的R代码

按惯例,看一段核心代码。你感受一下DESeq2的接口有多简洁:

library(DESeq2)

# 构建数据对象
dds <- DESeqDataSetFromMatrix(
  countData = counts_matrix,
  colData = sample_info,
  design = ~ condition
)

# 核心分析
dds <- DESeq(dds)

# 提取结果
res <- results(dds, contrast = c("condition", "treated", "control"))

如果你跑过这段代码,你会发现它几乎没有选项。那是因为DESeq把大部分默认参数设得很合理。但恰恰是那些没写出来的参数,比如betaPrior、independentFiltering、cooksCutoff,才是我们后面要重点讲解的。

注意:千万不要以为跑通默认参数就万事大吉了。对于小样本、高噪声、低表达基因占比大的数据,默认参数不一定最优。我见过有人拿3个生物学重复跑,出来2000个差异基因,结果复查时发现大部分是假阳性——离散度没有收缩好。

1.6 本章小结

到这里,我对DESeq2的背景和设计哲学做了一个梳理。你至少要知道:它为什么用负二项分布,哪些参数可以调,以及这些调整最终会怎么影响你画的图。后面的章节,我们会逐个参数拆解,看它们到底在做什么,以及影响有多大。

好了,这一章就到这里。记住一句话:用好DESeq2,不是会跑代码,而是理解每一步在干什么。