ggtree——处理数据

ggtree
Author

大番薯本薯

Published

January 9, 2026

Modified

January 9, 2026

与ggtree包相关的R包由很多,本节主要介绍treeio,tidytree两个R包。它们用来对读取树文件,生成树对象,以及操作树对象。更多有关树的读取与操作可以参考ape包与phytools包。

本节及后序小节的内容主要来源于ggtree书中的第1-2,4-7,10,12章。

树文件格式

树文件的格式有多种,常见的有三种:Newick,NEXUS,Phylip。

Newick

仅包含最基本的树结构,树结构特点:

  • 使用括号()表示嵌套的分支关系。
  • 用逗号,分隔同级子节点。
  • 用冒号:后接数字表示分支长度。
  • 整棵树以分号;结尾。
((t2:0.04,t1:0.34):0.89,(t5:0.37,(t4:0.03,t3:0.67):0.9):0.59);

NEXUS

由许多模块化结构(block)组成,每个block以BEGIN开始,以END;结束,旨在将树信息与meta信息统合在一起。

#NEXUS

BEGIN TAXA;
  DIMENSIONS NTAX=4;
  TAXLABELS SpeciesA SpeciesB SpeciesC SpeciesD;
END;

BEGIN CHARACTERS;
  DIMENSIONS NCHAR=6;
  FORMAT DATATYPE=DNA MISSING=? GAP=-;
  MATRIX
    SpeciesA  ATGCGT
    SpeciesB  ATGAGT
    SpeciesC  ACGCGT
    SpeciesD  TTGCGT
  ;
END;

BEGIN TREES;
  TREE tree1 = (SpeciesA:0.1,SpeciesB:0.2,(SpeciesC:0.3,SpeciesD:0.4):0.5);
END;

Phylip

主要用于存储多序列比对(MSA)数据,供构建系统发育树使用。其结构特点:

  • 第一行:两个整数,分别表示序列数量和序列长度。
  • 后续每行:一个最多10个字符的序列名 + 空格 + 序列本身。
  • 序列名不足10字符时需用空格补齐(严格对齐)。
4 6
SpeciesA  ATGCGT
SpeciesB  ATGAGT
SpeciesC  ACGCGT
SpeciesD  TTGCGT

数据类型

使用treeio读取树文件后,可以生成三种对象类型,这三种都可以被ggtree处理:

  • phylo:是最基本的对象类型,由ape提供。

  • tbl_tree:一个tibble格式的数据框,方便使用tidyverse系列的函数进行操作,囊括了树的最基本信息,也可以追加其他相关信息。

  • treedata:将phylo对象与树相关的信息(数据框)组合起来就是treedata,由treeio提供。

三种对象可以相互转换:as_tibble(), as.phylo(), as.treedata()

suppressMessages(library(tidyverse))
suppressMessages(library(ape))
suppressMessages(library(ggtree))
suppressMessages(library(treeio))
suppressMessages(library(tidytree))

set.seed(2026)
tree_phylo <- rtree(4)
tree_phylo
#> 
#> Phylogenetic tree with 4 tips and 3 internal nodes.
#> 
#> Tip labels:
#>   t1, t2, t4, t3
#> 
#> Rooted; includes branch length(s).
class(tree_phylo)
#> [1] "phylo"

tbl_tree <- as_tibble(tree_phylo)
tbl_tree
#> # A tbl_tree abstraction: 7 × 4
#> # which can be converted to treedata or phylo 
#> # via as.treedata or as.phylo
#>   parent  node branch.length label
#>    <int> <int>         <dbl> <chr>
#> 1      5     1       0.466   t1   
#> 2      6     2       0.253   t2   
#> 3      7     3       0.00593 t4   
#> 4      7     4       0.692   t3   
#> 5      5     5      NA       <NA> 
#> 6      5     6       0.861   <NA> 
#> # ℹ 1 more row

tree_data <- as.treedata(tbl_tree)
tree_data
#> 'treedata' S4 object'.
#> 
#> ...@ phylo:
#> 
#> Phylogenetic tree with 4 tips and 3 internal nodes.
#> 
#> Tip labels:
#>   t1, t2, t4, t3
#> 
#> Rooted; includes branch length(s).
#> 
#> with the following features available:
#>   ''.
#> 
#> # The associated data tibble abstraction: 7 × 3
#> # The 'node', 'label' and 'isTip' are from the phylo tree.
#>    node label isTip
#>   <int> <chr> <lgl>
#> 1     1 t1    TRUE 
#> 2     2 t2    TRUE 
#> 3     3 t4    TRUE 
#> 4     4 t3    TRUE 
#> 5     5 <NA>  FALSE
#> 6     6 <NA>  FALSE
#> 7     7 <NA>  FALSE

