跳转至

172_微生物组网络稳定性

一句话概述

微生物组生态网络稳定性分析通过构建物种共现/互作网络,评估网络的鲁棒性(robustness)、模块化(modularity)程度和关键物种(keystone species),揭示群落抵抗扰动和维持功能的生态学机制。


核心知识点总览

知识点说明
共现网络基于物种丰度相关性构建的互作网络
鲁棒性(Robustness)网络抵抗节点移除(物种丧失)的能力
模块化(Modularity)网络分解为紧密连接子群的程度
Keystone种在网络中起关键连接作用的物种
自然连通度衡量网络整体连通性的指标
攻击模拟系统性移除节点评估网络崩溃过程
网络拓扑度分布、聚类系数、平均路径长度等
稳定性指标抗干扰能力的定量评估

各步骤详解

第一步:理解微生物网络稳定性

白话解释: 微生物群落就像一张"关系网"——有些菌互相帮助(正相关),有些互相竞争(负相关)。稳定的群落就像一张编织紧密的网——即使抽掉几根线(几个物种消失),整体结构不会崩塌。不稳定的群落则像松散的网——稍有扰动就可能瓦解。

关键稳定性指标: | 指标 | 含义 | 解读 | |------|------|------| | Robustness | 随机/定向移除节点后连通性保持程度 | 高=抗扰动 | | Modularity(Q) | 模块间连接vs模块内连接的比值 | 高=功能分区明确 | | 自然连通度 | 邻接矩阵特征值的指数和 | 高=连通性好 | | 负相关比例 | 负(竞争)边占总边的比例 | 高=群落更稳定(理论) | | 度分布 | 节点连接数的分布(幂律/随机) | 幂律=少数hub存在 |


第二步:网络构建

代码示例:

library(SpiecEasi)  # 推荐的微生物网络推断方法
library(igraph)

# === 使用SpiecEasi构建网络(推荐) ===
# SpiecEasi使用稀疏逆协方差(GLASSO)或MB方法
# 比简单相关更适合组成性数据

# 输入: OTU/ASV counts矩阵 (样本×物种)
otu_matrix <- as.matrix(otu_counts)

# 方法1: GLASSO (graphical lasso)
se_result <- spiec.easi(
  otu_matrix,
  method = "glasso",
  lambda.min.ratio = 0.01,
  nlambda = 50,
  sel.criterion = "stars",  # StARS模型选择
  pulsar.params = list(rep.num = 50, ncores = 8)
)

# 方法2: MB (Meinshausen-Bühlmann)
se_mb <- spiec.easi(
  otu_matrix,
  method = "mb",
  lambda.min.ratio = 0.01,
  nlambda = 50,
  sel.criterion = "stars",
  pulsar.params = list(rep.num = 50, ncores = 8)
)

# 提取邻接矩阵和igraph对象
adj_matrix <- getRefit(se_result)
ig <- adj2igraph(adj_matrix, vertex.attr = list(name = colnames(otu_matrix)))

# === 替代方法: SparCC (组成性数据相关) ===
# sparcc结果转为网络
# library(SpiecEasi)  # 包含sparcc
# sparcc_result <- sparcc(otu_matrix)
# 使用bootstrap P值过滤显著边

# === 基本网络统计 ===
cat(sprintf("节点数: %d\n", vcount(ig)))
cat(sprintf("边数: %d\n", ecount(ig)))
cat(sprintf("平均度: %.2f\n", mean(degree(ig))))
cat(sprintf("聚类系数: %.3f\n", transitivity(ig, type = "global")))
cat(sprintf("模块化度: %.3f\n", modularity(cluster_louvain(ig))))
cat(sprintf("图密度: %.4f\n", graph.density(ig)))


第三步:鲁棒性分析(攻击模拟)

代码示例:

# === 网络鲁棒性: 节点移除模拟 ===

# 自然连通度计算
natural_connectivity <- function(graph) {
  adj <- as_adjacency_matrix(graph, sparse = FALSE)
  eigenvalues <- eigen(adj, only.values = TRUE)$values
  nc <- log(mean(exp(eigenvalues)))
  return(nc)
}

