Monday, July 11, 2022

A simple function to plot "radial" (slanted, fan-style) trees using phytools

About two & a half years ago I posted about an interesting tree visualization style that you could call a slanted-fan tree. This is a circular tree graph, but in which the edges are connected in a slanted (i.e., “cladogram”) fashion.

Here's an example taken from a talk by Matt McGee at the SSB stand-alone meeting in 2020:

This style of tree can basically already be rendered using ape::plot.phylo with type="radial", but phytools so far lacks similar functionality. I promised this would be coming to a phytools update soon, but, more than two & a half years later, I've failed to live up to that promise!

Today, I'm just going to take one step closer & show what a stand-alone function to produce this type of plot might look like.

fanCladogram<-function(tree,use.edge.length=FALSE,...){
    if(use.edge.length==FALSE){
        if(hasArg(power)) power<-list(...)$power
        else power<-0.6
        tree<-compute.brlen(tree,power=power)
    }
    args<-list(...)
    args$power<-NULL
    args$tree<-tree
    args$type<-"fan"
    args$color<-"transparent"
    do.call(plotTree,args)
    obj<-get("last_plot.phylo",envir=.PlotPhyloEnv)
    X<-cbind(obj$xx[1:Ntip(tree)],
        obj$yy[1:Ntip(tree)])
    rownames(X)<-tree$tip.label
    A<-cbind(obj$xx[1:tree$Nnode+Ntip(tree)],
        obj$yy[1:tree$Nnode+Ntip(tree)])
    rownames(A)<-1:tree$Nnode+Ntip(tree)
    pp.args<-list(...)
    pp.args$power<-NULL
    pp.args$tree<-tree
    pp.args$X<-X
    pp.args$A<-A
    pp.args$ftype<-"off"
    pp.args$node.size<-c(0,0)
    pp.args$xlim<-obj$x.lim
    pp.args$ylim<-obj$y.lim
    pp.args$xaxt<-"n"
    pp.args$type<-NULL
    pp.args$add<-TRUE
    do.call(phylomorphospace,pp.args)
}

That's pretty much it. Simple, right?

By default, I set use.edge.length=FALSE. This is because (just as with a regular slanted phylogram) there's no way to plot a fan-style slanted phylogram while guaranteeing that edges do not cross each other! (The problem can be largely, if not entirely, averted by setting the edge lengths to the so-called 'Grafen' values with ape::compute.brlen.)

OK, now let's try it. In this example, I'll use a tree of squamtes that is featured in my upcoming book with Luke Harmon, but is originally from a paper by Matt Brandley et al. (2008).

library(phytools) ## load phytools
squamate.tree<-read.nexus(file="http://www.phytools.org/Rbook/11/squamate.tre")
fanCladogram(squamate.tree,lwd=1,ftype="i",fsize=0.3)

plot of chunk unnamed-chunk-2

Neat.

Let's try a couple of other tricks.

Since we are using phytools::plotTree internally to compute the node positions, we should also be able to use other options of the plotTree function, such as part which, for a fan-style tree, tells R what fraction of the circle to use for the tree. Let's try, this time using a phylogeny of cetaceans from Morlon et al. (2011). I this case, I think I'll also 'ladderize' the tree, just to see what that looks like.

whale.tree<-read.tree(file="http://www.phytools.org/Rbook/10/Cetacea.phy")
fanCladogram(ladderize(whale.tree),lwd=2,ftype="i",fsize=0.7,part=0.5)

plot of chunk unnamed-chunk-3

We can likewise graph a stochastic character mapped tree. To illustrate this, why don't I use a phylogeny of bony fish & parental care from Benun Sutton & Wilson (2019).

bonyfish.tree<-read.tree(file="http://www.phytools.org/Rbook/7/bonyfish.tre")
bonyfish.data<-read.csv(file="http://www.phytools.org/Rbook/7/bonyfish.csv",
    row.names=1,stringsAsFactors=TRUE)
care<-setNames(bonyfish.data$paternal_care,rownames(bonyfish.data))
smap.bonyfish<-make.simmap(bonyfish.tree,care,model="ARD",nsim=1)
## make.simmap is sampling character histories conditioned on
## the transition matrix
## 
## Q =
##             male         none
## male 0.000000000  0.000000000
## none 0.002229021 -0.002229021
## (estimated using likelihood);
## and (mean) root node prior probabilities
## pi =
## male none 
##  0.5  0.5
## Done.
cols<-setNames(palette()[c(2,4)],levels(care))
fanCladogram(bonyfish.tree,lwd=4,part=0.5,fsize=0.6)
par(fg="transparent") ## to avoid plotting tip labels x 2
fanCladogram(smap.bonyfish,lwd=2,colors=cols,part=0.5,fsize=0.6,add=TRUE)
par(fg="black")
legend("topleft",levels(care),pch=22,pt.bg=cols,pt.cex=2,
    title="parental care",bty="n")

plot of chunk unnamed-chunk-4

Lastly, we ought to be able to use this style for a "contMap" type visualization. For this example, I'm going to use a classic dataset for mammalian body mass evolution from Garland et al. (1992).

mammal.tree<-read.tree(file="http://www.phytools.org/Rbook/2/mammalHR.phy")
mammal.tree<-ladderize(mammal.tree)
mammal.data<-read.csv(file="http://www.phytools.org/Rbook/2/mammalHR.csv",
    row.names=1)
lnMass<-setNames(log(mammal.data$bodyMass),rownames(mammal.data))
cMap.mammals<-contMap(mammal.tree,lnMass,plot=FALSE)
cMap.mammals<-setMap(cMap.mammals,c("#440154FF","#21908CFF","#FDE725FF"))
fanCladogram(mammal.tree,lwd=6,part=0.5,fsize=0.8)
fanCladogram(cMap.mammals$tree,lwd=4,colors=cMap.mammals$cols,part=0.5,
    fsize=0.8,add=TRUE)
add.color.bar(0.75,cols=cMap.mammals$cols,lims=cMap.mammals$lims,
    digits=2,x=par()$usr[1]+0.1,y=0.9*par()$usr[4],prompt=FALSE,
    title="log(body mass kg)",subtitle="")

plot of chunk unnamed-chunk-5

That's it.

3 comments:

  1. Can this be somehow combined with plotTree.wBars and arc.cladelabels?

    ReplyDelete
    Replies
    1. Here is an example of using this method with plotTree.wBars. Later I hope to add one to the blog showing arc.cladelabels. Hope this helps!

      Delete
    2. Hello Liam, thank you very much for your response!!!! It helped me a lot. I was able to add the bars to the tree :))) (you are awesome!!!)... I will be attentive to your blog for showing arc.cladelabels.

      Thank you again for your help!

      Delete

Note: due to the very large amount of spam, all comments are now automatically submitted for moderation.