4. 基本结果提取:results函数参数(contrast、alpha、lfcThreshold)
跑完 DESeq2 分析,拿到 dds 对象之后,最核心的一步就是提取差异表达结果。
说白了,results() 就是咱们的「数据筛子」。你希望从几万个基因里捞出哪些?这个函数的几个参数直接决定了你的输出长啥样。
我最早用 DESeq2 时,总觉得这步很简单——直接调用 results(dds) 就行。结果后来发现,同一个项目,同事跑出来的基因列表跟我完全不一样……嗯,那才意识到参数设置有多关键。
4.1 contrast 参数:指定你要比较哪两组
contrast 是 results() 里我使用最频繁的参数。
它解决了什么问题?你的实验设计可能有多个分组(比如:Control、Treatment_A、Treatment_B)。你总不能把所有组混在一起比。你需要明确告诉 DESeq2:我要看的是「哪两个组之间的差异」。
contrast 有三种写法:
# 写法一:指定因子名称和比较组(最常用)
results(dds, contrast = c("condition", "Treatment_A", "Control"))
# 写法二:直接给数字索引
results(dds, contrast = c(2, -1, 0, ...))
# 写法三:使用名称表达式(不推荐新手)
results(dds, name = "condition_Treatment_A_vs_Control")
| 写法 | 是否推荐 | 原因 |
|---|---|---|
| c("因子名", "分子组", "分母组") | 强烈推荐 | 可读性高,不容易搞混 |
| 数字索引 | 谨慎使用 | 一旦因子水平顺序改变,结果就错 |
| name 表达式 | 了解即可 | 自动生成的名称有时很拗口 |
4.2 alpha 参数:调显着性阈值
alpha 这个参数,我建议你理解成一个「截断点」。默认值是 0.1,但你的生物学背景或审稿人可能要求更严格的标准。
# 严格模式(大多数文章用这个)
res_strict <- results(dds, alpha = 0.05)
# 默认模式
res_default <- results(dds, alpha = 0.1)
# 宽松筛选(探索性分析可用)
res_explore <- results(dds, alpha = 0.2)
需要区分两个概念:
- pvalue:原始统计检验的 p 值
- padj:多重假设检验校正后的 q 值(BH 方法)
DESeq2 默认用 independent filtering 策略。你设定的 alpha 值会影响这个过滤强度。alpha 越小,过滤掉的基因越多,剩下来的「假阳性风险」越低。
4.3 lfcThreshold 参数:不只是看 p 值
很多初学者只盯着 p 值和 padj,觉得 < 0.05 就是差异基因。但做生物信息学久了你就会发现:
统计显着 ≠ 生物学相关
一个基因 padj = 0.0001,但 log2FoldChange 只有 0.2。这种基因,你放在 RNA-seq 里大概率是测序误差,换个平台验证不出来。
lfcThreshold 就是为了解决这个问题而存在的。
# 默认情况:只看 fold change 是否为 0
results(dds, contrast = c("condition", "A", "B"))
# 设置一个 fold change 阈值:只有 |log2FC| > 1 才算有意义
results(dds, contrast = c("condition", "A", "B"),
lfcThreshold = 1)
设置了 lfcThreshold 后,DESeq2 会执行一个 Wald test,零假设是「真实的 log2FC 的绝对值 ≤ 你设定的阈值」。而不是默认的「真实的 log2FC = 0」。
svalue(取代 padj)。svalue 表示「这个基因的 log2FC 落在阈值以内的后验概率」。我个人觉得 svalue 在解释时更直观。
4.4 三个参数协同工作
这三个参数不是孤立的。它们配合起来,能让你提取出更高质量的结果。
# 经典组合:严格筛选
res_final <- results(dds,
contrast = c("condition", "Treatment", "Control"),
alpha = 0.05,
lfcThreshold = 1)
这个命令做了三件事:
- contrast:指定 Treatment vs Control 的比较
- alpha:多重检验校正阈值设为 0.05
- lfcThreshold:要求 log2FC 至少大于 1 或小于 -1,才算「生物学上有意义」
为什么要用 lfcThreshold?我遇到过这种情况:某个基因 padj = 0.001,看起来很厉害。但 log2FC = -0.3。在 qPCR 验证时,死活做不出来。后来发现是测序深度不均导致的假象。从那以后,我对那些 fold change 很小的「显着基因」都保持警惕。
4.5 核心逻辑:从原始统计到可解释结果
下面这个图我经常在培训里用,它概括了 results() 函数的三个核心参数分别控制着差异表达分析的哪一层。
你看这个图就会明白:contrast 决定了「比什么」,alpha 决定了「多严格」,lfcThreshold 决定了「多大才值得报」。三个参数串起来,最终决定你报告里是哪张基因列表。
thresholds <- c(0, 0.5, 1, 1.5)
sapply(thresholds, function(t) {
res <- results(dds, contrast = c("condition", "A", "B"), lfcThreshold = t)
sum(res$padj < 0.05, na.rm = TRUE)
})
这样你就能直观看到:随着生物学意义门槛提高,还能剩下多少基因。
# 每次跑完 results,我都会习惯性跑一下这个
summary(res_final)
它能告诉你:总共有多少基因,多少上调,多少下调。如果数字异常(比如上调基因数离谱),赶紧回头查你的 contrast 有没有写反。
4.6 参数速查表
| 参数 | 默认值 | 作用 | 常见设置 |
|---|---|---|---|
| contrast | 无 | 指定比较的分组对 | c("condition", "B", "A") |
| alpha | 0.1 | 多重检验校正后的显着阈值 | 0.05(严格), 0.2(探索) |
| lfcThreshold | 0 | log2 fold change 的绝对阈值 | 1(两倍), 1.5(三倍) |
| altHypothesis | "greaterAbs" | 备择假设方向(与 lfcThreshold 配合) | "greater", "less" |
| independentFiltering | TRUE | 是否启用独立过滤 | 一般不动,除非特殊需求 |
4.7 避坑指南
- 忽视 contrast 的方向 —— 我同事曾经把分子和分母写反了,结果认为上调的基因其实是对照组上调。报告发出去后才发现,非常尴尬。
- 只看 p 值不看 fold change —— 这是我自己的教训。某些低表达基因的 padj 很小,但 log2FC 连 0.5 都不到。这类基因在独立验证时存活率极低。
- alpha 和 lfcThreshold 混用而不理解逻辑 —— 设置 lfcThreshold 后,DESeq2 的统计检验模型会变化。不能用默认的 pvalue 直接判断,要看 svalue。
好了,results() 的这三个参数,你理解透了吗?我建议你打开自己的 dds 对象,挨个试试不同的 alpha 和 lfcThreshold 组合,对比一下输出结果的变化。亲身跑一遍,比看十遍文档都管用。