# === 随机攻击(Random failure) ===
robustness_random <- function(graph, n_reps = 100) {
  n_nodes <- vcount(graph)
  nc_original <- natural_connectivity(graph)

  results <- matrix(0, nrow = n_nodes, ncol = n_reps)

  for (rep in 1:n_reps) {
    g_temp <- graph
    removal_order <- sample(V(g_temp)$name)

    for (i in 1:(n_nodes - 1)) {
      g_temp <- delete_vertices(g_temp, removal_order[i])
      if (vcount(g_temp) > 0) {
        results[i, rep] <- natural_connectivity(g_temp) / nc_original
      }
    }
  }

  # 返回平均鲁棒性曲线
  return(rowMeans(results))
}

# === 定向攻击(Targeted attack: 按度从高到低移除) ===
robustness_targeted <- function(graph) {
  n_nodes <- vcount(graph)
  nc_original <- natural_connectivity(graph)
  nc_curve <- numeric(n_nodes)

  g_temp <- graph
  for (i in 1:(n_nodes - 1)) {
    # 移除当前度最高的节点
    degrees <- degree(g_temp)
    hub <- names(which.max(degrees))
    g_temp <- delete_vertices(g_temp, hub)

    if (vcount(g_temp) > 0) {
      nc_curve[i] <- natural_connectivity(g_temp) / nc_original
    }
  }
  return(nc_curve)
}

# 运行模拟
rob_random <- robustness_random(ig, n_reps = 50)
rob_targeted <- robustness_targeted(ig)

# 可视化
fraction_removed <- seq(0, 1, length.out = length(rob_random))
plot(fraction_removed, rob_random, type = "l", col = "blue", lwd = 2,
     xlab = "Fraction of nodes removed",
     ylab = "Relative natural connectivity",
     main = "Network Robustness")
lines(fraction_removed, rob_targeted, col = "red", lwd = 2)
legend("topright", c("Random failure", "Targeted attack"),
       col = c("blue", "red"), lwd = 2)

# 鲁棒性指数: 曲线下面积(AUC)
robustness_index_random <- sum(rob_random) / length(rob_random)
robustness_index_targeted <- sum(rob_targeted) / length(rob_targeted)
cat(sprintf("随机攻击鲁棒性: %.3f\n", robustness_index_random))
cat(sprintf("定向攻击鲁棒性: %.3f\n", robustness_index_targeted))


第四步:模块化分析

代码示例:

# === 模块检测 ===
# Louvain算法(快速)
modules_louvain <- cluster_louvain(ig)
# Greedy优化
modules_greedy <- cluster_fast_greedy(ig)

# 模块化度
Q_louvain <- modularity(modules_louvain)
cat(sprintf("Modularity (Louvain): %.3f\n", Q_louvain))
# Q > 0.4 通常认为有显著模块结构

# 模块成员
membership <- membership(modules_louvain)
V(ig)$module <- membership

# 模块内/模块间连接比
# Zi-Pi分析(Within-module connectivity vs Among-module connectivity)
zi_pi <- function(graph, modules) {
  n <- vcount(graph)
  adj <- as_adjacency_matrix(graph, sparse = FALSE)
  mem <- membership(modules)

  results <- data.frame(node = V(graph)$name, Zi = 0, Pi = 0)

  for (i in 1:n) {
    ki <- degree(graph, V(graph)[i])
    if (ki == 0) next

    # 模块内连接
    same_module <- which(mem == mem[i])
    ki_s <- sum(adj[i, same_module])

    # Zi: within-module degree z-score
    all_in_module <- which(mem == mem[i])
    k_module <- sapply(all_in_module, function(j) sum(adj[j, same_module]))
    results$Zi[i] <- (ki_s - mean(k_module)) / (sd(k_module) + 1e-10)

    # Pi: participation coefficient
    unique_modules <- unique(mem)
    ki_t <- sapply(unique_modules, function(m) sum(adj[i, which(mem == m)]))
    results$Pi[i] <- 1 - sum((ki_t / ki)^2)
  }
  return(results)
}

