Saturday, June 27, 2020

Plotting the states of a discrete character at the tips of the tree using & not using ape::tiplabels

A phytools user today contacted me about using the function to.matrix with ape::tiplabels to visualize the state of a discrete character at the tips of the tree.

I thought it could be useful to reiterate a demonstration of how this is done.

For this I will use the phylogeny & data on feeding mode for Centrarchidae, the sunfish.

library(phytools)
data(sunfish.tree)
data(sunfish.data)

sunfish.tree is actually a "simmap" object, but we just want a simple "phylo" tree here:

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.
sunfish.tree<-as.phylo(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, ...
## 
## Rooted; includes branch lengths.

I'll also pull out the character feeding.mode from my sunfish data:

fmode<-setNames(sunfish.data$feeding.mode,rownames(sunfish.data))

Now we're pretty much ready to go. The function to.matrix in phytools just converts a factor or character vector into a binary matrix. It works as follows:

fmode
##    Acantharchus_pomotis        Lepomis_gibbosus     Lepomis_microlophus 
##                    pisc                     non                     non 
##       Lepomis_punctatus        Lepomis_miniatus         Lepomis_auritus 
##                     non                     non                     non 
##      Lepomis_marginatus       Lepomis_megalotis         Lepomis_humilis 
##                     non                     non                     non 
##     Lepomis_macrochirus         Lepomis_gulosus     Lepomis_symmetricus 
##                     non                    pisc                     non 
##       Lepomis_cyanellus      Micropterus_coosae      Micropterus_notius 
##                    pisc                    pisc                    pisc 
##     Micropterus_treculi   Micropterus_salmoides  Micropterus_floridanus 
##                    pisc                    pisc                    pisc 
## Micropterus_punctulatus    Micropterus_dolomieu Centrarchus_macropterus 
##                    pisc                    pisc                     non 
##      Enneacantus_obesus       Pomoxis_annularis  Pomoxis_nigromaculatus 
##                     non                    pisc                    pisc 
##  Archolites_interruptus    Ambloplites_ariommus   Ambloplites_rupestris 
##                    pisc                    pisc                    pisc 
##   Ambloplites_cavifrons 
##                    pisc 
## Levels: non pisc
to.matrix(fmode,levels(fmode))
##                         non pisc
## Acantharchus_pomotis      0    1
## Lepomis_gibbosus          1    0
## Lepomis_microlophus       1    0
## Lepomis_punctatus         1    0
## Lepomis_miniatus          1    0
## Lepomis_auritus           1    0
## Lepomis_marginatus        1    0
## Lepomis_megalotis         1    0
## Lepomis_humilis           1    0
## Lepomis_macrochirus       1    0
## Lepomis_gulosus           0    1
## Lepomis_symmetricus       1    0
## Lepomis_cyanellus         0    1
## Micropterus_coosae        0    1
## Micropterus_notius        0    1
## Micropterus_treculi       0    1
## Micropterus_salmoides     0    1
## Micropterus_floridanus    0    1
## Micropterus_punctulatus   0    1
## Micropterus_dolomieu      0    1
## Centrarchus_macropterus   1    0
## Enneacantus_obesus        1    0
## Pomoxis_annularis         0    1
## Pomoxis_nigromaculatus    0    1
## Archolites_interruptus    0    1
## Ambloplites_ariommus      0    1
## Ambloplites_rupestris     0    1
## Ambloplites_cavifrons     0    1

That's OK. Now let's use it with tiplabels to plot piecharts for the tips of the tree in which we color by the tip state of each species. Note that I'm going to want to order the rows of my matrix to match the tip order of my plotted tree:

plotTree(sunfish.tree,ftype="i",offset=0.6,fsize=0.9)
FMODE<-to.matrix(fmode,levels(fmode))
FMODE<-FMODE[sunfish.tree$tip.label,]
tiplabels(pie=FMODE,piecol=palette()[c(4,2)],cex=0.6)
legend("topleft",levels(fmode),pch=21,pt.bg=palette()[c(4,2)],
    pt.cex=2.2)

plot of chunk unnamed-chunk-5

Of course, we didn't need to do it that way. We could have also just plotted points at the tips of the tree as follows:

plotTree(sunfish.tree,ftype="i",offset=0.6,fsize=0.9)
obj<-get("last_plot.phylo",envir=.PlotPhyloEnv)
cols<-setNames(palette()[c(4,2)],levels(fmode))
points(obj$xx[1:Ntip(sunfish.tree)],
    obj$yy[1:Ntip(sunfish.tree)],pch=21,cex=2.2,
    bg=cols[fmode[sunfish.tree$tip.label]])
legend("topleft",levels(fmode),pch=21,pt.bg=palette()[c(4,2)],
    pt.cex=2.2)

plot of chunk unnamed-chunk-6

Neat!

How the heck did that work?

Well, first of all get("last_plot.phylo",envir=.PlotPhyloEnv) gets a special environmental variable that is created by plotTree (and many other phylogenetic plotting functions - it is based on ape::plot.phylo) with lots of different attributes of the most recently plotted tree - including the coordinates of all the nodes in the tree in get("last_plot.phylo",envir=.PlotPhyloEnv)$xx and get("last_plot.phylo",envir=.PlotPhyloEnv)$yy.

Next, when we create a vector of colors in which the names are the different states for our character (as we did using setNames(palette()[c(4,2)],levels(fmode))), then the simple call cols[fmode[sunfish.tree$tip.label]] gives us a vector with the colors that correspond to the states at all the tips of the tree - in the order of the tips.

Not bad.