ggtree for phylogenetic trees

Plot
Author

Jie Hua

Published

June 24, 2025

Modified

June 24, 2025

Basic Information

Phylogenetic trees are used to show the evolutionary relationships between different species or genes. The ggtree package in R is a powerful tool for visualizing these trees, allowing for customization and integration with other data types.

Official tutorial: https://bioconductor.org/packages/devel/bioc/vignettes/ggtreeExtra/inst/doc/ggtreeExtra.html

https://yulab-smu.top/treedata-book/

Installation

pak::pkg_install("ggtree")
pak::pkg_install("ggtreeExtra")
pak::pkg_install("ggstar")
pak::pkg_install("treeio")
pak::pkg_install("ggnewscale")

Load demo files

# Load required packages
library(ggplot2)
library(ggtree)
library(ggtreeExtra)
library(ggstar)
library(treeio)
library(ggnewscale)
# The path of tree file.
trfile <- system.file("extdata", "tree.nwk", package="ggtreeExtra")
# The path of file to plot tip point.
tippoint1 <- system.file("extdata", "tree_tippoint_bar.csv", package="ggtreeExtra")
# The path of first layer outside of tree.
ring1 <- system.file("extdata", "first_ring_discrete.csv", package="ggtreeExtra")
# The path of second layer outside of tree.
ring2 <- system.file("extdata", "second_ring_continuous.csv", package="ggtreeExtra")
# The tree file was imported using read.tree. If you have other tree format files, you can use corresponding functions from treeio package to read it.
tree <- read.tree(trfile)

# This dataset will to be plotted point and bar.
dat1 <- read.csv(tippoint1)
knitr::kable(head(dat1), caption = "Demo tree data 1", format = "pipe")
Demo tree data 1
ID Location Length Group Abundance
DE0655_HCMC_2001 HK 0.1786629 Yes 12.331055
MS0111_HCMC_1996 HK 0.2105236 Yes 9.652052
MS0063_HCMC_1995 HK 1.4337983 Yes 11.584822
DE0490_HCMC_2000 HK 0.3823731 Yes 7.893231
DE0885_HCMC_2001 HK 0.8478901 Yes 12.117232
DE0891_HCMC_2001 HK 1.5038646 Yes 10.819734
# This dataset will to be plotted heatmap
dat2 <- read.csv(ring1)
knitr::kable(head(dat2), caption = "Demo tree data 2", format = "pipe")
Demo tree data 2
ID Pos Type
DE0846_HCMC_2001 8 type2
MS0034_HCMC_1995 8 type2
EG1017_HCMC_2009 6 type2
KH18_HCMC_2009 5 type2
10365_HCMC_2010 7 type2
EG1021_HCMC_2009 1 type1
# This dataset will to be plotted heatmap
dat3 <- read.csv(ring2)
knitr::kable(head(dat3), caption = "Demo tree data 3", format = "pipe")
Demo tree data 3
ID Type2 Alpha
MS0004_HCMC_1995 p3 0.2256195
DE1150_HCMC_2002 p2 0.2222086
MS0048_HCMC_1995 p2 0.1881510
HUE57_HCMC_2010 p3 0.1939088
DE1486_HCMC_2002 p2 0.2018493
DE1165_HCMC_2002 p3 0.1812997

Fan layout tree

# The format of the datasets is the long shape for ggplot2. If you have short shape dataset,
# you can use `melt` of `reshape2` or `pivot_longer` of `tidyr` to convert it.

# We use ggtree to create fan layout tree. 
p <- ggtree(tree, layout="fan", open.angle=10, size=0.5)
#> Scale for y is already present.
#> Adding another scale for y, which will replace the existing scale.
p

Fan layout tree

Add annotation dataset to tree

## Next, we can use %<+% of ggtree to add annotation dataset to tree.
#p1 <- p %<+% dat1
#p1
## We use geom_star to add point layer outside of tree.
#p2 <- p1 + 
#      geom_star(
#          mapping=aes(fill=Location, size=Length, starshape=Group),
#          starstroke=0.2
#      ) +
#      scale_size_continuous(
#          range=c(1, 3),
#          guide=guide_legend(
#                     keywidth=0.5, 
#                     keyheight=0.5, 
#                     override.aes=list(starshape=15), 
#                     order=2)
#      ) + 
#      scale_fill_manual(
#          values=c("#F8766D", "#C49A00", "#53B400", "#00C094", "#00B6EB", "#A58AFF", "#FB61D7"),
#          guide="none" # don't show the legend.
#      ) + 
#      scale_starshape_manual(
#          values=c(1, 15),
#          guide=guide_legend(
#                    keywidth=0.5, # adjust width of legend
#                    keyheight=0.5, # adjust height of legend
#                    order=1 # adjust the order of legend for multiple legends.
#                ),
#          na.translate=FALSE # to remove the NA legend.
#      ) 
#p2

