微生物组时间序列分析¶
一句话概述¶
纵向微生物组数据分析通过追踪微生物群落随时间的动态变化,揭示群落演替规律、干预效应和个体化微生物组轨迹,本教程覆盖从实验设计到统计建模的完整方法体系。
目录¶
1. 核心知识点¶
| 序号 | 步骤 | 工具 | 目的 | 输入 | 输出 | 关键参数 |
|---|---|---|---|---|---|---|
| 1 | 数据预处理 | QIIME2/DADA2 | 去噪获得ASV/OTU表 | 原始测序数据 | 特征表+代表序列 | --p-trunc-len |
| 2 | 纵向数据整理 | R/Python | 构建时间序列数据框架 | 特征表+样本元数据 | 长格式数据框 | 时间点、受试者ID |
| 3 | 时间序列可视化 | ggplot2/q2-longitudinal | 探索群落动态模式 | 长格式数据 | 时序图/热图 | facet_wrap/geom_smooth |
| 4 | α多样性纵向分析 | q2-longitudinal/lme4 | 检测多样性随时间变化 | α多样性指标+时间 | 线性混合模型结果 | 随机截距/斜率 |
| 5 | β多样性纵向分析 | vegan/PERMANOVA | 检测群落组成随时间的变化 | 距离矩阵+元数据 | 统计检验结果 | strata参数 |
| 6 | 差异丰度时序分析 | MaAsLin2/LinDA | 识别随时间变化的分类群 | 特征表+元数据 | 差异分类群列表 | random_effects |
| 7 | 波动性分析 | q2-longitudinal | 量化群落稳定性和恢复力 | 纵向特征表 | 波动性指标 | volatility() |
| 8 | 状态转移分析 | Markov模型/DMM | 识别群落状态和转移概率 | 分组后的群落数据 | 状态转移矩阵 | 状态数k选择 |
| 9 | 干预效应评估 | 中断时间序列/DID | 评估干预对微生物组的影响 | 干预前后数据 | 干预效应估计 | 干预时间点 |
| 10 | 预测与因果推断 | VAR/Granger检验 | 预测未来组成/推断因果关系 | 多时间点数据 | 预测模型/因果网络 | 滞后阶数 |
2. 各步骤详解¶
步骤1:数据预处理¶
白话解释:在进行时间序列分析之前,需要先把原始测序数据处理成标准的特征表(ASV/OTU表)。纵向研究中,所有时间点的样本应该在同一批次中统一处理,避免引入批次效应。
技术细节:
纵向微生物组研究的数据预处理有几个特殊注意点:
统一处理:所有时间点的样本应使用相同的去噪参数(如DADA2的截断长度),确保不同时间点的数据可比。
批次校正:如果不同时间点的样本在不同批次测序(常见于长期随访研究),需要识别并校正批次效应。可以加入批次间的技术重复样本来评估批次效应大小。
样本追踪:纵向研究中样本数量大,确保样本-受试者-时间点的对应关系正确是基础。建议在元数据中使用唯一的受试者ID和标准化的时间编码。
缺失值处理:纵向研究不可避免会有部分时间点样本缺失(受试者退出、样本处理失败),需要在后续统计分析中妥善处理。
# QIIME2 纵向数据预处理
# 导入所有时间点的数据
qiime tools import \
--type 'SampleData[PairedEndSequencesWithQuality]' \
--input-path manifest_all_timepoints.tsv \
--output-path all_reads.qza \
--input-format PairedEndFastqManifestPhred33V2
# DADA2去噪(统一参数)
qiime dada2 denoise-paired \
--i-demultiplexed-seqs all_reads.qza \
--p-trunc-len-f 250 \
--p-trunc-len-r 200 \
--p-n-threads 16 \
--o-table table.qza \
--o-representative-sequences rep-seqs.qza \
--o-denoising-stats denoise-stats.qza
步骤2:纵向数据整理¶
白话解释:把特征表和样本元数据组织成适合时间序列分析的"长格式"数据框,每一行代表一个样本(= 一个受试者在一个时间点的观测),包含受试者ID、时间点、分组等信息。
技术细节:
纵向数据的结构: - 宽格式(wide format):每个受试者一行,不同时间点为不同列 - 长格式(long format):每个观测一行,更适合大多数统计建模
关键元数据字段: - subject_id:受试者唯一标识,用于关联同一个体的多次观测 - timepoint:时间信息,可以是序数(T1, T2, T3)或连续变量(天数/周数) - group:实验分组(如治疗组/对照组) - age/bmi等协变量:可能随时间变化的协变量
数据完整性检查: - 每个受试者有多少个时间点? - 时间间隔是否均匀? - 缺失模式是随机的还是系统性的?
library(phyloseq)
library(tidyverse)
# 读取phyloseq对象
ps <- readRDS("phyloseq_object.rds")
# 检查纵向结构
meta <- sample_data(ps) %>% as.data.frame()
meta$subject_id <- as.factor(meta$subject_id)
meta$timepoint <- as.numeric(meta$day)
# 数据完整性汇总
completeness <- meta %>%
group_by(subject_id) %>%
summarise(
n_timepoints = n(),
first_time = min(timepoint),
last_time = max(timepoint),
group = first(treatment_group)
)
cat("受试者数:", n_distinct(meta$subject_id), "\n")
cat("每人平均时间点数:", mean(completeness$n_timepoints), "\n")
cat("完整随访率:", mean(completeness$n_timepoints == max(completeness$n_timepoints)), "\n")
# 转换为长格式丰度表
otu_long <- psmelt(ps) %>%
select(Sample, OTU, Abundance, subject_id, timepoint, treatment_group) %>%
group_by(Sample) %>%
mutate(rel_abundance = Abundance / sum(Abundance))
步骤3:时间序列可视化¶
白话解释:在开始复杂的统计分析之前,先用图形直观地看看微生物组是如何随时间变化的。这包括追踪关键分类群的丰度变化、群落多样性的波动,以及个体间差异的大小。
技术细节:
常用的纵向可视化类型:
- 轨迹图(Spaghetti plot):每条线代表一个受试者,展示某指标随时间的变化轨迹
- 组均值趋势图:带置信区间的分组平均趋势
- 堆叠面积图(时间轴):展示群落组成随时间的变化
- 热图(按时间排列):纵轴为分类群,横轴为时间
- PCoA动画:在低维空间中展示群落状态的移动轨迹
可视化设计原则: - 使用颜色区分不同组/受试者 - 用平滑曲线(loess/GAM)展示总体趋势 - 在组内个体变异和组间差异之间找到平衡
library(ggplot2)
library(gganimate)
# 1. α多样性轨迹图
alpha_div <- estimate_richness(ps, measures = c("Shannon", "Observed"))
alpha_div$subject_id <- meta$subject_id
alpha_div$timepoint <- meta$timepoint
alpha_div$group <- meta$treatment_group
ggplot(alpha_div, aes(x=timepoint, y=Shannon, group=subject_id, color=group)) +
geom_line(alpha=0.3) +
geom_smooth(aes(group=group), method="loess", se=TRUE, linewidth=1.5) +
labs(x="时间(天)", y="Shannon多样性", title="微生物组α多样性时间轨迹") +
scale_color_brewer(palette="Set1") +
theme_minimal(base_size=14)
# 2. 关键分类群丰度变化
top_genera <- otu_long %>%
group_by(OTU) %>%
summarise(mean_abund = mean(rel_abundance)) %>%
top_n(10, mean_abund) %>%
pull(OTU)
otu_long %>%
filter(OTU %in% top_genera) %>%
ggplot(aes(x=timepoint, y=rel_abundance, color=treatment_group)) +
geom_smooth(method="loess", se=TRUE) +
facet_wrap(~OTU, scales="free_y", ncol=5) +
labs(x="时间(天)", y="相对丰度") +
theme_minimal()
# 3. 群落组成堆叠面积图(按时间)
otu_long %>%
filter(OTU %in% top_genera) %>%
group_by(timepoint, treatment_group, OTU) %>%
summarise(mean_abund = mean(rel_abundance), .groups="drop") %>%
ggplot(aes(x=timepoint, y=mean_abund, fill=OTU)) +
geom_area() +
facet_wrap(~treatment_group) +
labs(x="时间(天)", y="平均相对丰度") +
theme_minimal()
步骤4:α多样性纵向分析¶
白话解释:α多样性反映单个样本内的微生物多样性。纵向分析的核心问题是:多样性随时间如何变化?不同处理组的变化趋势是否不同?这需要用到考虑重复测量的统计方法——线性混合效应模型。
技术细节:
线性混合效应模型(LME)是纵向数据分析的核心工具,因为它能: - 处理同一受试者的重复测量(自相关结构) - 允许不等间距时间点和缺失数据 - 区分固定效应(组间效应)和随机效应(个体间变异)
模型结构:Shannon ~ Time * Group + (1 + Time | Subject) - 固定效应:时间主效应、组别主效应、时间×组别交互(关键!) - 随机效应:随机截距(个体基线差异)+ 随机斜率(个体变化速率差异)
交互项的解读:如果时间×组别交互显著,说明不同组的多样性变化趋势不同(如抗生素组多样性下降速度比对照组快)。
QIIME2的q2-longitudinal插件提供了专门的纵向分析接口,但对于复杂设计,R的lme4/nlme包更灵活。
library(lme4)
library(lmerTest) # 提供p值
library(emmeans)
# 拟合线性混合模型
model_shannon <- lmer(
Shannon ~ timepoint * treatment_group + (1 + timepoint | subject_id),
data = alpha_div
)
# 模型摘要
summary(model_shannon)
# ANOVA表(Type III检验)
anova(model_shannon, type = 3)
# 如果交互作用显著,进行简单斜率分析
emtrends(model_shannon, pairwise ~ treatment_group, var = "timepoint")
# 可视化模型拟合
library(effects)
plot(allEffects(model_shannon))
# QIIME2 longitudinal 分析
qiime longitudinal linear-mixed-effects \
--m-metadata-file metadata.tsv \
--m-metadata-file alpha_diversity.qza \
--p-metric shannon_entropy \
--p-state-column day \
--p-individual-id-column subject_id \
--p-group-columns treatment_group \
--o-visualization lme_shannon.qzv
步骤5:β多样性纵向分析¶
白话解释:β多样性衡量样本之间的群落组成差异。纵向分析中,我们想知道:不同时间点之间群落组成是否显著变化?不同组的变化方向和幅度是否不同?
技术细节:
纵向β多样性分析比横截面分析复杂,因为需要考虑重复测量的非独立性:
PERMANOVA with strata:使用
strata参数指定受试者ID,使置换仅在受试者内进行,尊重重复测量结构。但这种方法的统计功效有限。PCoA + 轨迹分析:在PCoA空间中追踪个体和组的中心点移动轨迹。
变化量分析(Bray-Curtis距离的变化):计算每个受试者相邻时间点之间的β多样性变化量,然后比较组间差异。
PERMANOVA(时间切片):在每个时间点单独做PERMANOVA,观察组间差异何时出现/消失。
距离基矩阵分析(adonis2):将时间和组别同时纳入模型。
library(vegan)
library(phyloseq)
# 计算Bray-Curtis距离
bc_dist <- distance(ps, method="bray")
# 1. PERMANOVA with strata(考虑重复测量)
adonis_result <- adonis2(
bc_dist ~ timepoint * treatment_group,
data = meta,
strata = meta$subject_id,
permutations = 999
)
print(adonis_result)
# 2. 计算个体内时间变化量(turnover)
calc_turnover <- function(ps, meta) {
subjects <- unique(meta$subject_id)
turnover_list <- list()
for (subj in subjects) {
subj_samples <- meta %>% filter(subject_id == subj) %>% arrange(timepoint)
if (nrow(subj_samples) < 2) next
for (i in 2:nrow(subj_samples)) {
s1 <- subj_samples$sample_id[i-1]
s2 <- subj_samples$sample_id[i]
bc <- as.matrix(bc_dist)[s1, s2]
turnover_list[[length(turnover_list)+1]] <- data.frame(
subject_id = subj,
from_time = subj_samples$timepoint[i-1],
to_time = subj_samples$timepoint[i],
turnover = bc,
group = subj_samples$treatment_group[1]
)
}
}
bind_rows(turnover_list)
}
turnover_df <- calc_turnover(ps, meta)
# 比较组间turnover差异
wilcox.test(turnover ~ group, data = turnover_df)
# 可视化
ggplot(turnover_df, aes(x=to_time, y=turnover, color=group)) +
geom_smooth(method="loess", se=TRUE) +
labs(x="时间(天)", y="Bray-Curtis变化量", title="群落组成变化速率") +
theme_minimal()
步骤6:差异丰度时序分析¶
白话解释:找出哪些具体的微生物(属、种或ASV水平)在不同组之间随时间变化存在显著差异。比如,抗生素治疗后哪些细菌显著减少了?恢复期哪些细菌最先回升?
技术细节:
MaAsLin2(Microbiome Multivariable Associations with Linear Models 2)是纵向微生物组差异丰度分析的首选工具,因为它: - 原生支持随机效应(random_effects参数) - 支持多种数据转换和正态化方法 - 自动处理稀疏零值 - 支持多协变量和交互效应
模型选择考虑: - 固定效应:时间、组别、时间×组别交互、协变量 - 随机效应:受试者ID(至少需要随机截距) - 数据转换:LOG、AST、NONE - 正态化:TSS(总和缩放)、CSS、NONE
替代工具: - LinDA:基于线性模型的纵向差异丰度分析 - ZIBR:零膨胀Beta回归(处理高零值的丰度数据) - NBZIMM:负二项零膨胀混合模型
library(Maaslin2)
# 准备输入数据
features <- as.data.frame(otu_table(ps)) # 特征表(行=样本, 列=特征)
metadata <- as.data.frame(sample_data(ps))
# MaAsLin2纵向分析
fit_data <- Maaslin2(
input_data = features,
input_metadata = metadata,
output = "maaslin2_longitudinal",
fixed_effects = c("timepoint", "treatment_group", "timepoint:treatment_group"),
random_effects = c("subject_id"),
normalization = "TSS",
transform = "LOG",
min_abundance = 0.001,
min_prevalence = 0.1,
max_significance = 0.05,
cores = 8
)
# 查看显著的时间×组别交互效应
sig_interaction <- fit_data$results %>%
filter(metadata == "timepoint:treatment_group" & qval < 0.05) %>%
arrange(qval)
print(sig_interaction)
步骤7:波动性分析¶
白话解释:微生物组的"稳定性"是纵向研究的重要指标。有些人的微生物组很稳定(天天差不多),有些人波动很大。波动性分析量化这种稳定性,并研究哪些因素影响群落稳定性。
技术细节:
波动性(Volatility)的量化方法:
- 个体内变异系数(CV):对每个受试者计算各时间点α多样性或特定分类群丰度的变异系数
- 时间自相关(Temporal autocorrelation):相邻时间点的相关性,高自相关=稳定
- β多样性方差:同一受试者不同时间点之间的平均Bray-Curtis距离
- Novelty/Stability指标:QIIME2定义的指标
恢复力(Resilience)分析: - 干预后群落恢复到基线状态的速度 - 计算方法:干预后各时间点与基线的Bray-Curtis距离的衰减速率
# QIIME2 波动性分析
qiime longitudinal volatility \
--m-metadata-file metadata.tsv \
--m-metadata-file shannon.qza \
--p-state-column day \
--p-individual-id-column subject_id \
--p-default-group-column treatment_group \
--p-default-metric shannon_entropy \
--o-visualization volatility.qzv
# R中手动计算波动性指标
volatility_metrics <- meta %>%
left_join(alpha_div, by=c("sample_id")) %>%
group_by(subject_id) %>%
arrange(timepoint) %>%
summarise(
group = first(treatment_group),
mean_shannon = mean(Shannon),
sd_shannon = sd(Shannon),
cv_shannon = sd(Shannon) / mean(Shannon), # 变异系数
range_shannon = max(Shannon) - min(Shannon),
# 相邻时间点变化量的均值
mean_delta = mean(abs(diff(Shannon))),
n_timepoints = n()
)
# 比较组间波动性
wilcox.test(cv_shannon ~ group, data = volatility_metrics)
# 可视化
ggplot(volatility_metrics, aes(x=group, y=cv_shannon, fill=group)) +
geom_boxplot() +
geom_jitter(width=0.2, alpha=0.5) +
labs(x="组别", y="Shannon变异系数", title="微生物组稳定性比较") +
theme_minimal()
步骤8:状态转移分析¶
白话解释:微生物群落可能存在几种"典型状态"(如健康状态、失调状态),不同的人在不同时间可能处于不同状态。状态转移分析就是找出这些状态,并计算从一个状态转移到另一个状态的概率。
技术细节:
Dirichlet Multinomial Mixtures (DMM) 是识别群落类型(community state types, CST)的常用方法: 1. 将所有样本聚类为k种状态 2. 对每个受试者的每个时间点标记其所属状态 3. 构建状态转移矩阵(Markov transition matrix)
马尔可夫模型假设:当前状态转移到下一状态的概率仅取决于当前状态,与历史无关。这在微生物组中是一个简化假设,但通常是合理的起点。
隐马尔可夫模型(HMM)是更复杂的扩展,允许存在不可直接观测的"隐状态"。
library(DirichletMultinomial)
library(markovchain)
# 1. DMM聚类确定群落状态
count_matrix <- as.matrix(otu_table(ps)) # 样本×OTU
# 尝试不同的k值
fit_list <- lapply(1:6, function(k) dmn(count_matrix, k))
# 选择最优k(最小Laplace近似)
laplace_vals <- sapply(fit_list, laplace)
best_k <- which.min(laplace_vals)
cat("最优状态数:", best_k, "\n")
# 分配状态标签
best_fit <- fit_list[[best_k]]
state_assignments <- apply(mixture(best_fit), 1, which.max)
meta$community_state <- paste0("CST", state_assignments)
# 2. 构建转移矩阵
transitions <- meta %>%
arrange(subject_id, timepoint) %>%
group_by(subject_id) %>%
mutate(next_state = lead(community_state)) %>%
filter(!is.na(next_state))
# 估计转移概率
trans_matrix <- table(transitions$community_state, transitions$next_state)
trans_prob <- prop.table(trans_matrix, margin=1)
print(round(trans_prob, 3))
# 3. 拟合马尔可夫链
mc <- new("markovchain",
states = colnames(trans_prob),
transitionMatrix = as.matrix(trans_prob)
)
# 稳态分布
steady <- steadyStates(mc)
cat("稳态分布:\n")
print(round(steady, 3))
步骤9:干预效应评估¶
白话解释:如果研究涉及某种干预(如抗生素治疗、饮食改变、益生菌补充),我们需要正式地评估这个干预对微生物组产生了多大的影响,以及这个影响持续了多久。
技术细节:
评估干预效应的方法:
- 前后比较(Paired test):最简单但信息量有限
- 中断时间序列分析(ITS):检测干预时间点前后趋势的"断点"
- 差异中的差异(DID):比较治疗组和对照组在干预前后的变化差异
- 广义估计方程(GEE):适合群体水平推断
DID模型:Y = β₀ + β₁*Time + β₂*Treatment + β₃*Time×Treatment + ε - β₃是核心关注的参数:干预效应(干预组相对于对照组的额外变化)
恢复时间估计: - 定义"恢复"标准(如Bray-Curtis距离回到基线±1SD范围内) - 使用生存分析方法估计恢复时间的中位数和分布
# 差异中的差异(DID)分析
library(lme4)
# 标记干预前后
meta$post_intervention <- ifelse(meta$timepoint > intervention_day, 1, 0)
# DID模型
did_model <- lmer(
Shannon ~ post_intervention * treatment_group + (1 | subject_id),
data = alpha_div_meta
)
summary(did_model)
# 恢复时间分析
library(survival)
# 定义恢复事件
recovery_data <- meta %>%
filter(treatment_group == "antibiotic", timepoint > intervention_day) %>%
group_by(subject_id) %>%
mutate(
baseline_dist = bc_to_baseline, # 与基线的Bray-Curtis距离
recovered = baseline_dist < recovery_threshold,
time_since_intervention = timepoint - intervention_day
) %>%
summarise(
recovery_time = min(time_since_intervention[recovered], na.rm=TRUE),
censored = all(!recovered)
)
# Kaplan-Meier恢复曲线
surv_obj <- Surv(recovery_data$recovery_time, !recovery_data$censored)
km_fit <- survfit(surv_obj ~ 1)
plot(km_fit, xlab="干预后天数", ylab="未恢复比例", main="微生物组恢复曲线")
步骤10:预测与因果推断¶
白话解释:如果有足够多的时间点,我们可以尝试预测微生物组未来的状态,或者推断不同微生物之间是否存在因果关系(如A的增加是否导致了B随后的减少)。
技术细节:
向量自回归(VAR)模型和Granger因果检验是时间序列分析中推断方向性关系的经典方法:
Granger因果:如果加入X的历史信息能显著改善对Y的预测(相比仅用Y自身历史),则X"Granger-causes"Y。注意:这不是真正的因果关系,只是预测性关系。
稀疏VAR(SVAR):对于高维微生物组数据,需要使用正则化方法(如LASSO-VAR)来估计稀疏的微生物间相互作用网络。
动态贝叶斯网络(DBN):另一种推断时间序列因果结构的方法,对短时间序列更适用。
注意事项: - Granger因果需要平稳时间序列(可能需要差分) - 时间序列长度要足够(通常每个受试者≥10个时间点) - 组合学丰度数据的特殊性(和约束)需要考虑
library(vars)
library(SparseTSCGM)
# 选择几个关键属进行VAR分析
key_genera <- c("Bacteroides", "Faecalibacterium", "Prevotella", "Ruminococcus")
# 对单个受试者拟合VAR模型(需要足够多的时间点)
subj_data <- otu_long %>%
filter(subject_id == "S001", OTU %in% key_genera) %>%
pivot_wider(names_from=OTU, values_from=rel_abundance) %>%
arrange(timepoint) %>%
select(all_of(key_genera))
# CLR变换(处理组合数据)
subj_clr <- compositions::clr(subj_data + 1e-6)
# 选择最优滞后阶数
var_select <- VARselect(subj_clr, lag.max=5, type="const")
optimal_lag <- var_select$selection["AIC(n)"]
# 拟合VAR模型
var_model <- VAR(subj_clr, p=optimal_lag, type="const")
# Granger因果检验
causality(var_model, cause="Bacteroides")
# 脉冲响应函数(一个属的扰动如何影响其他属)
irf_result <- irf(var_model, impulse="Bacteroides", response="Faecalibacterium", n.ahead=10)
plot(irf_result)
3. 实战命令/代码(完整流程)¶
#!/usr/bin/env Rscript
# =====================================================
# 微生物组时间序列分析完整流程
# 场景:抗生素干预研究,追踪肠道菌群变化与恢复
# =====================================================
# ---- 加载包 ----
library(phyloseq)
library(tidyverse)
library(lme4)
library(lmerTest)
library(vegan)
library(Maaslin2)
library(ggplot2)
# ---- 1. 数据加载 ----
ps <- readRDS("data/phyloseq_longitudinal.rds")
meta <- as.data.frame(sample_data(ps))
# 确保关键变量类型正确
meta$subject_id <- as.factor(meta$subject_id)
meta$day <- as.numeric(meta$day)
meta$group <- factor(meta$group, levels=c("control","antibiotic"))
meta$phase <- factor(
ifelse(meta$day < 0, "baseline",
ifelse(meta$day <= 7, "treatment", "recovery")),
levels=c("baseline","treatment","recovery")
)
sample_data(ps) <- sample_data(meta)
cat("样本数:", nsamples(ps), "\n")
cat("受试者数:", n_distinct(meta$subject_id), "\n")
cat("时间点范围:", range(meta$day), "\n")
# ---- 2. α多样性时序分析 ----
alpha <- estimate_richness(ps, measures=c("Shannon","Observed","Simpson"))
alpha$sample_id <- rownames(alpha)
alpha_meta <- cbind(alpha, meta)
# 线性混合效应模型
lme_shannon <- lmer(
Shannon ~ day * group + phase * group + (1 + day | subject_id),
data = alpha_meta
)
cat("\n=== Shannon多样性混合模型 ===\n")
print(anova(lme_shannon, type=3))
# 可视化
p_alpha <- ggplot(alpha_meta, aes(x=day, y=Shannon, color=group)) +
geom_line(aes(group=subject_id), alpha=0.2) +
geom_smooth(method="loess", linewidth=1.5, se=TRUE) +
geom_vline(xintercept=c(0, 7), linetype="dashed", color="grey50") +
annotate("rect", xmin=0, xmax=7, ymin=-Inf, ymax=Inf, alpha=0.1, fill="red") +
labs(x="天数(Day 0 = 干预开始)", y="Shannon多样性",
title="肠道微生物组α多样性纵向变化") +
scale_color_brewer(palette="Set1", name="组别") +
theme_minimal(base_size=13)
ggsave("figures/alpha_longitudinal.pdf", p_alpha, width=10, height=6)
# ---- 3. β多样性时序分析 ----
bc_dist <- distance(ps, method="bray")
# 按时间切片的PERMANOVA
timepoints <- sort(unique(meta$day))
permanova_results <- lapply(timepoints, function(t) {
samps <- meta$sample_id[meta$day == t]
if (length(samps) < 10) return(NULL)
sub_dist <- usedist::dist_subset(bc_dist, samps)
sub_meta <- meta[meta$day == t, ]
result <- adonis2(sub_dist ~ group, data=sub_meta, permutations=999)
data.frame(day=t, R2=result$R2[1], pvalue=result$`Pr(>F)`[1])
}) %>% bind_rows()
# ---- 4. 差异丰度分析(MaAsLin2) ----
features_df <- as.data.frame(t(otu_table(ps)))
metadata_df <- as.data.frame(sample_data(ps))
maaslin_result <- Maaslin2(
input_data = features_df,
input_metadata = metadata_df,
output = "results/maaslin2_longitudinal",
fixed_effects = c("day", "group", "day:group", "phase"),
random_effects = c("subject_id"),
normalization = "TSS",
transform = "LOG",
min_abundance = 0.001,
min_prevalence = 0.1,
cores = 8
)
# ---- 5. 波动性计算 ----
volatility <- alpha_meta %>%
group_by(subject_id, group) %>%
arrange(day) %>%
summarise(
cv_shannon = sd(Shannon) / mean(Shannon),
mean_delta = mean(abs(diff(Shannon))),
.groups = "drop"
)
vol_test <- wilcox.test(cv_shannon ~ group, data=volatility)
cat(sprintf("\n波动性比较 (Wilcoxon): p = %.4f\n", vol_test$p.value))
# ---- 6. 恢复时间分析 ----
# 计算每个样本与该受试者基线的距离
baseline_samples <- meta %>% filter(phase == "baseline") %>%
group_by(subject_id) %>% slice_tail(n=1) %>% pull(sample_id)
recovery_dist <- meta %>%
filter(group == "antibiotic", day > 7) %>%
rowwise() %>%
mutate(
baseline_sample = baseline_samples[match(subject_id,
meta$subject_id[meta$sample_id %in% baseline_samples])],
dist_to_baseline = as.matrix(bc_dist)[sample_id, baseline_sample]
)
p_recovery <- ggplot(recovery_dist, aes(x=day, y=dist_to_baseline)) +
geom_line(aes(group=subject_id), alpha=0.3) +
geom_smooth(method="loess", color="red") +
geom_hline(yintercept=0.3, linetype="dashed", color="blue") +
labs(x="天数", y="与基线的Bray-Curtis距离", title="抗生素组微生物组恢复轨迹") +
theme_minimal()
ggsave("figures/recovery_trajectory.pdf", p_recovery, width=8, height=5)
cat("\n=== 分析完成 ===\n")
4. 面试常问点¶
Q1:纵向微生物组研究相比横截面研究有什么优势?
纵向研究能:(1)追踪个体内微生物组的动态变化,区分个体间差异和个体内时间变化;(2)建立时间顺序,为因果推断提供基础;(3)评估干预效应的时效性和持续性;(4)量化群落稳定性和恢复力。横截面研究只能捕捉一个时间快照,无法区分"状态"和"过程"。
Q2:为什么纵向数据不能用普通的t检验或ANOVA?
因为同一受试者的重复测量之间存在相关性(自相关),违反了独立性假设。如果忽略这种相关性:(1)标准误估计不正确;(2)p值不可靠(通常偏小,导致假阳性);(3)无法充分利用个体内变化的信息。需要使用线性混合模型、GEE等考虑相关结构的方法。
Q3:线性混合效应模型中的随机效应有什么生物学含义?
随机截距反映每个受试者的微生物组"基线水平"不同(有人天然多样性高,有人低);随机斜率反映不同受试者对时间或干预的"响应速率"不同(有人恢复快,有人慢)。这种个体间异质性在微生物组研究中非常普遍,必须在建模中考虑。
Q4:微生物组时间序列数据有什么特殊的统计挑战?
(1)组合性(compositionality):相对丰度加和为1,组分间非独立;(2)零膨胀:大量零值,且零可能是"真零"或"采样零";(3)高维度:物种数远多于时间点数;(4)不等间距时间点;(5)缺失数据;(6)非正态性:丰度数据通常右偏。这些挑战需要专门的统计方法。
Q5:如何评估微生物组的"恢复力"(resilience)?
常用方法:(1)计算扰动后各时间点与基线的β多样性距离,观察其衰减速率;(2)定义恢复阈值(如距离回到基线±1SD),用生存分析估计恢复时间;(3)比较恢复后的"新稳态"是否与原始基线相同(弹性恢复vs塑性变形);(4)α多样性的恢复速率(用指数衰减模型拟合)。
Q6:Granger因果在微生物组中的应用有什么局限性?
(1)需要平稳时间序列,但微生物丰度数据通常非平稳;(2)需要较长的时间序列(每个受试者≥10-15个时间点),很多研究达不到;(3)Granger因果不是真正的因果关系,只是预测性关系;(4)高维数据中多重检验问题严重;(5)组合数据的约束使标准VAR模型的假设被违反。
5. 易错/易混淆点¶
忽略重复测量的非独立性:对纵向数据直接使用t检验、Wilcoxon检验或标准PERMANOVA会严重低估p值,产生大量假阳性。必须使用考虑相关结构的方法(LME、GEE、分层置换)。
时间变量的类型选择:时间可以作为连续变量(天数)或分类变量(phase/阶段)纳入模型,两者回答不同的问题。连续时间检测线性趋势,分类时间比较阶段间差异。选择不当会错过重要效应。
混淆个体间差异和个体内变化:纵向数据中的总变异包含"受试者间变异"和"受试者内变异"两个层次。如果不正确建模随机效应,可能将稳定的个体间差异误报为时间效应。
缺失数据处理不当:纵向研究中常见样本缺失。如果缺失与结局相关(如重症患者退出),简单的完整案例分析会引入偏倚。应使用能处理不平衡数据的方法(如LME天然支持不等长时间序列)。
批次效应与时间效应混淆:如果不同时间点的样本在不同批次测序,批次效应可能被误判为时间效应。应在同一批次中处理所有时间点的样本,或加入批次间重复样本进行评估。
稀疏数据的零值处理:低丰度物种在某些时间点完全检测不到(零值),这可能是真实缺失或采样不足。直接log变换会产生负无穷。需要使用适当的zero-handling策略(伪计数、零膨胀模型等)。
过度解读短时间序列的因果关系:仅有3-5个时间点的数据不适合做VAR/Granger因果分析。这些方法需要足够长的时间序列来可靠地估计滞后效应。
6. 补充知识¶
实验设计考量¶
纵向微生物组研究的实验设计需要特别考虑: - 采样频率:取决于预期的变化速率(肠道菌群日变化vs季节变化) - 随访时长:覆盖感兴趣的生物学过程(如抗生素恢复通常需要1-3个月) - 样本量计算:纵向研究的效能分析需要考虑组内相关系数(ICC) - 技术重复:每个批次应包含质控样本(mock community、重提取重复) - 样本保存:长期研究中样本保存条件的一致性至关重要
推荐工具与资源¶
- QIIME2 q2-longitudinal: https://docs.qiime2.org/latest/plugins/available/longitudinal/
- MaAsLin2: https://huttenhower.sph.harvard.edu/maaslin2/
- SplinectomeR: 基于样条的纵向微生物组分析
- TimeSeriesExperiment: Bioconductor包
- 综述: Gerber (2014) "The dynamic microbiome" FEBS Letters
- 综述: Faust et al. (2015) "Metagenomics meets time series analysis"
新兴方向¶
- 多组学纵向整合:同时追踪微生物组、代谢组、免疫指标的协同变化
- 个性化基线(personalized baseline):承认每个人有不同的"正常"微生物组
- 实时微生物组监测:结合可穿戴设备和快速测序的连续监测
- 因果推断新方法:整合干预实验和观察性纵向数据的因果推断框架