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 怎么一步步搭起来了。

DGEList 创建与填充流程 DGEList 对象构建流程 counts 矩阵 基因×样本,整数 分组因子 指定 baseline 水平 基因注释 symbol, chr, biotype… DGEList 对象 $counts + $samples + $genes calcNormFactors TMM 标准化 estimateDisp 离散度估计 ⚠️ 分组因子水平顺序决定了差异分析的 baseline,建议用 levels 参数显式指定。 基因注释行名必须与 counts 行名一致,否则 edgeR 会报警告。

你回想一下,整个创建过程其实就三步:

  1. 准备 counts 矩阵(必须)
  2. 设定分组因子(建议在创建时一并传入)
  3. 附加基因注释(可选,但强烈推荐)

重要提醒: DGEList 对象创建后,y$samples$group 的因子水平不能随意改,否则之前存的 dispersion 估计全需要重算。我在一个项目里半路改了分组顺序,结果下游结果全要重跑——那滋味,你懂吧。