操作树对象

获取子树

tidytree提供了一些函数用来提取一个子树:

  • child():获取某个node的下一级所有节点。
  • offspring():获取某个node的所有下属节点。
  • parent():获取某个node的上一级所有节点。
  • ancestor():获取某个node的所有祖先节点。
  • sibling():获取某个node的同一级所有节点。
  • MRCA():获取某些nodes的最近共同祖先。
  • tree_subset():获取某个节点的前,由参数定义层级的所有节点。
  • drop.tip():丢弃某些tip。
# 适配三种对象类型
child(tbl_tree, 5)
#> # A tibble: 2 × 4
#>   parent  node branch.length label
#>    <int> <int>         <dbl> <chr>
#> 1      5     1         0.466 t1   
#> 2      5     6         0.861 <NA>
offspring(tbl_tree, 5)
#> # A tibble: 6 × 4
#>   parent  node branch.length label
#>    <int> <int>         <dbl> <chr>
#> 1      5     1       0.466   t1   
#> 2      6     2       0.253   t2   
#> 3      7     3       0.00593 t4   
#> 4      7     4       0.692   t3   
#> 5      5     6       0.861   <NA> 
#> 6      6     7       0.581   <NA>
parent(tbl_tree, 2)
#> # A tibble: 1 × 4
#>   parent  node branch.length label
#>    <int> <int>         <dbl> <chr>
#> 1      5     6         0.861 <NA>
ancestor(tbl_tree, 2)
#> # A tibble: 2 × 4
#>   parent  node branch.length label
#>    <int> <int>         <dbl> <chr>
#> 1      5     5        NA     <NA> 
#> 2      5     6         0.861 <NA>
sibling(tbl_tree, 2)
#> # A tibble: 1 × 4
#>   parent  node branch.length label
#>    <int> <int>         <dbl> <chr>
#> 1      6     7         0.581 <NA>
MRCA(tbl_tree, 2, 3)
#> # A tibble: 1 × 4
#>   parent  node branch.length label
#>    <int> <int>         <dbl> <chr>
#> 1      5     6         0.861 <NA>

# 不适配 tbl_tree
# tree_subset(tree_data, node = 5, levels_back = 0)
tree_s <- tree_subset(tree_phylo, node = "t4", levels_back = 2)
tree_s
#> 
#> Phylogenetic tree with 3 tips and 2 internal nodes.
#> 
#> Tip labels:
#>   t2, t4, t3
#> 
#> Rooted; includes branch length(s).

tree_d <- drop.tip(tree_phylo, "t3")
tree_d
#> 
#> Phylogenetic tree with 3 tips and 2 internal nodes.
#> 
#> Tip labels:
#>   t1, t2, t4
#> 
#> Rooted; includes branch length(s).

tree_subset()的提取方式——数竖线;在提取的同时会默认将该node的offspring节点归为一组,其他归为另一组,并添加group信息。

p1 <- ggtree(tree_phylo) + geom_tiplab()
p2 <- ggtree(tree_s, aes(color = group)) + geom_tiplab()
p3 <- ggtree(tree_d) + geom_tiplab()
plot_list(p1, p2, p3)

合并树

treeio::merge_tree()只能合并拓扑结构相同但附属信息不同的树,无法合并拓扑结构不同的树。

tree1 <- read.tree(text = "((A:0.1,B:0.2):0.3,(C:0.4,D:0.5):0.6);")
# tree3 <- read.tree(text = "((A:0.1,B:0.2):0.3,(C:0.4):0.6);")
tree2 <- read.tree(text = "((B:0.15,C:0.25):0.35,(E:0.45,F:0.55):0.65);")

merge_tree(tree1, tree2)
#> Error in `merge_tree()`:
#> ! input tree object is not supported...

追加相关meta信息

tidytree::full_join()可以将相关meta信息与树进行合并,适配所有对象类型。

