Plotting methods for comparative data and phylogenies


Continuous character methods

In this tutorial I'm going to quickly overview a range of plotting methods for phylogenies & comparative data that are implemented in the phytools package.

Continuous character methods

The first plotting method I'll illustrate is called a 'traitgram' which is a projection of the tree into a space defined by trait values & time since the root. So, for example:

## first load phytools
require(phytools)
## Loading required package: phytools
## Loading required package: ape
## Loading required package: maps
## simulate a tree
tree<-pbtree(n=26,tip.label=LETTERS)
plotTree(tree)

plot of chunk unnamed-chunk-1

## simulate data under Brownian motion
x<-fastBM(tree)
x
##          A          B          C          D          E          F 
## -2.5058532 -2.5353028 -1.5198028 -3.3444568 -2.6098775 -2.6830252 
##          G          H          I          J          K          L 
## -2.7673502 -0.5024255 -0.4028755 -2.1903261 -1.3840878 -0.7009695 
##          M          N          O          P          Q          R 
## -1.6265088 -1.8593068 -2.7533331 -3.1032355 -3.1114163 -2.2118431 
##          S          T          U          V          W          X 
## -3.7306722 -2.7624590 -4.5961496 -2.5471489 -2.4448663 -2.7218879 
##          Y          Z 
##  0.7788399  1.6670663
## plot a simple traitgram
phenogram(tree,x,spread.labels=TRUE)

plot of chunk unnamed-chunk-2

## change the spread costs
phenogram(tree,x,spread.labels=TRUE,spread.cost=c(1,0))

plot of chunk unnamed-chunk-2

We can also plot a traitgram with the uncertainty about ancestral character states visualized using transparent probability density. This is implemented in the phytools function fancyTree. So, for instance:

## plot traitgram with 95% CI
fancyTree(tree,type="phenogram95",x=x,spread.cost=c(1,0))
## Computing density traitgram...

plot of chunk unnamed-chunk-3

Next, we can explore the continuous character mapping function in phytools called contMap. Here is a quick demo using the same data:

## plot contMap
obj<-contMap(tree,x)

plot of chunk unnamed-chunk-4

The function contMap returns an object of class "contMap" which we can then more easily replot using, for instance, different parameters:

## plot leftward
plot(obj,direction="leftwards")

plot of chunk unnamed-chunk-5

## plot fan style tree
plot(obj,type="fan",lwd=5) ## for example

plot of chunk unnamed-chunk-5

Here are some real data using anole.tre and svl.csv:

anole.tree<-read.tree("http://www.phytools.org/Ilhabela2015/data/anole.tre")
## or anole.tree<-read.tree("anole.tre")
anole.tree
## 
## Phylogenetic tree with 100 tips and 99 internal nodes.
## 
## Tip labels:
##  ahli, allogus, rubribarbus, imias, sagrei, bremeri, ...
## 
## Rooted; includes branch lengths.
svl<-read.csv("http://www.phytools.org/Ilhabela2015/data/svl.csv",
    header=TRUE,row.names=1)
