Monday, November 7, 2022

Creating a phylomorphospace projection (using phytools) that pretty much looks like it was made with ggplot2

I recently stumbled on the following tweet:

My immediate interpretation was that the user wanted to create a phylomorphospace projection that looked like it had been made using ggplot2.

I'm not so sure that's correct, but we could do that – so why not?

Let's try.

First load phytools and some data.

library(phytools)
data(sunfish.tree)
sunfish.tree
## 
## Phylogenetic tree with 28 tips and 27 internal nodes.
## 
## Tip labels:
##  Acantharchus_pomotis, Lepomis_gibbosus, Lepomis_microlophus, Lepomis_punctatus, Lepomis_miniatus, Lepomis_auritus, ...
## 
## The tree includes a mapped, 2-state discrete character
## with states:
##  non, pisc
## 
## Rooted; includes branch lengths.
data(sunfish.data)
head(sunfish.data)
##                      feeding.mode gape.width buccal.length
## Acantharchus_pomotis         pisc      0.114        -0.009
## Lepomis_gibbosus              non     -0.133        -0.009
## Lepomis_microlophus           non     -0.151         0.012
## Lepomis_punctatus             non     -0.103        -0.019
## Lepomis_miniatus              non     -0.134         0.001
## Lepomis_auritus               non     -0.222        -0.039

Because the tree is a "simmap" object, we'll next convert it to a simple "phylo" object before plotting. We'll also pull out the two quantitative characters from our input data frame.

tree<-as.phylo(sunfish.tree)
X<-sunfish.data[,2:3]

Now, let's create an ordinary phylomorphospace plot using the default settings.

phylomorphospace(tree,X,ftype="off")

plot of chunk unnamed-chunk-3

OK, now we're ready to proceed.

## get the last plotted tree coordinates
## just for x & y limits
pp<-get("last_plot.phylo",envir=.PlotPhyloEnv)
## adjust axis label offsets
par(mgp = c(2, 0.4, 0))
## create empty plot
plot(NA,xlim=pp$x.lim,ylim=pp$y.lim,axes=FALSE,
    xlab="",ylab="")
## add grey polygon to simulate ggplot2 background
polygon(x=c(par()$usr[1:2],par()$usr[2:1]),
    y=c(rep(par()$usr[3],2),rep(par()$usr[4],2)),
    col="#dddddd",border=FALSE)
## set x & y tick positions
xticks<-pretty(par()$usr[1:2])
yticks<-pretty(par()$usr[3:4])
## add x-axis
axis(1,las=1,at=xticks,lwd.ticks=2,lwd=0,cex.axis=0.8,
    tcl=-0.25)
## simulate vertical white bars from ggplot2
xx<-seq(xticks[1],xticks[length(xticks)],
    length.out=2*length(xticks)-1)
abline(v=xx,col="white",lwd=rep(c(2,1),6))
## do the same for y-axis
axis(2,at=yticks,las=1,lwd.ticks=2,lwd=0,cex.axis=0.8,
    tcl=-0.25)
yy<-seq(yticks[1],yticks[length(yticks)],
    length.out=2*length(yticks)-1)
abline(h=yy,col="white",lwd=rep(c(2,1),6))
## add phylomorphospace without points
phylomorphospace(tree,X,ftype="off",add=TRUE,
    node.size=0)
## get ggplot2-like colors
hues<-seq(15,375,length=Ntip(tree)+1)
cols<-hcl(h=hues,l=65,c=100)[1:Ntip(tree)]
## add points
points(X[tree$tip.label,],col=cols,pch=16,cex=1.5)
## add x and y axis labels
title(xlab="gape width",ylab="buccal length",cex=0.9)

plot of chunk unnamed-chunk-4

Finally, let's combine it with our phylogeny, as in the original tweet.

layout(matrix(c(1,2),1,2),width=c(0.45,0.55))
plotTree(tree,ftype="off",mar=c(5.1,1.1,4.1,1.1))
pp<-get("last_plot.phylo",envir=.PlotPhyloEnv)
hues<-seq(15,375,length=Ntip(tree)+1)
cols<-hcl(h=hues,l=65,c=100)[1:Ntip(tree)]
points(pp$xx[1:Ntip(tree)],pp$yy[1:Ntip(tree)],
    pch=16,cex=1.5,col=cols)
par(mar=c(5.1,4.1,4.1,2.1),mgp=c(2,0.4,0))
phylomorphospace(tree,X,ftype="off",axes=FALSE,bty="n",
    xlab="",ylab="",node.size=0)
pp<-get("last_plot.phylo",envir=.PlotPhyloEnv)
polygon(x=c(par()$usr[1:2],par()$usr[2:1]),
    y=c(rep(par()$usr[3],2),rep(par()$usr[4],2)),
    col="#dddddd",border=FALSE)
xticks<-pretty(par()$usr[1:2])
yticks<-pretty(par()$usr[3:4])
axis(1,las=1,at=xticks,lwd.ticks=2,lwd=0,cex.axis=0.8,
    tcl=-0.25)
xx<-seq(xticks[1],xticks[length(xticks)],
    length.out=2*length(xticks)-1)
abline(v=xx,col="white",lwd=rep(c(2,1),6))
axis(2,at=yticks,las=1,lwd.ticks=2,lwd=0,cex.axis=0.8,
    tcl=-0.25)
yy<-seq(yticks[1],yticks[length(yticks)],
    length.out=2*length(yticks)-1)
abline(h=yy,col="white",lwd=rep(c(2,1),6))
phylomorphospace(tree,X,ftype="off",add=TRUE,
    node.size=0)
points(X[tree$tip.label,],col=cols,pch=16,cex=1.5)
title(xlab="gape width",ylab="buccal length",cex=0.9)

plot of chunk unnamed-chunk-5