# 添加额外信息
dat <- tibble(label = paste0("t", 1:4), trait = rnorm(4))
full_join(tbl_tree, dat, by = "label")
#> # A tbl_tree abstraction: 7 × 5
#> # which can be converted to treedata or phylo 
#> # via as.treedata or as.phylo
#>   parent  node branch.length label  trait
#>    <int> <int>         <dbl> <chr>  <dbl>
#> 1      5     1       0.466   t1    -0.735
#> 2      6     2       0.253   t2    -1.02 
#> 3      7     3       0.00593 t4    -0.474
#> 4      7     4       0.692   t3     0.114
#> 5      5     5      NA       <NA>  NA    
#> 6      5     6       0.861   <NA>  NA    
#> # ℹ 1 more row

tidytree::groupClade()tidytree::groupOTU()分别用来根据“枝”或“tip”来进行分组。

treeClade <- groupClade(tbl_tree, c(6, 7))
treeClade
#> # A tbl_tree abstraction: 7 × 5
#> # which can be converted to treedata or phylo 
#> # via as.treedata or as.phylo
#>   parent  node branch.length label group
#>    <int> <int>         <dbl> <chr> <fct>
#> 1      5     1       0.466   t1    0    
#> 2      6     2       0.253   t2    1    
#> 3      7     3       0.00593 t4    2    
#> 4      7     4       0.692   t3    2    
#> 5      5     5      NA       <NA>  0    
#> 6      5     6       0.861   <NA>  1    
#> # ℹ 1 more row
p1 <- ggtree(as.treedata(treeClade), aes(color = group)) + geom_tiplab()


treeOTU <- groupOTU(tbl_tree, c("t1", "t4"), group_name = "fake_group")
treeOTU
#> # A tbl_tree abstraction: 7 × 5
#> # which can be converted to treedata or phylo 
#> # via as.treedata or as.phylo
#>   parent  node branch.length label fake_group
#>    <int> <int>         <dbl> <chr> <fct>     
#> 1      5     1       0.466   t1    1         
#> 2      6     2       0.253   t2    0         
#> 3      7     3       0.00593 t4    1         
#> 4      7     4       0.692   t3    0         
#> 5      5     5      NA       <NA>  1         
#> 6      5     6       0.861   <NA>  1         
#> # ℹ 1 more row
p2 <- ggtree(as.treedata(treeOTU), aes(color = fake_group)) + geom_tiplab()


plot_list(p1, p2)

将树相关的meta信息可以被绘制到树中,增加图片信息丰度。

修改树的根节点或枝长

root()会将outgroup排除后,重新计算根节点。

tree_rer <- root(tree_phylo, outgroup = "t3")
ggtree(tree_rer) + geom_tiplab()

treeio::rescale_tree()可以根据treedata格式中的numeric列重新定义枝的长度。

tree_rel <- tbl_tree
tree_rel$new.length <- c(1, 2, 3, 1, NA, 2, 2)
ggtree(rescale_tree(as.treedata(tree_rel), "new.length")) +
  geom_tiplab() +
  theme_tree2()

绘图数据

可以使用ggtree(tree)$datafortify()两种方式生成适配geom_tree()函数的数据,这在生成复杂图形及其有用。

set.seed(1024)
x <- rtree(30)
y <- rtree(30)
p1 <- ggtree(x, layout = "roundrect") +
  geom_tiplab(geom = "shadowtext", bg.colour = alpha("firebrick", .5)) +
  geom_hilight(
    mapping = aes(
      subset = node %in% c(38, 48, 58, 36),
      node = node,
      fill = as.factor(node)
    )
  ) +
  labs(fill = "clades for tree in left")

p2 <- ggtree(y)

d1 <- p1$data
d2 <- p2$data

## reverse x-axis and
## set offset to make the tree on the right-hand side of the first tree
d2$x <- max(d2$x) - d2$x + max(d1$x) + 1

pp <- p1 + geom_tree(data = d2, layout = "ellipse") +
  geom_tiplab(data = d2, hjust = 1, geom = "shadowtext", bg.colour = alpha("firebrick", .5)) +
  ggnewscale::new_scale_fill() +
  geom_hilight(
    data = d2,
    mapping = aes(
      subset = node %in% c(38, 48, 58),
      node = node,
      fill = as.factor(node)
    )
  ) +
  labs(fill = "clades for tree in right")

dd <- bind_rows(d1, d2) %>%
  filter(!is.na(label))

pp + geom_line(aes(x, y, group = label), data = dd, color = "grey")

Back to top