## or svl<-read.csv("svl.csv",header=TRUE,row.names=1)
svl<-as.matrix(svl)[,1]
svl
##            ahli         alayoni         alfaroi        aliniger 
##        4.039125        3.815705        3.526655        4.036557 
##        allisoni         allogus   altitudinalis         alumina 
##        4.375390        4.040138        3.842994        3.588941 
##       alutaceus     angusticeps     argenteolus     argillaceus 
##        3.554891        3.788595        3.971307        3.757869 
##         armouri   bahorucoensis        baleatus        baracoae 
##        4.121684        3.827445        5.053056        5.042780 
##       barahonae        barbatus        barbouri        bartschi 
##        5.076958        5.003946        3.663932        4.280547 
##         bremeri        breslini    brevirostris        caudalis 
##        4.113371        4.051111        3.874155        3.911743 
##       centralis  chamaeleonides    chlorocyanus     christophei 
##        3.697941        5.042349        4.275448        3.884652 
##       clivicola     coelestinus        confusus           cooki 
##        3.758726        4.297965        3.938442        4.091535 
##    cristatellus    cupeyalensis         cuvieri    cyanopleurus 
##        4.189820        3.462014        4.875012        3.630161 
##         cybotes     darlingtoni       distichus dolichocephalus 
##        4.210982        4.302036        3.928796        3.908550 
##       equestris      etheridgei   eugenegrahami       evermanni 
##        5.113994        3.657991        4.128504        4.165605 
##         fowleri         garmani         grahami           guafe 
##        4.288780        4.769473        4.154274        3.877457 
##       guamuhaya         guazuma       gundlachi       haetianus 
##        5.036953        3.763884        4.188105        4.316542 
##      hendersoni      homolechis           imias    inexpectatus 
##        3.859835        4.032806        4.099687        3.537439 
##       insolitus        isolepis           jubar           krugi 
##        3.800471        3.657088        3.952605        3.886500 
##      lineatopus   longitibialis        loysiana          lucius 
##        4.128612        4.242103        3.701240        4.198915 
##    luteogularis      macilentus        marcanoi          marron 
##        5.101085        3.715765        4.079485        3.831810 
##         mestrei       monticola          noblei        occultus 
##        3.987147        3.770613        5.083473        3.663049 
##         olssoni        opalinus      ophiolepis        oporinus 
##        3.793899        3.838376        3.637962        3.845670 
##        paternus        placidus       poncensis        porcatus 
##        3.802961        3.773967        3.820378        4.258991 
##          porcus      pulchellus         pumilis quadriocellifer 
##        5.038034        3.799022        3.466860        3.901619 
##      reconditus        ricordii     rubribarbus          sagrei 
##        4.482607        5.013963        4.078469        4.067162 
##    semilineatus        sheplani         shrevei      singularis 
##        3.696631        3.682924        3.983003        4.057997 
##      smallwoodi         strahmi       stratulus     valencienni 
##        5.035096        4.274271        3.869881        4.321524 
##       vanidicus    vermiculatus        websteri       whitemani 
##        3.626206        4.802849        3.916546        4.097479
obj<-contMap(anole.tree,svl,fsize=c(0.6,1),outline=FALSE)

plot of chunk unnamed-chunk-6

We can also plot this using a circular or fan tree:

plot(obj,type="fan",outline=FALSE)

plot of chunk unnamed-chunk-7

Note that the intense aliasing is just a byproduct of plotting to .png format. To get a high quality image we can plot to .pdf or other formats. For instance:

pdf(file="anole.contMap.pdf",width=10,height=10)
plot(obj,type="fan",outline=FALSE)
dev.off()
## png 
##   2

The result can be viewed here.

Finally, a relatively simple new plotting method in phytools is the function plotTree.wBars. That function pretty much does what it sounds like it does:

plotTree.wBars(anole.tree,exp(svl),type="fan",scale=0.002)

plot of chunk unnamed-chunk-9

It is not too difficult to combine this with a contMap plot. For example:

obj<-contMap(anole.tree,exp(svl),plot=FALSE)
plotTree.wBars(obj$tree,exp(svl),method="plotSimmap",
    tip.labels=TRUE,fsize=0.7,colors=obj$cols,type="fan",scale=0.002)
add.color.bar(1.0,obj$cols,title="trait value",lims=obj$lims,prompt=FALSE,
    x=0.9*par()$usr[1],y=0.9*par()$usr[3])

plot of chunk unnamed-chunk-10

pdf(file="anole.plotTree.wBars.pdf",width=10,height=10)
obj<-contMap(anole.tree,exp(svl),plot=FALSE)
plotTree.wBars(obj$tree,exp(svl),method="plotSimmap",
    tip.labels=TRUE,fsize=0.7,colors=obj$cols,type="fan",scale=0.002)
add.color.bar(1.0,obj$cols,title="trait value",lims=obj$lims,prompt=FALSE,
    x=0.9*par()$usr[1],y=0.9*par()$usr[3])
dev.off()
## png 
##   2

(A non-aliased version here.)

We can also do two dimensional visualizations of phylogenies in morphospace. The is called a 'phylomorphospace'. E.g.:

X<-fastBM(tree,nsim=3) ## simulate 3 characters
phylomorphospace(tree,X[,c(1,2)],xlab="trait 1",ylab="trait 2")

plot of chunk unnamed-chunk-12

With phytools we can do static or animated version of the same. I'll just do a static version here, but animated is the default:

