第3章 数据准备:输入数据格式要求,count矩阵构建,样本信息表的准备

讲DESeq2之前,我得先强调一件事:数据准备比跑差异分析本身更容易翻车。我接过不少"为什么跑出来全是NA"的问题,一查十有八九是输入格式出了岔子。说白了,DESeq2的要求不复杂,但稍微不注意,就给你报一堆看不懂的错。

本章咱们就把输入数据这件事掰开揉碎。主要说三块:

  • count矩阵到底长啥样?
  • sampleTable怎么建才能让DESeq2认出来?
  • 常见报错和我的避坑经验。

3.1 count矩阵:整数、无缺失、行为基因

DESeq2接收的是原始整数计数(raw counts),不是FPKM/TPM,也不是log后的值。为什么?因为它内部要用负二项分布建模,小数或者归一化后的数据会破坏统计假设。

3.1.1 基本格式

我通常把count矩阵存成一个data.frame或matrix,行是基因(或转录本),列是样本。比如这样:

GeneSample1Sample2Sample3
GeneA12095134
GeneB052
GeneC456389501
  • 所有值必须是非负整数(0可以,但不要有NA)
  • 行名必须是基因ID(Ensembl ID、symbol等,但不能有重复)
  • 列名必须是样本ID,与sampleTable中的样本名完全一致

核心:DESeq2只认整数。小数点、负数、NA进来,它会直接罢工。

3.1.2 从哪里获取count矩阵?

常用的上游流程:

  • featureCounts:输出一个表,包含Geneid和每个样本的count列。直接read.table读进来,取需要的列即可。
  • HTSeq-count:每个样本一个文件,需要合并。我习惯用merge或cbind手动拼。
  • Salmon / Kallisto / RSEM:输出的是估计count或TPM,需要先取"estimated counts"列,然后合并。

举个例子,从featureCounts输出构建count矩阵:

raw_counts <- read.table("featureCounts_results.txt", 
                         header=TRUE, row.names=1)
# 一般第1列是Geneid,后面列是样本名
# 取出需要的样本列(假设是第7到第9列)
count_matrix <- raw_counts[, 7:9]
colnames(count_matrix) <- c("Control1","Control2","Treat1")

注意:很多工具生成的count矩阵最后会有几列统计信息(如Chr、Start、End、Strand),记得去掉。

3.1.3 常见坑:非整数与缺失值

我曾经接手一个项目,跑DESeq2时报错“每个基因至少有一个样本大于0”,结果查了半天发现有一列是TPM(带小数)。还有个坑是Salmon输出的estimated counts有时候是浮点数,虽然大部分接近整数,但DESeq2还是会报warning。我的做法是:

# 四舍五入取整
count_matrix <- round(count_matrix)
# 确保没有NA
count_matrix[is.na(count_matrix)] <- 0

注意:不要用round处理salmon的估计count?我一般不推荐,因为DESeq2官方说可以用tximport直接生成整数count。后面第5章会细讲tximport。如果你手头只有浮点count,可以试试DESeqDataSetFromMatrix时设置countData = round(countData),但最好用专门的工具。

3.2 样本信息表(sampleTable):设计矩阵的“说明书”

DESeq2需要你提供一个data.frame,告诉它每个样本属于什么条件、批次等。这就是sampleTable。我习惯存成一个.csv文件,读进来保证列名规范。

3.2.1 基本结构

至少有两列:rownames(样本ID,与count矩阵列名一致)和因子列(如condition、batch)。

sampleconditionbatch
Control1Control1
Control2Control1
Treat1Treat2
Treat2Treat2
  • 样本ID必须唯一,且与count矩阵的列名一一对应(连拼写、大小写都不能错)
  • 因子列尽量写成字符或因子类型,不要用数字1/2代表条件,除非你清楚自己在做什么
  • batch等协变量列可以是非数值(如"batch1","batch2"),也可以是数值(但要作为因子处理)

3.2.2 构建示例

假设你有6个样本,3个对照、3个处理,来自两个批次:

sampleTable <- data.frame(
  sample = c("C1","C2","C3","T1","T2","T3"),
  condition = c("Control","Control","Control","Treat","Treat","Treat"),
  batch = c("batchA","batchA","batchA","batchB","batchB","batchB")
)
rownames(sampleTable) <- sampleTable$sample

