You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

plot.phylo绘制进化树:12个门仅8个显色的技术求助

进化树分支颜色分配异常问题排查与解决

问题背景

使用plot.phylo可视化进化树时,将25个门合并为12个专属门+Others并分配对应颜色后,仅8个门的颜色正常显示。排查发现edge_color数据框中Acidobacteria占据了22091条分支中的13188条,颜色覆盖范围异常。

原始代码与统计数据

数据处理与颜色映射代码

otutab <- read.csv("otutab_info.csv")
otu_tree <- ape::read.tree("otus.nwk")
OTU_taxa <- otutab[,c(1,230)]
phylums <- OTU_taxa %>% pull(Phylum) %>% unique()

# 门数量统计
OTU_taxa %>% 
group_by(Phylum) %>% 
summarise(n()) %>%
print(n=Inf)

# 合并小众门为Others
a <- c("Aminicenantes","BRC1","Chlamydiae","Euryarchaeota","Fibrobacteres","Fusobacteria","Hydrogenedentes",
       "Parcubacteria","Spirochaetes","Armatimonadetes", "Candidatus_Saccharibacteria",
        "Ignavibacteriae", "Latescibacteria")  
OTU_taxa[OTU_taxa$Phylum %in% a, 'Phylum'] <- "Others"

# 门-颜色映射
phylum_color <- tribble(
  ~Phylum, ~color,
  "Acidobacteria","#00C9A7",
  "Gemmatimonadetes","#007554",
  "Deinococcus-Thermus","#ab5f1f",
  "Firmicutes","#FF8066",
  "Proteobacteria", "#B39CD0",
  "Actinobacteria", "#54c3ff",
  "Unassigned", "#75b831",
  "Bacteroidetes","#b04380",
  "Chloroflexi","#e69f00",
  "Nitrospirae","#114662",
  "Planctomycetes","#D5CABD",
  "Verrucomicrobia","#845EC2",
  "Others", "#a6a6a6"
) %>%
  deframe()

# 分支颜色分配逻辑
edge_color <- as.data.frame(otu_tree$edge)
edge_color$color <- '#a6a6a6'
phylums <- OTU_taxa %>% pull(Phylum) %>% unique()
phylums <- phylums[-13] # 移除Others

for (phylum_ in phylums){
     Ancestor <- getMRCA(otu_tree,OTU_taxa %>% filter(Phylum==phylum_) %>% pull(OTUID))
     Children <- getDescendants(otu_tree,node=Ancestor)
     edge_color[edge_color$V2 %in% Children, 'color'] <- phylum_color[phylum_]
}

# 绘图
plot.phylo(otu_tree, type = "radial", show.tip.label=F, x.lim = c(-1,1), y.lim=c(-1,1), edge.color = edge_color$color)

排查确认信息

已验证phylum_color、phylums等颜色映射关系准确,门合并逻辑无错误,但Acidobacteria分支覆盖范围异常:

phylum_color
      Acidobacteria    Gemmatimonadetes Deinococcus-Thermus          Firmicutes 
          "#00C9A7"           "#007554"           "#ab5f1f"           "#FF8066" 
      Proteobacteria      Actinobacteria          Unassigned       Bacteroidetes 
          "#B39CD0"           "#54c3ff"           "#75b831"           "#b04380" 
        Chloroflexi         Nitrospirae      Planctomycetes     Verrucomicrobia 
          "#e69f00"           "#114662"           "#D5CABD"           "#845EC2" 
              Others 
          "#a6a6a6" 

phylums
 [1] "Proteobacteria"      "Actinobacteria"      "Firmicutes"          "Unassigned"         
 [5] "Deinococcus-Thermus" "Bacteroidetes"       "Gemmatimonadetes"    "Acidobacteria"      
 [9] "Verrucomicrobia"     "Chloroflexi"         "Planctomycetes"      "Nitrospirae"

问题根源

  1. getDescendants的范围问题:该函数会返回指定节点的所有后代节点(包括节点本身),若后续门的祖先节点被包含在前一门的后代节点中,颜色会被覆盖。
  2. 循环顺序影响:Acidobacteria在循环中靠后,会覆盖前面门的分支颜色。
  3. 分支匹配逻辑错误:用edge_color$V2 %in% Children匹配分支时,会错误包含其他门的祖先分支,导致大范围颜色覆盖。

解决方法

修正后的分支颜色分配代码

otutab <- read.csv("otutab_info.csv")
otu_tree <- ape::read.tree("otus.nwk")
OTU_taxa <- otutab[,c(1,230)]

# 合并小众门为Others
a <- c("Aminicenantes","BRC1","Chlamydiae","Euryarchaeota","Fibrobacteres","Fusobacteria","Hydrogenedentes",
       "Parcubacteria","Spirochaetes","Armatimonadetes", "Candidatus_Saccharibacteria",
        "Ignavibacteriae", "Latescibacteria")  
OTU_taxa[OTU_taxa$Phylum %in% a, 'Phylum'] <- "Others"

# 门-颜色映射
phylum_color <- tribble(
  ~Phylum, ~color,
  "Acidobacteria","#00C9A7",
  "Gemmatimonadetes","#007554",
  "Deinococcus-Thermus","#ab5f1f",
  "Firmicutes","#FF8066",
  "Proteobacteria", "#B39CD0",
  "Actinobacteria", "#54c3ff",
  "Unassigned", "#75b831",
  "Bacteroidetes","#b04380",
  "Chloroflexi","#e69f00",
  "Nitrospirae","#114662",
  "Planctomycetes","#D5CABD",
  "Verrucomicrobia","#845EC2",
  "Others", "#a6a6a6"
) %>%
  deframe()

# 初始化edge颜色
edge_color <- as.data.frame(otu_tree$edge)
edge_color$color <- '#a6a6a6'

# 匹配OTU对应的tip节点
otu_node <- match(OTU_taxa$OTUID, otu_tree$tip.label)

# 从每个OTU的tip节点向上追溯父节点,为分支上色
for (i in seq_along(otu_node)) {
  current_node <- otu_node[i]
  phylum <- OTU_taxa$Phylum[i]
  color <- phylum_color[phylum]
  
  # 向上遍历至根节点,为每个分支分配对应颜色
  while (current_node != 0) {
    edge_idx <- which(edge_color$V2 == current_node)
    if (length(edge_idx) > 0) {
      edge_color$color[edge_idx] <- color
      current_node <- edge_color$V1[edge_idx]
    } else {
      break
    }
  }
}

# 绘图
plot.phylo(otu_tree, type = "radial", show.tip.label=F, x.lim = c(-1,1), y.lim=c(-1,1), edge.color = edge_color$color)

关键改进点

  • 从每个OTU的tip节点向上追溯父节点,精准为对应分支上色,避免大范围节点匹配导致的颜色覆盖。
  • 逻辑直观,确保每个门的分支都能被正确标记为对应颜色。

内容的提问来源于stack exchange,提问作者Alex

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 09:32:02