# Or if you don't use %<+% to import annotation dataset, instead of `data` parameter of `geom_fruit`. 

# You should specify the column contained tip labels to y axis of `mapping`, here is `y=ID`.

# the `position` parameter was set to `identity` to add the points on the tip nodes.
p2 <- p + 
      geom_fruit(
          data=dat1,
          geom=geom_star,
          mapping=aes(y=ID, fill=Location, size=Length, starshape=Group),
          position="identity",
          starstroke=0.2
      ) + 
      scale_size_continuous(
          range=c(1, 3), # the range of size.
          guide=guide_legend(
                    keywidth=0.5, 
                    keyheight=0.5,
                    override.aes=list(starshape=15),
                    order=2
                )
      ) +
      scale_fill_manual(
          values=c("#F8766D", "#C49A00", "#53B400", "#00C094", "#00B6EB", "#A58AFF", "#FB61D7"),
          guide="none" 
      ) + 
      scale_starshape_manual(
          values=c(1, 15),
          guide=guide_legend(
                    keywidth=0.5,
                    keyheight=0.5,
                    order=1
                )
      )
p2

Fan layout tree with annotation

Add heatmap layer on the outer ring

# Next, we will add a heatmap layer on the outer ring of p2 using `geom_tile` of `ggplot2`.
# Since we want to map some variable of dataset to the `fill` aesthetics of `geom_tile`, but 
# the `fill` of p2 had been mapped. So I need use `new_scale_fill` of `ggnewscale` package to initialize it.
p3 <- p2 + 
      new_scale_fill() + 
      geom_fruit(
          data=dat2,
          geom=geom_tile,
          mapping=aes(y=ID, x=Pos, fill=Type),
          offset=0.08,   # The distance between external layers, default is 0.03 times of x range of tree.
          pwidth=0.25 # width of the external layer, default is 0.2 times of x range of tree.
      ) + 
      scale_fill_manual(
          values=c("#339933", "#dfac03"),
          guide=guide_legend(keywidth=0.5, keyheight=0.5, order=3)
      ) 
p3

Fan layout tree with heatmap

Add heatmap layer for continuous values

# You can also add heatmap layer for continuous values.
p4 <- p3 + 
      new_scale_fill() +
      geom_fruit(
          data=dat3,
          geom=geom_tile,
          mapping=aes(y=ID, x=Type2, alpha=Alpha, fill=Type2),
          pwidth=0.15,
          axis.params=list(
                          axis="x", # add axis text of the layer.
                          text.angle=-45, # the text angle of x-axis.
                          hjust=0  # adjust the horizontal position of text of axis.
                      )
      ) +
      scale_fill_manual(
          values=c("#b22222", "#005500", "#0000be", "#9f1f9f"),
          guide=guide_legend(keywidth=0.5, keyheight=0.5, order=4)
      ) +
      scale_alpha_continuous(
          range=c(0, 0.4), # the range of alpha
          guide=guide_legend(keywidth=0.5, keyheight=0.5, order=5)
      ) 
p4

Fan layout tree with heatmap for continuous values
# Then add a bar layer outside of the tree.
p5 <- p4 + 
      new_scale_fill() +
      geom_fruit(
          data=dat1,
          geom=geom_col,
          mapping=aes(y=ID, x=Abundance, fill=Location),  #The 'Abundance' of 'dat1' will be mapped to x
          pwidth=0.4,
          axis.params=list(
                          axis="x", # add axis text of the layer.
                          text.angle=-45, # the text size of axis.
                          hjust=0  # adjust the horizontal position of text of axis.
                      ),
          grid.params=list() # add the grid line of the external bar plot.
      ) + 
      scale_fill_manual(
          values=c("#F8766D", "#C49A00", "#53B400", "#00C094", "#00B6EB", "#A58AFF", "#FB61D7"),
          guide=guide_legend(keywidth=0.5, keyheight=0.5, order=6)
      ) +
      theme(#legend.position=c(0.96, 0.5), # the position of legend.
          legend.background=element_rect(fill=NA), # the background of legend.
          legend.title=element_text(size=7), # the title size of legend.
          legend.text=element_text(size=6), # the text size of legend.
          legend.spacing.y = unit(0.02, "cm")  # the distance of legends (y orientation).
      ) 
p5

Fan layout tree with heatmap for continuous values