zi_pi_results <- zi_pi(ig, modules_louvain)

# 节点角色分类 (Guimerà & Amaral 2005)
# Hub连接器: Zi>2.5, Pi>0.62 (Module hubs that connect modules)
# 模块Hub: Zi>2.5, Pi<0.62 (Hubs within their module)
# 连接器: Zi<2.5, Pi>0.62 (Connectors between modules)
# 外围: Zi<2.5, Pi<0.62 (Peripheral nodes)

zi_pi_results$Role <- case_when(
  zi_pi_results$Zi > 2.5 & zi_pi_results$Pi > 0.62 ~ "Network Hub",
  zi_pi_results$Zi > 2.5 & zi_pi_results$Pi <= 0.62 ~ "Module Hub",
  zi_pi_results$Zi <= 2.5 & zi_pi_results$Pi > 0.62 ~ "Connector",
  TRUE ~ "Peripheral"
)

table(zi_pi_results$Role)


第五步:Keystone物种鉴定

代码示例:

# === 综合指标鉴定Keystone种 ===
keystone_score <- data.frame(
  Node = V(ig)$name,
  Degree = degree(ig),
  Betweenness = betweenness(ig, normalized = TRUE),
  Closeness = closeness(ig, normalized = TRUE),
  Eigenvector = eigen_centrality(ig)$vector,
  Zi = zi_pi_results$Zi,
  Pi = zi_pi_results$Pi,
  Role = zi_pi_results$Role
)

# 综合得分(多个中心性指标的标准化加权和)
keystone_score$composite <- scale(keystone_score$Degree) * 0.25 +
  scale(keystone_score$Betweenness) * 0.30 +
  scale(keystone_score$Closeness) * 0.15 +
  scale(keystone_score$Eigenvector) * 0.30

# Top keystone候选
keystones <- keystone_score[order(-keystone_score$composite), ][1:10, ]
print(keystones[, c("Node", "Degree", "Betweenness", "Role", "composite")])

# === 组间网络稳定性比较 ===
# 比较健康vs疾病的网络稳定性
# 分别构建两组的网络并比较拓扑指标
compare_networks <- data.frame(
  Metric = c("Nodes", "Edges", "Avg_Degree", "Clustering", "Modularity",
             "Robustness_random", "Robustness_targeted", "Neg_edge_ratio"),
  Healthy = c(vcount(ig_healthy), ecount(ig_healthy), 
              mean(degree(ig_healthy)), transitivity(ig_healthy),
              modularity(cluster_louvain(ig_healthy)),
              rob_healthy_random, rob_healthy_targeted,
              neg_ratio_healthy),
  Disease = c(vcount(ig_disease), ecount(ig_disease),
              mean(degree(ig_disease)), transitivity(ig_disease),
              modularity(cluster_louvain(ig_disease)),
              rob_disease_random, rob_disease_targeted,
              neg_ratio_disease)
)
print(compare_networks)


实战命令

# === 微生物组网络分析流程 ===
Rscript network_analysis.R --otu_table otu_table.csv \
  --metadata metadata.csv \
  --method spieceasi \
  --groups "Healthy,Disease" \
  --output network_results/

面试常问点

Q1:为什么SpiecEasi比简单相关(如Pearson/Spearman)更适合构建微生物网络?

A: (1) 微生物数据是组成性的——相对丰度之和=1,导致物种间固有负相关(spurious correlation)。SpiecEasi使用CLR转换后的稀疏逆协方差估计,消除组成效应;(2) SpiecEasi估计的是条件独立性(偏相关)而非边际相关——两个物种可能因为共同依赖第三者而相关(间接关系),偏相关可以去除这种间接关联;(3) 稀疏化约束(LASSO)避免了在小样本下出现过多假阳性边。

Q2:网络鲁棒性分析中"随机攻击"和"定向攻击"的区别和意义?

