Plotting phylogenetic trees

Phylo defines recipes for all AbstractTrees, allowing them to be plotted with Plots.jl.

Keywords

It adds these keywords to the ones initially supported by Plots.jl:

  • treetype: choosing :fan or :dendrogram determines the shape of the tree
  • marker_group: applies the group keyword to node markers
  • line_group: applies the group keyword to branch lines
  • showtips: true (the default) shows the leaf names
  • tipfont: a tuple defining the font to use for leaf names (default is (7,)),

which sets the font size to 7 - for font definitions see Plots annotation fonts

Example plots

For this example, we will use the phylogeny of all extant hummingbird species.

Read the tree and plot it using two different treetypes. The default is :dendrogram

using Phylo, Plots
default(linecolor = :black, size = (400, 400)) # looks nicer with black lines
hummers = open(parsenewick, Phylo.path("hummingbirds.tree"))
plot(hummers, size = (400, 600), showtips = false)
Example block output

For larger trees, the :fan treetype may work better

plot(hummers, treetype = :fan)
Example block output

Sorting trees for plotting

Many phylogenies look more aesthetically pleasing if the descendants from each node are sorted in order of their size. This is called ladderize in some other packages.

sort!(hummers, rev = true)
plot(hummers, treetype = :fan)
Example block output

Coloring branches or nodes by a variable

It is common in evolutionary studies to color the branches or node markers with the value of some variable. Plots already offers the keyword attributes marker_z and line_z for these uses, and they also work on Phylo objects.

We can pass either

  • a Vector with the same number of elements as there are

branches / internal nodes, where the values follow a depthfirst order (because the tree is plotted in depthfirst order);

  • a Dict of node => value, with the value to be plotted for each node

(skipping nodes not in the Dict).

To demonstrate, let's start by defining a custom function for evolving a trait on the phylogeny according to Brownian motion, using the utility function map_depthfirst

evolve(tree) = map_depthfirst((val, node) -> val + randn(), 0., tree, Float64)
trait = evolve(hummers)
plot(hummers, treetype = :fan, line_z = trait, linecolor = :RdYlBu, linewidth = 5, showtips = false)
Example block output

The inbuilt facilities for sampling traits on trees on Phylo returns a Node => value Dict, which can also be passed to marker_z

brownsampler = BrownianTrait(hummers, "Trait")
plot(hummers,
     showtips = false, marker_z = rand(brownsampler),
     linewidth = 2, markercolor = :RdYlBu, size = (400, 600))
Example block output

We can also use the map_depthfirst utility function to highlight the clade descending from, e.g., Node 248. Here, the recursive function creates a vector of colors which will all be orange after encountering Node 248 but black before

clade_color = map_depthfirst((val, node) -> node == "Node 248" ? :orange : val, :black, hummers)
plot(hummers, linecolor = clade_color, showtips = false, linewidth = 2, size = (400, 600))
Example block output

The Plots attributes related to markers (markersize, markershape etc.) will put markers on the internal nodes (or on both internal and tip nodes if a longer) vector is passed). The series_attributes keyword is also supported and behaves the same way

plot(hummers,
     size = (400, 800),
     linecolor = :orange, linewidth = 5,
     markersize = 10, markercolor = :steelblue, markerstrokecolor = :white,
     series_annotations = text.(1:nnodes(hummers), 5, :center, :center, :white),
     tipfont = (4,))
Example block output

The marker_group and line_group keywords allow plotting discrete values onto nodes or branches within the phylogeny.

Let's randomly evolve a discrete trait for temperature preference on the tree, using rand! to add the modelled values to the tree's list of node data. In addition to taking a Vector or a Dict, marker_group also accepts the name of internal node data.

## evolve the trait and add it to the tree
using Random
@enum TemperatureTrait lowTempPref midTempPref highTempPref
tempsampler = SymmetricDiscreteTrait(hummers, TemperatureTrait, 0.4, "Temperature")
rand!(tempsampler, hummers)

## and plot it
plot(hummers, showtips = false,
   marker_group = "Temperature",
    legend = :topleft, msc = :white, treetype = :fan,
    c = [:red :blue :green])
Example block output
Phylo.map_depthfirstFunction
map_depthfirst(FUN, start, tree, eltype = nothing)

Apply FUN to each node in tree in depth-first order, and return the result. FUN must take two arguments, val and node, where val is the result of applying FUN to the previous node, and node is the current node. start specifies the initial value of val, and eltype specifies the type of the return value of FUN.

Examples

≡≡≡≡≡≡≡≡≡≡≡ Define a function to evolve a trait on the tree according to Brownian motion

julia> evolve(tree) = map_depthfirst((val, node) -> val + randn(), 0., tree, Float64)

source
Base.sortFunction
sort(::AbstractTree; rev = false)

Copies a tree and sorts its branches. See sort! for further details.

source
Base.sort!Function
sort!(::AbstractTree; rev = false)

Sorts the branches descending from each node by total number of descendants. This creates a clearer tree for plotting. The process is also called "ladderizing" the tree. Use rev=true to reverse the sorting order.

source