跳到正文
百沐一下百沐一下
本节目录

表达矩阵与分组表整理规范

跟完这篇,您能把手头的数据整理成两张对得上的表——一张表达矩阵、一张分组表,直接喂给差异分析,而不再为「样本名对不上」返工。

差异分析(比如 DESeq2)其实只认两样东西:一张放数值的表达矩阵,一张说明分组的分组表。把这两张表做规范,后面九成的报错都不会发生。本篇按顺序带您把它们整理到位,每一步都给出可以照抄核对的 R 代码。

先分清两张表的分工

动手前,先记住两张表各管一件事:

文件管什么一句话
表达矩阵每个基因在每个样本里的数值行是基因,列是样本
分组表每个样本属于哪一组、扮演什么角色一行一个样本

两张表通过样本名这一把钥匙连起来:矩阵的列名,要和分组表里的样本名一一对上。整理的全部功夫,基本都花在「让这把钥匙严丝合缝」上。

整理表达矩阵

表达矩阵就是一张二维表:行是基因(或其他特征),列是样本,每个格子放一个表达量数值。第一列是基因 ID,第一行是样本名。

一个 3 行 3 列的示意(真实数据往往上万行):

gene_idSample_1Sample_2Sample_3
ENSG0000014151012041187985
ENSG0000001204830245388
ENSG00000133703556753216012

几条容易忽略的点:

  1. 第一列是基因 ID,作为行名;剩下每一列对应一个样本。
  2. 数值列只放数字,别混进单位、备注或全角符号,否则整列会被当成文本。
  3. 缺失值统一留空或写 NA,不要「-」「/」「未测」各写各的。
  4. 差异分析通常要原始 count(未归一化的整数),别先算成 TPM 再传。count、CPM、TPM 各用在什么场景,见表达量单位怎么选

读进 R 核对一下形状,是个好习惯:

counts <- read.csv("counts.csv", row.names = 1, check.names = FALSE)
dim(counts)          # 看行数(基因数)和列数(样本数)
head(counts[, 1:3])  # 瞄一眼前几行几列

check.names = FALSE 很关键:不加的话,R 会把 Sample-1 这类列名偷偷改成 Sample.1,之后就和分组表对不上了。

顺带提醒一个老坑:别让 Excel 改坏基因名。 Excel 打开 CSV 时,会把 SEPT2MARCH1DEC1 这类基因 Symbol 自动变成日期,把 ENSG00000141510 这种长数字变成科学计数法,存回去就永久坏了。对策很简单:全程用纯文本格式(CSV/TSV,UTF-8 编码)读写,尽量不拿 Excel 中转;非要在表格软件里看,也要把基因 ID 列先设成「文本」再打开。

用稳定 ID 当行名,并去掉重复

第一列的基因 ID,建议用 Ensembl ID(如 ENSG00000141510)当主键,而不是基因 Symbol(如 TP53)。

原因很直接:Symbol 会随版本更名、存在别名,还常出现多个写法指向同一个基因;Ensembl ID 稳定、唯一,机器读起来没有歧义。手头只有 Symbol 也能用,但更稳妥的做法是转成 Ensembl ID 当主键,Symbol 另留一列做人看的备注。ID 之间怎么转换、注意哪些坑,见基因 ID 转换

**去重要在设行名之前做。**做完 ID 转换,常会碰到同一个 ID 占了好几行(多探针、多转录本映射到同一基因)。行名不允许重复,所以要先在「gene_id 还是普通一列」的状态下去重,再把它设成行名。一个被广泛采用的惯例是:同一个基因保留平均表达最高的那一行(这是常用做法、不是铁律,按数据和目的也可换成求和或取中位数)。

library(dplyr)

# 此时 counts 里 gene_id 是普通的一列(刚做完 ID 转换,可能有重复)
counts <- counts %>%
  mutate(.avg = rowMeans(across(where(is.numeric)))) %>%  # 算出每行的平均表达
  arrange(desc(.avg)) %>%                                 # 平均高的排前面
  distinct(gene_id, .keep_all = TRUE) %>%                 # 每个 ID 只留第一行(即平均最高的一行)
  select(-.avg)