phylomorphospace3d(tree,X,method="static")

plot of chunk unnamed-chunk-13

Try the animated version on your own.

Finally, it's possible to combine the continuous character mapping and 2D phylomorphospaces using a type of phylogenetic scatterplot. Here's a demo:

fancyTree(tree,type="scattergram",X=X)
## Computing multidimensional phylogenetic scatterplot matrix...

plot of chunk unnamed-chunk-14

Discrete character methods

Stochastic character maps are easy to plot using phytools. For instance:

data(anoletree)
plotSimmap(anoletree,type="fan")
## no colors provided. using the following legend:
##        CG        GB        TC        TG        Tr        Tw 
##   "black"     "red"  "green3"    "blue"    "cyan" "magenta"
## setEnv=TRUE for this type is experimental. please be patient with bugs
## we can use any color scheme, but this is the default
cols<-setNames(palette()[1:length(unique(getStates(anoletree,
    "tips")))],
    sort(unique(getStates(anoletree,"tips"))))
add.simmap.legend(colors=cols,x=0.9*par()$usr[1],
    y=0.9*par()$usr[4],prompt=FALSE)

plot of chunk unnamed-chunk-15

This is just one stochastically mapped tree. Let's perform the method of stochastic mapping & plot the posterior probabilities at internal nodes.

x<-getStates(anoletree,"tips")
trees<-make.simmap(anoletree,x,model="ER",nsim=100)
## make.simmap is sampling character histories conditioned on the transition matrix
## Q =
##             CG          GB          TC          TG          Tr          Tw
## CG -0.11570723  0.02314145  0.02314145  0.02314145  0.02314145  0.02314145
## GB  0.02314145 -0.11570723  0.02314145  0.02314145  0.02314145  0.02314145
## TC  0.02314145  0.02314145 -0.11570723  0.02314145  0.02314145  0.02314145
## TG  0.02314145  0.02314145  0.02314145 -0.11570723  0.02314145  0.02314145
## Tr  0.02314145  0.02314145  0.02314145  0.02314145 -0.11570723  0.02314145
## Tw  0.02314145  0.02314145  0.02314145  0.02314145  0.02314145 -0.11570723
## (estimated using likelihood);
## and (mean) root node prior probabilities
## pi =
##        CG        GB        TC        TG        Tr        Tw 
## 0.1666667 0.1666667 0.1666667 0.1666667 0.1666667 0.1666667
## Done.
obj<-describe.simmap(trees,plot=FALSE)
obj
## 100 trees with a mapped discrete character with states:
##  CG, GB, TC, TG, Tr, Tw 
## 
## trees have 24.11 changes between states on average
## 
## changes are of the following types:
##      CG,GB CG,TC CG,TG CG,Tr CG,Tw GB,CG GB,TC GB,TG GB,Tr GB,Tw TC,CG
## x->y  0.24  0.26  0.22  0.13  0.32  0.58  0.97  1.33  0.35  1.58  1.17
##      TC,GB TC,TG TC,Tr TC,Tw TG,CG TG,GB TG,TC TG,Tr TG,Tw Tr,CG Tr,GB
## x->y  0.55  0.32  0.69     1  1.17  3.58  2.02     1  2.07  0.16  0.22
##      Tr,TC Tr,TG Tr,Tw Tw,CG Tw,GB Tw,TC Tw,TG Tw,Tr
## x->y  0.18  0.15   0.3  0.55  0.83  1.24   0.5  0.43
## 
## mean total time spent in each state is:
##              CG         GB         TC         TG          Tr         Tw
## raw  13.2553592 47.1436754 32.3207568 71.1973321 13.12972055 28.6201165
## prop  0.0644506  0.2292234  0.1571509  0.3461778  0.06383972  0.1391576
##        total
## raw  205.667
## prop   1.000
plot(obj,type="fan")
## setEnv=TRUE for this type is experimental. please be patient with bugs
add.simmap.legend(colors=cols,x=0.9*par()$usr[1],
    y=0.9*par()$usr[4],prompt=FALSE)

plot of chunk unnamed-chunk-16

We can also overlay the posterior probabilities on a random stochastic map in a similar way:

plotSimmap(trees[[1]],type="fan")
## no colors provided. using the following legend:
##        CG        GB        TC        TG        Tr        Tw 
##   "black"     "red"  "green3"    "blue"    "cyan" "magenta"
## setEnv=TRUE for this type is experimental. please be patient with bugs
nodelabels(pie=obj$ace,piecol=cols,cex=0.5)
add.simmap.legend(colors=cols,x=0.9*par()$usr[1],
    y=0.9*par()$usr[4],prompt=FALSE)

plot of chunk unnamed-chunk-17

For binary characters, we can also map the posterior density of stochastic maps onto the nodes & branches of a tree using the phytools function densityMap. Here's a demo of this using simulated data.

## first let's simulate data
Q<-matrix(c(-1,1,1,-1),2,2)
rownames(Q)<-colnames(Q)<-c(0,1)
tree<-sim.history(tree,Q)
## Done simulation(s).
x<-tree$states
x
##   A   B   C   D   E   F   G   H   I   J   K   L   M   N   O   P   Q   R 
## "1" "0" "0" "1" "1" "0" "0" "1" "0" "1" "0" "0" "0" "1" "1" "1" "1" "0" 
##   S   T   U   V   W   X   Y   Z 
## "0" "0" "0" "1" "1" "1" "0" "1"
## stochastic maps
trees<-make.simmap(tree,x,nsim=100)
## make.simmap is sampling character histories conditioned on the transition matrix
## Q =
##           0         1
## 0 -1.223814  1.223814
## 1  1.223814 -1.223814
## (estimated using likelihood);
## and (mean) root node prior probabilities
## pi =
##   0   1 
## 0.5 0.5
## Done.
## make densityMap
obj<-densityMap(trees,lwd=4,outline=TRUE)
## sorry - this might take a while; please be patient

plot of chunk unnamed-chunk-18

Combining discrete & continuous characters

We can, for instance, overlay a discrete character history (or stochastic map) onto a traitgram or phylomorphospace.

## simulate data with a high rate on some branches
x<-sim.rates(tree,setNames(c(1,10),c(0,1)))
## plot traitgram   
phenogram(tree,x,colors=setNames(c("blue","red"),
    c(0,1)),spread.labels=TRUE,spread.cost=c(1,0))

plot of chunk unnamed-chunk-19

## we can do the same with phylomorphospaces
X<-cbind(sim.rates(tree,setNames(c(1,10),c(0,1))),
    sim.rates(tree,setNames(c(1,10),c(0,1))))
phylomorphospace(tree,X,colors=setNames(c("blue","red"),c(0,1)))

plot of chunk unnamed-chunk-19

## it is even possible to overlay a posterior density from stochastic mapping
phylomorphospace(obj$tree,X,colors=obj$cols,
    lwd=3,xlab="x",ylab="y")
add.color.bar(4,obj$cols,title="PP(state=1)",
    prompt=FALSE,x=0.9*par()$usr[1],y=0.9*par()$usr[3])

plot of chunk unnamed-chunk-19

Other stuff

How about projecting a tree onto a geographic map? Here's an example using phytools function phylo.to.map:

# simulate tree & data
tree<-pbtree(n=26,scale=100)
tree$tip.label<-LETTERS[26:1]
lat<-fastBM(tree,sig2=10,bounds=c(-90,90))
long<-fastBM(tree,sig2=80,bounds=c(-180,180))
# now plot projection
xx<-phylo.to.map(tree,cbind(lat,long),plot=FALSE)
## objective: 190
## objective: 190
## objective: 190
## objective: 188
## objective: 188
## objective: 188
## objective: 188
## objective: 186
## objective: 184
## objective: 184
## objective: 184
## objective: 130
## objective: 130
## objective: 130
## objective: 128
## objective: 128
## objective: 128
## objective: 104
## objective: 98
## objective: 98
## objective: 94
## objective: 92
## objective: 90
## objective: 90
## objective: 88
## tree points to map coordinates
plot(xx,type="phylogram",asp=1.3,mar=c(0.1,0.5,3.1,0.1))

plot of chunk unnamed-chunk-20

## tree projected directly on map
plot(xx,type="direct",asp=1.3,mar=c(0.1,0.5,3.1,0.1))

plot of chunk unnamed-chunk-20

That's it.

Written by Liam J. Revell. Last updated July 5, 2015