A: 随机攻击模拟随机的物种丧失(如非特异性环境变化);定向攻击模拟关键物种(hub)的优先丧失(如特异性抗生素作用)。无标度网络(如多数微生物网络)对随机攻击鲁棒(少数hub保持连通)但对定向攻击脆弱(移除hub网络迅速崩溃)。如果某个群落对定向攻击也很鲁棒,说明它的连接更均匀分布(没有过度依赖少数hub)。

Q3:如何鉴定微生物网络中的Keystone种?

A: Keystone种是对群落结构和功能有不成比例影响的物种。鉴定标准:(1) 高度中心性(betweenness centrality高——很多最短路径经过它);(2) 高Zi(模块内连接多=模块hub)和/或高Pi(跨模块连接多=connector);(3) 移除该节点后网络连通性显著下降;(4) 低丰度但高连接度(与其丰度不成比例的网络重要性)。通常综合多个指标排名,top 5-10%为keystone候选。

Q4:模块化(Modularity)在微生物生态学中如何解释?

A: 高模块化(Q>0.4)意味着网络可以分成若干紧密连接的子群(模块),模块间连接稀疏。生态学解释:(1) 每个模块可能代表一个功能性"公会"(guild)——共享相似生态位或代谢互作的物种集合;(2) 模块化结构增强稳定性——扰动被限制在一个模块内不会波及全网;(3) 模块的组成可能对应特定的代谢功能(如发酵模块、产甲烷模块)。

Q5:健康人群和疾病人群的微生物网络通常有什么差异?

A: 多项研究发现的一致性模式:(1) 疾病组网络连接更稀疏(边数少)——群落互作减弱;(2) 疾病组模块化降低——功能分区不清晰;(3) 疾病组对定向攻击更脆弱——少数物种承担过多连接;(4) 疾病组负相关(竞争)比例降低——调控机制削弱;(5) 疾病组keystone种可能不同或消失。但注意这些是趋势而非绝对规律。


易错点

1. 样本量不足构建网络

错误: 用20个样本构建包含200个物种的网络。 正确做法: 网络推断需要足够样本估计物种间关系。建议样本数至少是物种数的3-5倍。SpiecEasi可以处理p>n的情况(通过稀疏化),但样本太少时结果不稳定。建议≥50个样本。

2. 混淆共现与互作

错误: 将正相关解释为"互利共生",负相关解释为"竞争"。 正确做法: 共现网络反映的是统计关联,不等于直接互作。正相关可能因为共享生态位(对相同环境条件响应一致)而非互利共生。真正的互作需要实验验证(共培养、代谢组学)。

3. 不做随机网络对比

错误: 直接报告网络拓扑指标而不与零模型比较。 正确做法: 需要生成同等规模的随机网络(Erdős-Rényi或配置模型)作为对比。只有当真实网络的模块化/聚类系数显著高于随机网络时,才能声称有生物学意义的结构。

4. 稀有物种处理不当

错误: 将只在3个样本中出现的物种纳入网络。 正确做法: 过低prevalence的物种无法可靠估计其与其他物种的关系(大量零值导致相关系数不稳定)。建议保留在至少20-30%样本中出现的物种。

5. 组间网络比较不公平

错误: 健康组有100个样本但疾病组只有30个,直接比较网络拓扑。 正确做法: 样本量不同会系统性影响网络密度和拓扑指标(样本多→估计更准→边更多)。应下采样到相同样本量后再比较,或使用标准化指标。


补充知识

网络分析工具

工具功能语言
SpiecEasi组成性数据网络推断R
MENAP分子生态网络分析(MEN)R
FlashWeave大规模快速网络推断Julia
NetCoMi微生物网络比较R
Cytoscape网络可视化Java
igraph图论分析R/Python

理论基础

  • May's稳定性准则: 群落越大越密集越不稳定(但模块化可缓解)
  • 无标度网络: hub节点的存在使网络对随机扰动鲁棒但对定向攻击脆弱
  • 嵌套性vs模块化: 嵌套网络适合互利共生;模块化网络适合竞争主导的群落