rownames(counts) <- counts$gene_id   # 去重后再把 ID 设成行名
counts$gene_id <- NULL

去重后再确认一次没有重复:

any(duplicated(rownames(counts)))    # 应为 FALSE

如果您更习惯直接对重复项取平均,limma::avereps() 也能一行完成,只是它是「求平均」而非「保留最高」,含义略有不同,按需要选一种即可。

整理分组表

分组表告诉工具每个样本属于哪一组。最省事的写法是两列起步、协变量各占一列:

  • sample:样本名,必须和表达矩阵的列名完全一致
  • group:这个样本的分组,比如 tumor / normaltreat / control
  • 协变量列(可选):批次、性别这类需要在模型里校正的因素,各占一列,如 batchsex

示意:

samplegroupbatch
Sample_1tumorb1
Sample_2tumorb2
Sample_3normalb1

三个细节:

  1. 同一组的 group 值要写得一模一样——tumorTumor 会被当成两组。
  2. 一行只写一个样本,一列只放一种信息;批次、性别别塞进样本名里,各开一列。
  3. **谁做参照要心里有数。**差异分析里 log2FC 的正负,取决于哪一组被当成参照(比如「tumor 相对 normal」)。在 R 里把参照组设成因子的第一个水平,方向就固定了:
meta$group <- factor(meta$group, levels = c("normal", "tumor"))
# 上面把 normal 设为参照,结果里的 log2FC 就是「tumor 相对 normal」

这一步怎么接进 DESeq2 的 design,见差异分析(DESeq2)

让两张表对齐

**样本名一一对应,是差异分析里最常见的报错来源。**分组表的 sample,要和表达矩阵的列名一个不多、一个不少地对上。用几行 R 代码就能查清楚:

meta <- read.csv("meta.csv", check.names = FALSE)

# 谁在矩阵里、不在分组表里
setdiff(colnames(counts), meta$sample)
# 谁在分组表里、不在矩阵里
setdiff(meta$sample, colnames(counts))

两行结果都返回 character(0),才说明名字集合完全一致。常见对不上的原因:

现象多半是因为
大小写不同Sample_1sample_1
分隔符不同Sample_1Sample-1Sample.1
多余空格Sample_1 末尾带了一个空格
少写或多写某个样本忘了写进分组表,或写了矩阵里没有的样本

名字集合一致后,还要让顺序也一致——DESeq2 等工具默认矩阵的列顺序和分组表的行顺序一一对应,顺序错了,分组就贴错了:

counts <- counts[, meta$sample]              # 按分组表顺序重排矩阵的列
all(colnames(counts) == meta$sample)         # 应为 TRUE

这一步返回 TRUE,两张表就正式对齐了,可以进差异分析。

上传前自查清单

动手跑分析前,花一分钟对照这张单子过一遍:

  1. 矩阵:行是基因、列是样本,第一列是基因 ID。
  2. 主键:用 Ensembl ID,行名无重复(any(duplicated(...))FALSE)。
  3. 数值:差异分析用原始 count,数值列里只有数字,没被 Excel 改坏。
  4. 分组表:sample / group 两列齐全,同组 group 值写法统一,参照组已定。
  5. 对齐:两张表样本名集合相同、顺序一致(上面两段代码都通过)。

这五条都打勾,基本就告别了整理阶段的返工。

在平台上跑一遍

在百沐一下上做这一步不用写代码:把表达矩阵和分组表两个文件传上去,用一句话说清您想比哪两组、谁做参照,平台会自动核对样本名、把对不上的地方标出来给您确认,再往下接差异分析。各类文件的格式与整理要求见上传数据的格式要求,或直接到 app.baimuyixia.com 传一份数据试试识别是否顺利。

相关