我自己写代码时,习惯把rownames(sampleTable)设为样本名,方便后面DESeqDataSetFromMatrix直接按行名匹配。

3.2.3 与count矩阵对齐

运行DESeq2前,务必保证count矩阵的列顺序与sampleTable的行顺序一致。DESeq2内部会按rownames匹配,但如果有样本在count里不在sampleTable里,会报错。

我常用的检查:

all(colnames(count_matrix) %in% rownames(sampleTable))
all(rownames(sampleTable) %in% colnames(count_matrix))

小技巧:直接用match重排count矩阵的列,让它们按sampleTable的顺序排好:count_matrix <- count_matrix[, rownames(sampleTable)]。

3.3 一张图理清数据准备流程

下面这张SVG图概括了从原始测序数据到DESeq2输入的完整准备步骤。你可以照着这个流程自查。

DESeq2数据准备流程 DESeq2数据准备流程 原始测序数据(FASTQ) 比对/定量(STAR/featureCounts/Salmon等) count矩阵 整数、无NA、行为基因、列为样本 sampleTable 样本ID、条件、批次等因子列 DESeqDataSet DESeq()差异分析 关键检查点:count矩阵列名与sampleTable行名完全一致;count为整数;因子列不要误设成数值。

3.4 常见报错及我的避坑指南

3.4.1 "Error in DESeqDataSet(se, design) : every gene contains at least one zero"

这个错其实不是真的说所有基因都是零——而是提示你的count矩阵里有缺失值(NA)或者非整数。DESeq2内部要求所有值非负整数且没有缺失。我曾经遇到一个case,是因为HTSeq输出某个样本的计数文件里有一行"no_feature",被我读进来变成了NA。只要用is.na排查一下就能找到。

处理办法:

  • 检查count矩阵是否有NA
  • 检查是否有小数(用any(count_matrix %% 1 != 0))
  • 如果确实有基因在所有样本都是0,不用担心,DESeq2会自动过滤掉低表达基因。但报这个错通常不是全零的问题。

3.4.2 "Error: object 'condition' not found"

这个我碰到过好几次:设计公式里写了~ condition,但sampleTable里没有叫"condition"的列。有时候列名是"Condition"(大写C),或"group"。DESeq2是大小写敏感的。

建议:写设计公式之前,先colnames(sampleTable)看一眼。

3.4.3 "Error in checkForExperimentalErrors(object) : the model matrix is not full rank"

这是多重共线性问题。常见于:你把批次和条件完全混淆了(比如所有对照在batch1,处理都在batch2),设计公式写成~ batch + condition时,DESeq2无法区分。解决方法要么去掉batch项,要么重新设计实验让批次和条件交叉。

重要:批次校正不是万能的。如果你的实验设计本身就有混淆(batch和condition完全一致),任何统计方法都拆不开。设计实验时就要考虑交叉或部分重复。

3.4.4 count矩阵行名重复

有时候合并多个样本的计数时,基因名重复(比如不同转录本有相同gene symbol)。DESeq2会报错“duplicate rownames”。我习惯合并前先检查一下:any(duplicated(rownames(count_matrix)))。如果有重复,可以用aggregate按基因名求和,或者用make.names(unique=TRUE)加后缀区分。但我个人更推荐前者——同一个gene symbol的计数应该合并。

3.5 小结

数据准备阶段,花10分钟检查count矩阵和sampleTable,可以省掉后面几小时debug时间。我每次跑新项目都会跑下面这几行代码,算是我的个人习惯:

# 基础检查
stopifnot(is.matrix(count_matrix) || is.data.frame(count_matrix))
stopifnot(all(count_matrix == floor(count_matrix), na.rm=TRUE))  # 整数
stopifnot(!any(is.na(count_matrix)))                              # 无缺失
stopifnot(all(colnames(count_matrix) %in% rownames(sampleTable))) # 列名匹配
stopifnot(!any(duplicated(rownames(count_matrix))))               # 无重复基因

只要这几行过了,DESeq2的输入数据基本不会出幺蛾子了。