Thursday, July 21, 2016

More on showing a barplot next to a plotted tree

I just added a new function to phytools - plotTree.barplot - to reproduce the visualization shown in a previous recent post.

The idea is to put a barplot next to a plotted tree, but in which the bars line up nicely with the tips.

There already exists a function in phytools to do this kind of visual. It is quite flexible, but it works by just drawing rectangles in the plotting device where we have already drawn a tree. To do this, the user needs to set a scale for the rectangles (the units of measure for our trait are not usually scaled to the edges of the phylogeny), and there is not an obvious way to include an axis.

Here is a demo of that function, using the data & tree that I employed in my prior recent demo:

library(phytools)
plotTree.wBars(eel.tree,bsize,scale=0.5,tip.label=TRUE,fsize=0.7)

plot of chunk unnamed-chunk-1

The new implementation results in a highly similar plot - but instead of attempting to plot the bars & the tree in the same device, it opens a plot array using par()$mfrow and then prints the tree & bar plot to different subplots. This means that they are no longer required to be rendered to the same scale, and it is very easy to include an axis or other labels on the bar plot.

To accomplish this & still permit the user to pass whatever arguments they wanted to plotTree and barplot, I have made use of the function do.call, which executes a function with a list of arguments passed to it. That means that arguments that the user wants to send to each of these functions needs to be supplied in two lists: args.plotTree (to be passed to plotTree), and args.barplot.

First, here is the function code:

plotTree.barplot<-function(tree,x,args.plotTree=list(),
    args.barplot=list()){
    cw<-reorder(tree)
    args.barplot$height<-x[cw$tip.label]
    args.barplot$plot<-FALSE
    args.barplot$horiz<-TRUE
    args.barplot$axes<-FALSE
    args.barplot$names.arg<-""
    if(is.null(args.barplot$space)) args.barplot$space<-0.7
    if(is.null(args.barplot$mar)) 
        args.barplot$mar<-c(5.1,0,2.1,1.1)
    else args.barplot$mar[2]<-0.1
    args.plotTree$tips<-setNames(do.call(barplot,args.barplot)[,1],
        cw$tip.label)
    args.barplot$plot<-TRUE
    args.barplot$ylim<-range(args.plotTree$tips)
    args.plotTree$tree<-cw
    if(is.null(args.plotTree$mar)) 
        args.plotTree$mar<-c(5.1,1.1,2.1,0)
    else {
        args.plotTree$mar[4]<-0.1
    }
    if(args.plotTree$mar[1]!=args.barplot$mar[1])
        args.plotTree$mar[1]<-args.barplot$mar[1]
    if(args.plotTree$mar[3]!=args.barplot$mar[3])
        args.plotTree$mar[3]<-args.barplot$mar[3]
    if(is.null(args.plotTree$ftype)) args.plotTree$ftype<-"i"
    if(is.null(args.plotTree$lwd)) args.plotTree$lwd<-1
    par(mfrow=c(1,2))
    do.call(plotTree,args.plotTree)
    par(mar=args.barplot$mar)
    obj<-do.call(barplot,args.barplot)
    axis(1)
    if(!is.null(args.barplot$xlab)) title(xlab=args.barplot$xlab)
    else title(xlab="x")
    invisible(obj)
}

Now here is an example:

plotTree.barplot(eel.tree,bsize,list(fsize=0.7),
    list(col="blue",space=1,log="x",
    xlim=c(10,max(bsize)),xlab="length (cm)"))

plot of chunk unnamed-chunk-3

You get the idea.

1 comment: