丰度数据基础:OTU/ASV表格、物种注释、元数据读入
做丰度数据分析,第一步就是搞清楚我们手里到底有什么数据。我刚开始接触这个领域时,拿到一个项目,对方丢给我一堆文件:有的叫 otu_table.txt,有的叫 taxonomy.txt,还有一堆 metadata.csv。当时我就懵了——这些文件到底长什么样?该怎么读进来?
其实说白了,丰度数据分析就三种核心数据:
- 丰度表(OTU/ASV 表格)—— 谁(物种)在多少个样本里出现了
- 物种注释 —— 这些物种从门到属的分类信息
- 元数据 —— 样本本身的实验条件、分组信息
这三者缺一不可。先把它们读进 R 里,后面的分析和调优才有根基。
1. OTU/ASV 表格:丰度的“骨架”
OTU 和 ASV 本质上是同一类东西——都是对测序序列进行聚类后得到的“代表单元”。区别只是聚类算法不同。我习惯用 ASV 表做分析,因为它的分辨率更高,能区分到单碱基差异。但不管哪种,表格的格式大同小异。
标准格式长这样:
# OTU ID Sample1 Sample2 Sample3
OTU_1 120 85 203
OTU_2 45 112 78
OTU_3 203 67 145
第一列是 OTU/ASV 的 ID,后面每列是一个样本,单元格里是丰度值(通常是整数)。
读入时我推荐用 read.table() 或 data.table::fread()。注意设置 row.names = 1 把第一列变成行名。如果文件里有注释行(以 # 开头),要加 comment.char = "#" 忽略掉。
# 用 data.table 快速读入
library(data.table)
otutab <- fread("otutab.txt", header = TRUE, sep = "\t")
otutab <- as.data.frame(otutab)
rownames(otutab) <- otutab[,1]
otutab <- otutab[,-1]
dim(otutab),确保行数(物种数量)和列数(样本数量)和预期一致。有一次我读入后发现有 0 列,才发现文件分隔符是逗号而不是制表符——这种坑太常见了。
2. 物种注释:给每个 OTU/ASV 贴上“身份证”
光有丰度表,你只知道每个样本里有多少个“单元”,但不知道它是什么。物种注释就是用来给每个 OTU/ASV 一个分类学标签:从界到门、纲、目、科、属、种,层层剥开。
典型的注释文件结构:
# OTU ID Kingdom Phylum Class Order
OTU_1 Bacteria Proteobacteria Gammaproteobacteria Enterobacterales
OTU_2 Bacteria Firmicutes Bacilli Lactobacillales
OTU_3 Bacteria Bacteroidetes Bacteroidia Bacteroidales
读入方法和丰度表类似,但要小心列分隔符。有时注释表会用制表符,有时用逗号。我见过有人在 Excel 里存成 CSV,结果分类名里自带逗号(比如 “Candidatus, Unknown”),导致读入时字段错位。解决办法是:用 quote = "\"" 或者直接改用制表符格式。
# 读入物种注释
tax <- read.table("taxonomy.txt", header = TRUE, sep = "\t",
row.names = 1, stringsAsFactors = FALSE)
# 查看前几行
head(tax)
注意:注释和丰度表必须用同一套 OTU/ASV ID。顺序可以不一致,后续用 merge() 或 match() 对齐即可。
setdiff() 检查 ID 是否一一对应。
3. 元数据:样本的“档案”
元数据记录了每个样本的实验信息:分组、时间、处理、个体编号等等。没有它,你的丰度表就是一堆冷冰冰的数字。
标准结构:
SampleID Group Time Treatment
Sample1 Control T0 Untreated
Sample2 Control T0 Untreated
Sample3 Treat T1 DrugA
读入方法同样简单。关键点是:样本名必须和丰度表的列名一致。
metadata <- read.csv("metadata.csv", row.names = 1)
# 检查样本名是否匹配
stopifnot(all(rownames(metadata) %in% colnames(otutab)))
match() 或 %in% 操作时更顺手。如果元数据里有多余的样本,或者丰度表里有多余的样本,一定要先做交集。我一般这样处理:
common_samples <- intersect(colnames(otutab), rownames(metadata))
otutab <- otutab[, common_samples]
metadata <- metadata[common_samples, ]
4. 三张表的整合:一篇结构图理清关系
我把这三张表的关系画成一张图。你看一眼,后面做 alpha 多样性、beta 多样性、差异分析时,心里就有数了。
5. 读入后的快速核查清单
数据读入后,我建议你按下面这张表逐项检查,避免后期分析跑出奇怪结果。
| 检查项 | 方法 | 预期结果 |
|---|---|---|
| 丰度表维度 | dim(otutab) |
行数>0,列数=样本数 |
| 丰度值类型 | class(otutab[,1]) |
整数或数值 |
| 注释表行数 | nrow(tax) |
等于丰度表行数 |
| 注释层级列数 | colnames(tax) |
至少包含Kingdom到Genus |
| 元数据样本名匹配 | all(rownames(metadata) %in% colnames(otutab)) |
TRUE |
| 元数据分组列数 | length(unique(metadata$Group)) |
符合实验设计 |
总结一下:
- 丰度表是核心,行是物种,列是样本。
- 物种注释是字典,告诉每个ID是什么分类。
- 元数据是样本的背景档案。
- 读入后立即做 ID 一致性检查,否则后续所有分析都是废的。
这三张表读入 R 后,我一般会直接塞进 phyloseq 包里的 phyloseq() 函数,生成一个统一对象。但那是下一部分的内容了。今天到这里,先把基础打牢。下次我们聊“数据清洗与过滤”——嗯,你会发现很多有趣的问题藏在那些 0 值和低丰度 OTU 里。