4. DGEList 对象:创建、分组与注释
说到 edgeR 的实战,第一步就是造出 DGEList 对象。这个对象就像是整个分析管的入口——你喂给它 counts 矩阵、分组信息、基因注释,它才能开始做 normalization、dispersion 估计、差异检验。我在项目里见过不少新手直接把原始 count 矩阵扔进 edgeR 函数里,结果跑出来的结果乱七八糟。为什么?因为 DGEList 没建对。
说白了,DGEList 就是 edgeR 的统一数据容器。它把表达量、样本属性、基因特征全打包在一起。你后面写的 calcNormFactors、estimateDisp、glmFit 全都依赖这个对象。所以这一章,我们就把它彻底搞明白。
4.1 从 counts 矩阵创建 DGEList
假设你手头有一个 counts 矩阵——行是基因,列是样本。值都是整数,代表 mapping 到每个基因上的 read count。这种情况最常见。
counts <- matrix(rnbinom(1000, mu=100, size=10), nrow=100, ncol=10)
rownames(counts) <- paste0("gene", 1:100)
colnames(counts) <- paste0("sample", 1:10)
# 最简洁的创建方式
y <- DGEList(counts = counts)
你瞧瞧,一行代码就搞定了。但这时候 y 对象里只有 counts 数据,没有分组信息。分组是什么?就是你的实验条件——对照组 vs 处理组,或者时间点 A vs 时间点 B。
记住: DGEList 默认把样本当独立个体,不做组间比较。你必须主动告诉它分组结构。
4.2 指定分组因子
分组因子(group factor)是 edgeR 里最重要的元数据。它决定了后续的 normalization 和 dispersion 估计怎么按组聚合。我个人习惯在创建 DGEList 时就把分组带上,一步到位。
group <- factor(c("control", "control", "control",
"treatment", "treatment", "treatment",
"control", "treatment", "control", "treatment"))
y <- DGEList(counts = counts, group = group)
这里有个坑:factor 默认按字母顺序排列水平。如果你的对照组是 "control" 而处理组是 "treatment",字母顺序正好是 control 在前,没问题。但万一你的组名是 "WT" 和 "KO"?字母顺序会变成 "KO" 在前,"WT" 在后,但你想让 WT 作为 baseline?
我曾经在 RNA-seq 项目里就是因为没注意因子水平顺序,导致差异分析时 baseline 选错,DE 基因结果翻了一倍。后来加上 levels 参数才纠正回来。
正确做法是显式指定参考水平:
group <- factor(group, levels = c("WT", "KO")) # 明确 WT 为 baseline
你也可以在创建完 DGEList 之后再补分组:
y$samples$group <- factor(c("ctrl","ctrl","trt","trt","ctrl","trt","ctrl","trt","ctrl","trt"),
levels = c("ctrl", "trt"))
效果一样,只是更灵活——比如你从外部 CSV 读入样本信息表,然后直接赋值到 y$samples 里。
4.3 添加基因注释信息
差异分析完成后,你总要知道每个基因是干啥的吧?基因注释就是干这件事的。常见注释包括基因符号(symbol)、染色体位置、Entrez ID、GO 号等等。
添加注释有两种主流方式:
- 直接从现有对象传入:另一个 DGEList 的
$genes。 - 从外部数据框构建:读入 CSV/TXT,再赋值给
y$genes。
假设你手头有一个注释数据框:
gene_annot <- data.frame(
gene_id = rownames(counts),
symbol = sample(c("TP53", "BRCA1", "EGFR", "MYC", "KRAS"), 100, replace = TRUE),
chr = sample(c("1", "2", "3", "5", "7"), 100, replace = TRUE),
biotype = rep("protein_coding", 100)
)
y <- DGEList(counts = counts, group = group, genes = gene_annot)
注意:genes 数据框的行名必须与 counts 矩阵的行名一一对应。反过来,如果你已经有一个 y 对象,也能后加:
y$genes <- gene_annot # 直接赋值
小提示: 我习惯用 biomaRt 包从 Ensembl 自动拉取注释,然后 merge 到 y$genes。这样能保证基因 ID 和符号的一致性,省去手工对表的时间。
4.4 DGEList 对象的结构一览
创建完成后,用 str(y) 看看它的内部结构。你会发现它是个列表,包含两个主要数据框:
| 成分 | 说明 | 行/列 |
|---|---|---|
y$counts | 原始 count 矩阵(整数) | 基因 × 样本 |
y$samples | 样本信息表,包含 group、lib.size、norm.factors 等 | 样本 × 元数据 |
y$genes | 基因注释表,可选 | 基因 × 注释列 |
# 查看样本信息
head(y$samples)
# 查看基因注释
head(y$genes)
嗯,这里要提醒一点:y$samples 里的 lib.size 列是自动计算的总文库大小(每列 counts 之和)。你无需手动设置,但如果你用外部计算的文库大小覆盖,也没问题——只是不推荐,除非你特别清楚自己在做什么。
4.5 核心流程一览
我画了一张图,把从创建到注释的完整流程串起来,你看一眼就明白 DGEList 怎么一步步搭起来了。
你回想一下,整个创建过程其实就三步:
- 准备 counts 矩阵(必须)
- 设定分组因子(建议在创建时一并传入)
- 附加基因注释(可选,但强烈推荐)
重要提醒: DGEList 对象创建后,y$samples$group 的因子水平不能随意改,否则之前存的 dispersion 估计全需要重算。我在一个项目里半路改了分组顺序,结果下游结果全要重跑——那滋味,你懂吧。