多批次数据合并:为什么非做不可?

做微生物组分析的人,多半会碰到一个头疼的事——手上有好几批数据,单独分析都好好的,一放到一起就乱套。我刚入行那年,接过一个项目,三批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(冗余分析)的校正策略。这些我们后面章节会细讲。

合并流程总览

我自己在项目中沉淀了一套流程,用一张图就能说清楚。

多批次数据合并流程与挑战 多批次数据合并流程与常见挑战 Batch 1 V3-V4 引物, MiSeq Batch 2 V4 引物, NovaSeq Batch 3 公共数据, 不同平台 数据合并(Merge) 格式统一 → 分类单元对齐 → 纵向拼接 常见的合并挑战 批次效应 分类单元不一致 测序深度差异 数据稀疏性 合并后的标准丰度表 样本×物种,可进行下游分析 合并前一定要做批次效应检测,不要跳过质量评估

合并前必须做的三件事

别急着动手。我每次合并前都会check三样东西,你也应该养成这个习惯。

  1. 检查分类单元命名格式——是"g__Bacteroides"还是"Bacteroides"?是"OTU1"还是"ASV_1"?统一格式,不然合并后你会多出一堆「新物种」。
  2. 检查样本元数据——至少要有批次标签、分组信息。我见过有人合并完才发现忘记记录哪个样本来自哪个批次,批次校正直接没法做。
  3. 画一张分布图——看看每个批次的测序深度、物种数、稀疏曲线。差异太大就要小心了。

特别提醒:合并后的丰度表一定要做标准化(相对丰度、CSS、TMM等)。直接拿原始计数做差异分析,结果会被高丰度批次主导。标准化方法我们下一章专门讲。

小结

多批次合并不是简单的表格拼接。批次效应、分类不一致、深度差异、稀疏性——每个都是能让你分析翻车的坑。我个人的经验是:宁可在合并前多花几天做质量检查和格式统一,也不要在合并后花几个月去排查「假阳性」结果。

如果你现在手头正有几批数据要合并,不妨先停下来,画一张PCoA看看批次效应有多严重。然后再决定用哪种策略——直接合并、最小公共分类单元合并、还是重新注释。选对了路,后面会顺很多。