多批次数据合并:为什么非做不可?
做微生物组分析的人,多半会碰到一个头疼的事——手上有好几批数据,单独分析都好好的,一放到一起就乱套。我刚入行那年,接过一个项目,三批16S数据分别来自不同的测序中心,连引物版本都不一样。嗯,那叫一个酸爽。
说白了,多批次合并不是「把表格粘在一起」那么简单。你想想看,每批数据都有自己的「脾气」:实验条件、测序平台、分析流程,甚至同一个人不同时间跑的结果都会有差异。这些差异,我们叫它批次效应。
核心观点:批次效应不是噪声,是系统性偏差。不处理好它,你的生物学结论可能全是假的。
现实场景——什么时候需要合并?
我在项目中遇到过至少三种典型情况:
- 时间跨度的项目——同一个课题,样本分批次采集和测序,前后隔了几个月甚至一年。
- 多中心合作——不同实验室各自测了一批样本,最后汇总到一起分析。
- 公共数据整合——你从NCBI或EBI下载了多个研究的数据,想放在一起做比较或meta分析。
这些场景的共同点是什么?数据来自不同「批次」,但你想回答的生物学问题是同一个。
合并时的四个「拦路虎」
别急着写代码。先看看合并路上最常见的四个坑。我自己每个都踩过,说出来都是泪。
| 挑战 | 具体表现 | 后果 |
|---|---|---|
| 批次效应 | 同一样本在不同批次中丰度差异明显 | 聚类时样本按批次分开,而非按生物学分组 |
| 分类单元不一致 | 不同批次用了不同的数据库或注释工具 | OTU/ASV编号对不上,无法直接合并 |
| 测序深度差异 | 有的批次条数多(10万+),有的少(1万-) | 稀有物种检测率不同,α多样性被扭曲 |
| 数据稀疏性 | 合并后出现大量零值,尤其是低丰度物种 | 统计检验效能下降,相关性分析失真 |
我曾经踩过的坑:一次合并两个公共数据集,没检查分类单元命名格式。一个用"g__Bacteroides",另一个用"Bacteroides"。合并后同一个属被当成两个不同的物种。亏得我留了个心眼画了张热图,才发现不对劲。
常见的合并策略——三种思路
怎么应对上面的挑战?我总结了三种主流做法,各有各的适用场景。
策略一:直接合并(不推荐)
就是把几个丰度表按行(样本)或按列(物种)拼起来。前提是分类单元必须完全一致。说实话,现实中很少能满足这个条件。就算格式一样,批次效应也够你喝一壶的。
# 伪代码示例:直接合并(仅当分类单元完全一致时)
import pandas as pd
batch1 = pd.read_csv("batch1_abundance.csv", index_col=0)
batch2 = pd.read_csv("batch2_abundance.csv", index_col=0)
# 检查列名是否一致
assert list(batch1.columns) == list(batch2.columns)
# 直接纵向拼接
merged = pd.concat([batch1, batch2], axis=0)
merged.to_csv("merged_direct.csv")
我的建议:直接合并只在「同一实验室、同一流程、同一次测序」的批次间勉强可用。但凡换过引物、换过版本,就别偷这个懒。
策略二:按最小公共分类单元合并
这是比较务实的做法。把不同批次都向上归并到同一个分类层级(比如属级),再合并。损失一些分辨率,但换来一致性。
# 思路:统一到属水平再合并
def collapse_to_genus(table, taxonomy):
# table: 样本×OTU的丰度表
# taxonomy: OTU→属的映射
genus_table = table.groupby(taxonomy, axis=1).sum()
return genus_table
# 对每个批次分别归并
genus_b1 = collapse_to_genus(batch1, tax_map1)
genus_b2 = collapse_to_genus(batch2, tax_map2)
# 取公共属
common_genera = list(set(genus_b1.columns) & set(genus_b2.columns))
merged = pd.concat([genus_b1[common_genera], genus_b2[common_genera]], axis=0)
策略三:基于参考数据库重新注释
最彻底,但也最费劲。把所有批次的原始序列(或代表性ASV序列)拿过来,用同一个数据库、同一个流程重新跑一遍注释。这样从头到尾都一致,批次效应也最小。
但代价是计算量大,而且你得有原始序列文件。不是所有公共数据都提供这些。
我的推荐:有原始序列时,优先用策略三。没有的话,策略二是最稳妥的折中方案。策略一嘛...除非你确信两个批次是「双胞胎」。
批次效应的检测——合并前先看看
合并之前,我建议你先做件事:画一张PCoA图,按批次着色。如果样本点按批次扎堆,而不是按实验分组分布——恭喜你,批次效应实锤了。
# R语言示例:检查批次效应
library(vegan)
library(ggplot2)
# merged_otu: 合并后的OTU表(样本×物种)
# metadata: 包含Batch和Group信息
dist_mat <- vegdist(merged_otu, method = "bray")
pcoa_res <- cmdscale(dist_mat, k = 2, eig = TRUE)
pcoa_df <- data.frame(
PC1 = pcoa_res$points[,1],
PC2 = pcoa_res$points[,2],
Batch = metadata$Batch,
Group = metadata$Group
)
ggplot(pcoa_df, aes(x = PC1, y = PC2, color = Batch, shape = Group)) +
geom_point(size = 3) +
theme_minimal()
看到明显的批次分离,别慌。这说明你需要做批次校正,而不是放弃合并。常用的校正方法有ComBat、limma的removeBatchEffect、或者基于RRA(冗余分析)的校正策略。这些我们后面章节会细讲。
合并流程总览
我自己在项目中沉淀了一套流程,用一张图就能说清楚。
合并前必须做的三件事
别急着动手。我每次合并前都会check三样东西,你也应该养成这个习惯。
- 检查分类单元命名格式——是"g__Bacteroides"还是"Bacteroides"?是"OTU1"还是"ASV_1"?统一格式,不然合并后你会多出一堆「新物种」。
- 检查样本元数据——至少要有批次标签、分组信息。我见过有人合并完才发现忘记记录哪个样本来自哪个批次,批次校正直接没法做。
- 画一张分布图——看看每个批次的测序深度、物种数、稀疏曲线。差异太大就要小心了。
特别提醒:合并后的丰度表一定要做标准化(相对丰度、CSS、TMM等)。直接拿原始计数做差异分析,结果会被高丰度批次主导。标准化方法我们下一章专门讲。
小结
多批次合并不是简单的表格拼接。批次效应、分类不一致、深度差异、稀疏性——每个都是能让你分析翻车的坑。我个人的经验是:宁可在合并前多花几天做质量检查和格式统一,也不要在合并后花几个月去排查「假阳性」结果。
如果你现在手头正有几批数据要合并,不妨先停下来,画一张PCoA看看批次效应有多严重。然后再决定用哪种策略——直接合并、最小公共分类单元合并、还是重新注释。选对了路,后面会顺很多。