Wednesday, October 26, 2022

Plotting a tree based on the coordinates computed internally by phytools or ape plotting functions

At the end of a recent phylogenetic comparative methods in R workshop at the University of Wyoming, I showed how to write a very simple function that uses internals of ape and phytools to plot a simplified tree.

I tweeted about it, and one of my followers asked me to post about it on my blog.

Today, a colleague asked me how we could go from this slanted tree to a square “phylogram-style” plot.

First, the algorithm.

## load phytools
library(phytools)
## load an example tree
data(salamanders)
## call plotTree to compute the coordinates of our graph
plotTree(salamanders,plot=FALSE)
grid()
## pull the coordinates of our graph and plot them
pp<-get("last_plot.phylo",
    envir=.PlotPhyloEnv)
## this is out "last_plot.phylo" object
str(pp)
## List of 20
##  $ type           : chr "phylogram"
##  $ use.edge.length: logi TRUE
##  $ node.pos       : num 1
##  $ show.tip.label : logi TRUE
##  $ show.node.label: logi FALSE
##  $ font           : num 1
##  $ cex            : num 1
##  $ adj            : num 0
##  $ srt            : num 0
##  $ no.margin      : logi FALSE
##  $ label.offset   : num 0.2
##  $ x.lim          : num [1:2] 0 523
##  $ y.lim          : num [1:2] 1 26
##  $ direction      : chr "rightwards"
##  $ tip.color      : chr "black"
##  $ Ntip           : int 26
##  $ Nnode          : int 25
##  $ edge           : int [1:50, 1:2] 27 28 29 30 30 29 31 31 28 32 ...
##  $ xx             : num [1:51] 422 422 422 422 422 ...
##  $ yy             : num [1:51] 1 2 3 4 5 6 7 8 9 10 ...
## graph our vertices
points(pp$xx,pp$yy,pch=16)
## add our lines
invisible(apply(salamanders$edge,1,function(e,pp) 
    lines(pp$xx[e],pp$yy[e]),
    pp=pp))
## add our labels
text(pp$xx[1:Ntip(salamanders)],
    pp$yy[1:Ntip(salamanders)],
    salamanders$tip.label,pos=4)

plot of chunk unnamed-chunk-1

Cool.

Now let's combine everything into a single function without the grid or vertices.

funkyTree<-function(phy){
    plotTree(phy,plot=FALSE)
    pp<-get("last_plot.phylo",
        envir=.PlotPhyloEnv)
    invisible(apply(phy$edge,1,
        function(e,pp) 
        lines(pp$xx[e],pp$yy[e]),
        pp=pp))
    N<-Ntip(phy)
    text(pp$xx[1:N],pp$yy[1:N],
        gsub("_"," ",phy$tip.label),
        pos=4,font=3)
    cat("We did it!\n")
}
data(vertebrate.tree)
funkyTree(vertebrate.tree)

plot of chunk unnamed-chunk-2

## We did it!

Lastly, let's do a square phylogram. In this case, I'll switch from an apply call to a for loop, but we could also do this with apply.

In this case, the key jump is understanding that in the case of the slanted tree the vertices are the x and y coordinates of each parent and daughter; however, in the square representation we have to use the y coordinates of the daughters twice: once for the vertical position of the descendant node, and then a second time for the line ending of the vertical line that bisects the parent.

squareTree<-function(phy){
    plotTree(phy,plot=FALSE)
    pp<-get("last_plot.phylo",
        envir=.PlotPhyloEnv)
    for(i in 1:nrow(phy$edge)){
        lines(rep(pp$xx[phy$edge[i,1]],2),
            pp$yy[phy$edge[i,]])
        lines(pp$xx[phy$edge[i,]],
            rep(pp$yy[phy$edge[i,2]],2))
    }
    N<-Ntip(phy)
    text(pp$xx[1:N],pp$yy[1:N],
        gsub("_"," ",phy$tip.label),
        pos=4,font=3)
    cat("We did it!\n")
}
data(mammal.tree)
par(cex=0.8)
squareTree(mammal.tree)

plot of chunk unnamed-chunk-3

## We did it!

No comments:

Post a Comment

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