607 微生物源追踪(SourceTracker2 / FEAST)¶
一句话概述: 微生物源追踪(Microbial Source Tracking)通过比较"源"(source)和"汇"(sink)的微生物群落组成,定量估计目标样本中微生物的来源比例,回答"这些微生物从哪里来"的问题。
核心知识点速查表¶
| 知识点 | 白话解释 |
|---|---|
| 微生物源追踪(MST) | 定量判断一个样本中的微生物有多少来自各个已知来源 |
| Source(源) | 已知的微生物来源环境(如皮肤、口腔、土壤、粪便等) |
| Sink(汇) | 待分析的目标样本,想知道它的微生物从哪来 |
| Unknown(未知源) | 不能被任何已知source解释的部分 |
| SourceTracker2 | 基于贝叶斯+Gibbs采样的经典源追踪工具(2011年发表) |
| FEAST | 基于EM算法的快速源追踪工具(2019年,比SourceTracker快约300倍) |
| Gibbs采样 | 一种MCMC方法,通过迭代采样近似后验概率分布 |
| EM算法 | 期望最大化算法,通过迭代优化混合模型参数 |
| SourceID-NMF | 2024年新方法,用非负矩阵分解做源追踪 |
| FastST | 2025年新方法,支持方向性推断+速度更快 |
一、什么是微生物源追踪?¶
白话解释:
想象你在一条河里取了一杯水,发现里面有很多细菌。你想知道: - 有多少细菌来自上游的农场? - 有多少来自城市污水? - 有多少来自自然土壤? - 有多少是未知来源的?
微生物源追踪就是用数学方法回答这些问题。它把你的样本(sink/汇)看作多个来源(source/源)的"混合物",然后计算每个来源贡献了多少比例。
常见应用场景:
| 场景 | Source | Sink |
|---|---|---|
| 婴儿肠道菌群来源 | 母亲口腔、皮肤、阴道、母乳 | 婴儿粪便 |
| 水质污染溯源 | 人类粪便、牲畜粪便、土壤 | 河水/海水 |
| 室内微生物来源 | 人体皮肤、室外空气、土壤 | 室内灰尘 |
| FMT供体追踪 | 供体粪便、受体自身 | 受体术后粪便 |
| 手术感染追踪 | 患者皮肤、手术器械、空气 | 伤口分泌物 |
二、SourceTracker2详解¶
2.1 原理¶
SourceTracker2的核心思想:
1. 假设sink样本是多个source的混合
2. 用Dirichlet-Multinomial模型描述混合过程
3. 用Gibbs采样(MCMC)迭代估计每个source的贡献比例
4. 同时估计"unknown"(未知源)的比例
数学表达(简化版):
Sink = p1×Source1 + p2×Source2 + ... + pk×Sourcek + pu×Unknown
其中 p1+p2+...+pk+pu = 1(所有比例之和为1)
2.2 安装与运行¶
# ========== 安装SourceTracker2 ==========
# SourceTracker2是Python包
# 方法1:pip安装
pip install sourcetracker # pip安装
# 方法2:conda安装
conda install -c bioconda sourcetracker # conda安装
# 方法3:从GitHub安装(最新版)
git clone https://github.com/biota/sourcetracker2.git # 克隆仓库
cd sourcetracker2 # 进入目录
pip install . # 安装
2.3 输入数据准备¶
# ========== 准备输入数据 ==========
# SourceTracker2需要两个输入文件:
# 1. OTU/ASV丰度表(BIOM格式或TSV格式)
# 2. 样本元数据(标注source/sink分组)
import pandas as pd # 导入pandas
# 准备元数据文件(mapping file)
# 关键列:SampleID, SourceSink, Env
metadata = pd.DataFrame({
"SampleID": [
"skin1", "skin2", "skin3", # 皮肤source样本
"oral1", "oral2", "oral3", # 口腔source样本
"gut1", "gut2", "gut3", # 肠道source样本
"infant1", "infant2", "infant3" # 婴儿sink样本
],
"SourceSink": [
"source", "source", "source", # source样本标记为"source"
"source", "source", "source",
"source", "source", "source",
"sink", "sink", "sink" # sink样本标记为"sink"
],
"Env": [
"Skin", "Skin", "Skin", # source的环境类型
"Oral", "Oral", "Oral",
"Gut", "Gut", "Gut",
"Infant", "Infant", "Infant" # sink的环境标签(不参与计算)
]
})
# 保存元数据
metadata.to_csv("mapping_file.txt", sep="\t", index=False) # TSV格式保存
print("元数据准备完毕")
print(metadata)
2.4 运行SourceTracker2¶
# ========== 命令行运行SourceTracker2 ==========
# 基本用法
sourcetracker2 gibbs \
-i otu_table.biom \ # 输入:OTU/ASV丰度表(BIOM格式)
-m mapping_file.txt \ # 输入:元数据文件
-o st2_results/ \ # 输出目录
--source_sink_column SourceSink \ # 元数据中标注source/sink的列名
--source_column_value source \ # source的标签值
--sink_column_value sink \ # sink的标签值
--source_category_column Env \ # 元数据中标注环境类型的列名
--jobs 8 # 并行8个作业
# 高级参数
sourcetracker2 gibbs \
-i otu_table.biom \
-m mapping_file.txt \
-o st2_results_advanced/ \
--source_sink_column SourceSink \
--source_column_value source \
--sink_column_value sink \
--source_category_column Env \
--burnin 100 \ # 烧入期100次(预热迭代,丢弃结果)
--draws_per_restart 10 \ # 每次重启后抽取10个样本
--restarts 5 \ # 重启5次(提高结果稳定性)
--alpha1 0.001 \ # source的Dirichlet先验参数
--alpha2 0.1 \ # source环境间的Dirichlet先验
--beta 10 \ # sink的Dirichlet先验
--jobs 16 # 并行16个作业
# 输出文件解读:
# mixing_proportions.txt — 核心结果!每个sink中各source的比例
# mixing_proportions_stds.txt — 比例的标准差
2.5 结果可视化¶
# ========== R语言:SourceTracker2结果可视化 ==========
library(ggplot2) # 可视化
library(tidyr) # 数据整理
library(dplyr) # 数据操作
# 读取SourceTracker2的混合比例结果
proportions <- read.delim("st2_results/mixing_proportions.txt", # 读取结果
row.names = 1, # 第一列为行名
check.names = FALSE) # 不修改列名
# 转换为长格式(方便ggplot绑定)
props_long <- proportions %>%
tibble::rownames_to_column("Sink_Sample") %>% # 行名转列
pivot_longer(cols = -Sink_Sample, # 转为长格式
names_to = "Source", # source列名
values_to = "Proportion") # 比例值列名
# 堆叠柱状图
ggplot(props_long, aes(x = Sink_Sample, y = Proportion, fill = Source)) + # 绑定数据
geom_bar(stat = "identity", width = 0.8) + # 堆叠柱状图
scale_y_continuous(labels = scales::percent, # Y轴显示百分比
limits = c(0, 1)) +
labs(title = "Microbial Source Tracking Results", # 标题
x = "Sink Samples", # X轴
y = "Source Proportion", # Y轴
fill = "Source Environment") + # 图例
theme_minimal() + # 简约主题
theme(axis.text.x = element_text(angle = 45, hjust = 1)) # X轴标签旋转
# 饼图(单个样本)
single_sample <- props_long %>% filter(Sink_Sample == "infant1") # 选一个样本
ggplot(single_sample, aes(x = "", y = Proportion, fill = Source)) +
geom_bar(stat = "identity", width = 1) + # 柱状图
coord_polar("y") + # 转为饼图
labs(title = "Source Contributions to infant1") +
theme_void() # 无背景
三、FEAST详解¶
3.1 原理与优势¶
FEAST (Fast Expectation-maximization for Microbial Source Tracking)
核心区别于SourceTracker2:
- SourceTracker2用Gibbs采样(慢但理论性强)
- FEAST用EM算法(快约300倍!)
FEAST的EM算法流程:
E步:基于当前估计的混合比例,计算每个OTU来自各source的概率
M步:基于E步的概率,更新混合比例
重复E-M步直到收敛
FEAST优势:
1. 速度快300倍(大数据集时优势明显)
2. 支持多个sink同时分析
3. 自动估计unknown比例
3.2 安装与运行¶
# ========== R语言:安装和运行FEAST ==========
# 安装FEAST
# install.packages("devtools") # 如果没有devtools
devtools::install_github("cozygene/FEAST") # 从GitHub安装
library(FEAST) # 加载FEAST
# 准备输入数据
# 1. OTU丰度矩阵(行=样本,列=OTU/ASV)
# 2. 元数据(必须包含Env, SourceSink, id列)
# 读取OTU表
otu_table <- read.delim("otu_table.tsv", # 读取OTU表
row.names = 1, # 第一列为行名
check.names = FALSE)
# 准备元数据(FEAST格式)
metadata <- data.frame(
Env = c(rep("Skin", 3), rep("Oral", 3), # 环境类型
rep("Gut", 3), rep("Infant", 3)),
SourceSink = c(rep("Source", 9), # source/sink标签
rep("Sink", 3)),
id = c(rep(1, 9), # sink id(所有source共享id=1表示都对应sink 1)
1, 2, 3), # 每个sink有独立id
row.names = rownames(otu_table) # 行名=样本名
)
# 注意:FEAST的元数据格式与SourceTracker2不同!
# id列:每个sink样本有唯一id,对应的source样本共享同一个id
# 运行FEAST
feast_result <- FEAST(
C = as.matrix(otu_table), # OTU计数矩阵(必须是矩阵格式)
metadata = metadata, # 元数据
different_sources_flag = 0, # 0=所有sink共享同一组source
dir_path = "feast_output/", # 输出目录
outfile = "feast_result" # 输出文件前缀
)
# 查看结果
print(feast_result)
# 输出为比例矩阵:行=sink样本,列=source环境+Unknown
3.3 FEAST多sink分析¶
# ========== FEAST:每个sink有不同的source组合 ==========
# 准备元数据(每个sink独立的source组)
metadata_multi <- data.frame(
Env = c("Skin", "Skin", "Oral", "Oral", "Gut", "Gut", # sink1的source
"Skin", "Skin", "Oral", "Oral", "Gut", "Gut", # sink2的source
"Infant", "Infant"), # sink样本
SourceSink = c(rep("Source", 12), rep("Sink", 2)),
id = c(rep(1, 6), rep(2, 6), 1, 2), # id=1对应sink1,id=2对应sink2
row.names = rownames(otu_table)
)
# 运行FEAST(different_sources_flag=1表示每个sink有独立的source组)
feast_multi <- FEAST(
C = as.matrix(otu_table),
metadata = metadata_multi,
different_sources_flag = 1, # 每个sink有不同的source组
dir_path = "feast_multi_output/",
outfile = "feast_multi"
)
四、2024-2025年新方法¶
4.1 SourceID-NMF(2024)¶
SourceID-NMF:基于非负矩阵分解的源追踪
核心思想:
将OTU丰度矩阵分解为 W(源特征矩阵)× H(混合比例矩阵)
通过NMF的非负约束,自然满足比例非负的要求
优势:
- 更准确的定量(特别是source贡献差异大时)
- 不需要预设混合模型
- 对非贡献source的误分配更少
4.2 FastST(2025)¶
FastST:支持方向性推断的快速源追踪
独特功能:
1. 不需要预先指定source和sink(自动推断方向性!)
2. 计算速度比FEAST更快
3. 方向性推断准确率>90%
适用场景:
- 不确定哪个是source、哪个是sink时
- 研究微生物在不同环境之间的传播方向
五、实际案例:T2D研究中的应用¶
# ========== 案例:FMT(粪菌移植)后菌群来源追踪 ==========
# 场景:T2D患者接受FMT后,追踪肠道菌群来自供体还是自身
# 准备数据
# source1: 供体粪便(FMT前)
# source2: 患者自身粪便(FMT前)
# sink: 患者粪便(FMT后不同时间点)
metadata_fmt <- data.frame(
Env = c("Donor", "Donor", "Donor", # 供体source
"Patient_Pre", "Patient_Pre", "Patient_Pre", # 患者FMT前source
"FMT_1w", "FMT_4w", "FMT_12w"), # FMT后1/4/12周的sink
SourceSink = c(rep("Source", 6), rep("Sink", 3)),
id = c(rep(1, 3), rep(1, 3), 1, 1, 1), # 共享id=1
row.names = c("donor1", "donor2", "donor3",
"patient_pre1", "patient_pre2", "patient_pre3",
"patient_1w", "patient_4w", "patient_12w")
)
# 运行FEAST
fmt_result <- FEAST(
C = as.matrix(otu_table_fmt),
metadata = metadata_fmt,
different_sources_flag = 0,
dir_path = "fmt_tracking/",
outfile = "fmt_source_tracking"
)
# 预期结果解读:
# FMT后1周:供体菌群占比高(如60%供体,20%自身,20%未知)
# FMT后4周:供体菌群比例下降
# FMT后12周:部分回到自身菌群为主
# 这可以评估FMT的持久性效果
常见报错与解决¶
| 报错 | 原因 | 解决方案 |
|---|---|---|
SourceTracker2运行极慢(>24h) | OTU数太多或样本数太多 | 过滤低丰度OTU,减少到<500个OTU |
FEAST: Error in solve.default | 输入矩阵中有全0的行或列 | 过滤掉在所有样本中丰度为0的OTU |
所有source比例都接近0 | source和sink完全不相关 | 检查source是否与sink有生物学联系 |
Unknown比例>80% | source不够全面 | 增加更多候选source环境 |
结果不稳定(每次运行不同) | 随机种子未固定 | 设置固定种子(SourceTracker2: --seed; FEAST: set.seed()) |
FEAST的id列设置错误 | 不理解id列的含义 | id表示sink-source对应关系,同一id的source用于同一个sink |
内存溢出 | OTU表太大 | 聚合到属水平再做源追踪 |
速查表¶
========== 源追踪工具对比 ==========
工具 年份 算法 速度 准确性 方向性推断
SourceTracker2 2011 Gibbs采样 慢 高 不支持
FEAST 2019 EM算法 快(300x) 高 不支持
STENSL 2022 FEAST+L1正则 快 更高 不支持
SourceID-NMF 2024 非负矩阵分解 中 更高 不支持
FastST 2025 新框架 最快 高 支持(>90%)
========== 选择建议 ==========
样本少(<50)、需要稳定结果 → SourceTracker2
样本多(>50)、需要速度 → FEAST
需要最高准确性 → SourceID-NMF
不确定source/sink方向 → FastST
========== 输入数据要求 ==========
1. OTU/ASV丰度表(计数值,非相对丰度)
2. 元数据文件(标注source/sink分组)
3. Source样本:每个环境类型建议 ≥3 个重复
4. OTU数:建议过滤到100-500个(太多太慢)
5. 聚合层面:种/属/科都可以,属水平最常用
========== 结果解读 ==========
比例 > 0.3 → 该source是主要来源
比例 0.1-0.3 → 有一定贡献
比例 < 0.1 → 贡献微小(可能是假阳性)
Unknown > 0.5 → source库不够全面
========== 关键R包 ==========
FEAST:devtools::install_github("cozygene/FEAST")
可视化:ggplot2, tidyr, dplyr
数据处理:phyloseq, biomformat
面试高频问题¶
Q1:SourceTracker2和FEAST的核心区别是什么?¶
答: - SourceTracker2(2011):用贝叶斯模型+Gibbs采样(MCMC)。把sink看作多个source的混合物,通过反复随机采样来逼近真实的混合比例。速度慢但理论基础扎实 - FEAST(2019):用EM(期望最大化)算法。交替进行"估计每个OTU来自哪个source"(E步)和"更新source比例"(M步),直到收敛。速度快约300倍
选择建议: - 样本少、注重稳定性 → SourceTracker2 - 样本多、注重速度 → FEAST
Q2:源追踪中"Unknown"比例很高怎么解释?¶
答: Unknown代表不能被任何已知source解释的微生物比例。高Unknown的原因: 1. source库不全面:没有包含真正的来源环境(需要增加更多source类型) 2. 微生物进化/适应:微生物在新环境中发生了变化,不再和任何source一样 3. 稀有taxa的噪声:低丰度OTU在source中没有出现,被归为unknown 4. 混合source:真正的来源是多个source的混合,工具无法区分
解决方法:增加source种类、聚合到属水平、过滤低丰度OTU。
Q3:源追踪对数据有什么要求?¶
答: 1. 输入格式:OTU/ASV计数表(不是相对丰度!工具内部会标准化) 2. source样本:每种环境类型建议≥3个生物学重复 3. source完整性:尽可能包含所有可能的来源环境 4. 数据清洗:建议过滤低丰度OTU,保留100-500个高频OTU 5. 聚合层面:可以是ASV/OTU/属/科,属水平是最常用的平衡
Q4:源追踪在你的T2D研究中有什么应用价值?¶
答(参考框架): 1. FMT疗效追踪:T2D患者接受粪菌移植后,追踪肠道菌群有多少来自供体、多少是自身残留,评估FMT的持久性 2. 婴儿队列研究:追踪婴儿肠道菌群的来源(母亲阴道/皮肤/母乳),研究早期菌群定植与T2D风险的关系 3. 环境暴露研究:分析T2D患者肠道菌群中有多少可能来自饮食、环境等外部来源
Q5:2024-2025年源追踪领域有什么新进展?¶
答: 1. SourceID-NMF(2024):用非负矩阵分解替代概率模型,在定量准确性上优于FEAST和SourceTracker2,特别是减少了对非贡献source的误分配 2. FastST(2025):最大突破是支持方向性推断——不需要预先指定哪个是source、哪个是sink,算法自动判断微生物传播的方向,准确率>90% 3. 源追踪库质量评估(2025):研究强调在做源追踪之前,应该先评估source库的质量和特异性,避免不可靠的结果