In some recent updates I have added some functionality to the methods
associated with the phytools object class "cophylo"
, for
co-phylogenetic plotting.
I'm considering writing up a short paper describing the methods, and some of the optimization algorithms that I have designed to maximize tip alignment by different criteria, so I thought it might be useful to find an empirical 'exemplar' dataset.
Today I found the following article by Lopez-Vaamonde et al. (2001) from MPE:
Lopez-Vaamonde, C., J. Y. Rasplus, G. D. Weiblen, & J. M. Cook. 2001. Molecular phylogenies of fig wasps: Partial cocladogenesis of pollinators and parasites. Molecular Phylogenetics and Evolution 21: 55-71.
Rather than try to find the trees online, I just transcribed them into Newick cladograms by eye. Here is the result:
library(phytools)
Pleistodontes<-read.tree(text="(((1,(2,3)100)50,(((((((4,(5,(6,7)100)69)100,(8,(9,10))81)61,11),(12,13)100),(14,15)100),(16,17)96),18)),19);")
Pleistodontes$tip.label<-c("P._froggatti",
"P._blandus",
"P._near_blandus",
"P._proximus",
"P._athysanus",
"P._cuneatus",
"P._astrabocheilus",
"P._macrocainus",
"P._rigisamos",
"P._imperialis",
"P._nigriventris",
"P._xanthocephalus",
"P._greenwoodi",
"P._rieki",
"P._plebejus",
"P._schizodontes",
"P._nitens",
"P._spec._nov._1",
"P._regalis")
## rotate all nodes to match orientation in article
Pleistodontes<-rotateNodes(Pleistodontes,"all")
Sycoscapter<-read.tree(text="(1,((((2,3)100,((4,5),(6,7)78)100),((8,9)50,10)99),(((11,12),13)91,(14,15)77)));")
Sycoscapter$tip.label<-c("S._australis_{macrophylla}",
"S._3_{playpoda}",
"S._5_{cerasicarpa}",
"S._6_{lilliputiana}",
"S._7_{subpuberula}",
"S._1_{brachypoda}",
"S._15_{rubiginosa}",
"S._9_{triradiata}",
"S._10_{crassipes}",
"S._8_{pleurocarpa}",
"S._11_{glandifera}",
"S._12_{xylosycia}",
"S._14_{hesperidiiformis}",
"S._2_{aff._obliqua}",
"S._4_{obliqua}")
Sycoscapter<-rotateNodes(Sycoscapter,"all")
assoc<-cbind(
Pleistodontes$tip.label[c(7,8,5,6,18,1,3,4,10,15,13,16,12,14,19)],
Sycoscapter$tip.label)
assoc
## [,1] [,2]
## [1,] "P._greenwoodi" "S._4_{obliqua}"
## [2,] "P._xanthocephalus" "S._2_{aff._obliqua}"
## [3,] "P._plebejus" "S._14_{hesperidiiformis}"
## [4,] "P._rieki" "S._12_{xylosycia}"
## [5,] "P._blandus" "S._11_{glandifera}"
## [6,] "P._regalis" "S._8_{pleurocarpa}"
## [7,] "P._nitens" "S._10_{crassipes}"
## [8,] "P._schizodontes" "S._9_{triradiata}"
## [9,] "P._imperialis" "S._15_{rubiginosa}"
## [10,] "P._athysanus" "S._1_{brachypoda}"
## [11,] "P._astrabocheilus" "S._7_{subpuberula}"
## [12,] "P._proximus" "S._6_{lilliputiana}"
## [13,] "P._macrocainus" "S._5_{cerasicarpa}"
## [14,] "P._cuneatus" "S._3_{playpoda}"
## [15,] "P._froggatti" "S._australis_{macrophylla}"
Now let's create our "cophylo"
object without rotation:
obj<-cophylo(Pleistodontes,Sycoscapter,assoc=assoc,
rotate=FALSE)
obj
plot(obj,link.type="curved",link.lwd=3,link.lty="solid",
link.col=make.transparent("blue",0.25),fsize=0.8)
This matches the published co-phylogenetic plot as follows:
Of course, we can see if we can 'improve' on the tip matching by any criterion.
cophylo
allows us to optimize the rotation based on basically
any arbitrary criterion.
The default is to minimize the sum of squares difference between the vertical positions of the tips in both trees; however we can also minimize the sum of the absolute values. For instance:
obj<-cophylo(Pleistodontes,Sycoscapter,assoc=assoc,
print=TRUE)
## Rotating nodes to optimize matching...
## objective: 359.577777777778
## objective: 359.577777777778
## objective: 359.577777777778
## objective: 359.577777777778
## objective: 359.577777777778
## objective: 359.577777777778
## objective: 359.577777777778
## objective: 359.577777777778
## objective: 359.577777777778
## objective: 359.577777777778
## objective: 359.577777777778
## objective: 359.577777777778
## objective: 357.777777777778
## objective: 357.777777777778
## objective: 345.111111111111
## objective: 345.111111111111
## objective: 345.111111111111
## objective: 322.777777777778
## objective: 201.17728531856
## objective: 201.17728531856
## objective: 201.17728531856
## objective: 201.17728531856
## objective: 201.17728531856
## objective: 201.17728531856
## objective: 201.17728531856
## objective: 201.17728531856
## objective: 201.17728531856
## objective: 201.17728531856
## objective: 201.17728531856
## objective: 201.17728531856
## objective: 201.17728531856
## objective: 201.17728531856
## objective: 322.777777777778
## objective: 322.777777777778
## objective: 322.777777777778
## objective: 322.777777777778
## objective: 322.777777777778
## objective: 322.777777777778
## objective: 322.777777777778
## objective: 322.777777777778
## objective: 322.777777777778
## objective: 322.777777777778
## objective: 322.777777777778
## objective: 322.777777777778
## objective: 322.777777777778
## objective: 322.777777777778
## objective: 322.777777777778
## objective: 322.777777777778
## objective: 322.777777777778
## objective: 322.777777777778
## objective: 201.17728531856
## objective: 201.17728531856
## objective: 201.17728531856
## objective: 201.17728531856
## objective: 201.17728531856
## objective: 201.17728531856
## objective: 201.17728531856
## objective: 201.17728531856
## objective: 201.17728531856
## objective: 201.17728531856
## objective: 201.17728531856
## objective: 201.17728531856
## objective: 201.17728531856
## objective: 201.17728531856
## Done.
plot(obj,link.type="curved",link.lwd=3,link.lty="solid",
link.col=make.transparent("blue",0.25),fsize=0.8)
## absolute values
obj<-cophylo(Pleistodontes,Sycoscapter,assoc=assoc,
fn=function(x) abs(x),print=TRUE)
## Rotating nodes to optimize matching...
## objective: 57.2666666666667
## objective: 57.2666666666667
## objective: 57.2666666666667
## objective: 57.2666666666667
## objective: 57.2666666666667
## objective: 57.2666666666667
## objective: 57.2666666666667
## objective: 57.2666666666667
## objective: 57.2666666666667
## objective: 57.2666666666667
## objective: 57.2666666666667
## objective: 57.2666666666667
## objective: 56.2666666666667
## objective: 56.2666666666667
## objective: 52.4
## objective: 52.4
## objective: 52.4
## objective: 51.4
## objective: 40.5789473684211
## objective: 40.5789473684211
## objective: 40.5789473684211
## objective: 40.5789473684211
## objective: 40.5789473684211
## objective: 40.5789473684211
## objective: 40.5789473684211
## objective: 40.5789473684211
## objective: 40.5789473684211
## objective: 40.5789473684211
## objective: 40.5789473684211
## objective: 40.5789473684211
## objective: 40.5789473684211
## objective: 40.5789473684211
## objective: 51.4
## objective: 51.4
## objective: 51.4
## objective: 51.4
## objective: 51.4
## objective: 51.4
## objective: 51.4
## objective: 51.4
## objective: 51.4
## objective: 51.4
## objective: 51.4
## objective: 51.4
## objective: 51.4
## objective: 51.4
## objective: 51.4
## objective: 51.4
## objective: 51.4
## objective: 51.4
## objective: 40.5789473684211
## objective: 40.5789473684211
## objective: 40.5789473684211
## objective: 40.5789473684211
## objective: 40.5789473684211
## objective: 40.5789473684211
## objective: 40.5789473684211
## objective: 40.5789473684211
## objective: 40.5789473684211
## objective: 40.5789473684211
## objective: 40.5789473684211
## objective: 40.5789473684211
## objective: 40.5789473684211
## objective: 40.5789473684211
## Done.
plot(obj,link.type="curved",link.lwd=3,link.lty="solid",
link.col=make.transparent("blue",0.25),fsize=0.8)
It is even possible to, for instance, choose a function that does not make sense to optimize matching - for instance the inverse of the sum of squares:
obj<-cophylo(Pleistodontes,Sycoscapter,assoc=assoc,
fn=function(x) 1/x^2,print=TRUE)
## Rotating nodes to optimize matching...
## objective: 277.233847021676
## objective: 4.77348935389099
## objective: 4.77348935389099
## objective: 4.75539116602117
## objective: 4.75539116602117
## objective: 4.75539116602117
## objective: 4.75539116602117
## objective: 3.7739829235819
## objective: 3.7739829235819
## objective: 3.7739829235819
## objective: 3.7739829235819
## objective: 3.7739829235819
## objective: 3.7739829235819
## objective: 3.7739829235819
## objective: 3.7739829235819
## objective: 3.7739829235819
## objective: 3.7739829235819
## objective: 3.7739829235819
## objective: 6.05514593516918
## objective: 2.75709265189575
## objective: 1.56956478408719
## objective: 1.55965836490761
## objective: 1.55965836490761
## objective: 1.55965836490761
## objective: 1.55965836490761
## objective: 1.55965836490761
## objective: 1.55965836490761
## objective: 1.55965836490761
## objective: 0.506025805166682
## objective: 0.506025805166682
## objective: 0.506025805166682
## objective: 0.506025805166682
## objective: 0.307384379625682
## objective: 0.307384379625682
## objective: 0.307379556534829
## objective: 0.307379556534829
## objective: 0.307379556534829
## objective: 0.307379556534829
## objective: 0.307379556534829
## objective: 0.307379556534829
## objective: 0.307379556534829
## objective: 0.307379556534829
## objective: 0.307379556534829
## objective: 0.307379556534829
## objective: 0.306253296306206
## objective: 0.303195941167207
## objective: 0.303195941167207
## objective: 0.302741287258059
## objective: 0.301049328645266
## objective: 0.301049328645266
## objective: 0.483016922848627
## objective: 0.483016922848627
## objective: 0.483016922848627
## objective: 0.483016922848627
## objective: 0.483016922848627
## objective: 0.483016922848627
## objective: 0.483016922848627
## objective: 0.483016922848627
## objective: 0.483016922848627
## objective: 0.483016922848627
## objective: 0.483016922848627
## objective: 0.483016922848627
## objective: 0.483016922848627
## objective: 0.483016922848627
## objective: 0.301049328645266
## objective: 0.301049328645266
## objective: 0.301049328645266
## objective: 0.301049328645266
## objective: 0.301049328645266
## objective: 0.301049328645266
## objective: 0.301049328645266
## objective: 0.301049328645266
## objective: 0.301049328645266
## objective: 0.301049328645266
## objective: 0.301049328645266
## objective: 0.301049328645266
## objective: 0.30081492049661
## objective: 0.30081492049661
## objective: 0.30081492049661
## objective: 0.300433102030201
## objective: 0.300433102030201
## objective: 0.300433102030201
## objective: 0.482028221479567
## objective: 0.482028221479567
## objective: 0.482028221479567
## objective: 0.482028221479567
## objective: 0.482028221479567
## objective: 0.482028221479567
## objective: 0.482028221479567
## objective: 0.482028221479567
## objective: 0.482028221479567
## objective: 0.482028221479567
## objective: 0.482028221479567
## objective: 0.482028221479567
## objective: 0.482028221479567
## objective: 0.482028221479567
## objective: 0.300433102030201
## objective: 0.300433102030201
## objective: 0.300433102030201
## objective: 0.300433102030201
## objective: 0.300433102030201
## objective: 0.300433102030201
## objective: 0.300433102030201
## objective: 0.300433102030201
## objective: 0.300433102030201
## objective: 0.300433102030201
## objective: 0.300433102030201
## objective: 0.300433102030201
## objective: 0.300433102030201
## objective: 0.300433102030201
## objective: 0.300433102030201
## objective: 0.300433102030201
## objective: 0.300433102030201
## objective: 0.300433102030201
## objective: 0.482028221479567
## objective: 0.482028221479567
## objective: 0.482028221479567
## objective: 0.482028221479567
## objective: 0.482028221479567
## objective: 0.482028221479567
## objective: 0.482028221479567
## objective: 0.482028221479567
## objective: 0.482028221479567
## objective: 0.482028221479567
## objective: 0.482028221479567
## objective: 0.482028221479567
## objective: 0.482028221479567
## objective: 0.482028221479567
## Done.
plot(obj,link.type="curved",link.lwd=3,link.lty="solid",
link.col=make.transparent("blue",0.25),fsize=0.8)
This will have the tendency to minimize matching - which, of course, we don't want.
Finally, we can obviously add annotation - such as the bootstrap values at nodes from the original paper. (Though to get the look we want can be tricky.) For instance:
obj<-cophylo(Pleistodontes,Sycoscapter,assoc=assoc,
rotate=FALSE)
plot(obj,link.type="curved",link.lwd=3,link.lty="solid",
link.col=make.transparent("blue",0.25),fsize=0.8)
nodes<-1:obj$trees[[1]]$Nnode+Ntip(obj$trees[[1]])
nodelabels.cophylo(node=c(30,33),
text=obj$trees[[1]]$node.label[c(30,33)-Ntip(obj$trees[[1]])],
adj=c(1.1,-0.2),
frame="none",cex=0.7,which="left")
nodelabels.cophylo(node=35,
text=obj$trees[[1]]$node.label[35-Ntip(obj$trees[[1]])],
adj=c(-0.1,-0.1),
frame="none",cex=0.7,which="left")
nodelabels.cophylo(node=setdiff(nodes,c(30,33,35)),
text=obj$trees[[1]]$node.label[
setdiff(nodes,c(30,33,35))-Ntip(obj$trees[[1]])],
adj=c(1.1,1.1),
frame="none",cex=0.7,which="left")
nodes<-1:obj$trees[[2]]$Nnode+Ntip(obj$trees[[2]])
nodelabels.cophylo(node=c(19,20,24),
text=obj$trees[[2]]$node.label[c(19,20,24)-Ntip(obj$trees[[2]])],
adj=c(-0.2,-0.4),
frame="none",cex=0.7,which="right")
nodelabels.cophylo(node=setdiff(nodes,c(19,20,24)),
text=obj$trees[[2]]$node.label[setdiff(nodes,c(19,20,24))-
Ntip(obj$trees[[2]])],
adj=c(-0.2,1.1),
frame="none",cex=0.7,which="right")
That's it.
I think having a citation for us to use for your cophylo method would be good; at the moment I've been just citing phytools in some submitted manuscripts.
ReplyDeleteHave you given any time toward better optimization for rotating trees with polytomies? I've considered doing this myself, since I always seem to be applying cophylo to trees with big polytomies...
Dave. It's funny that you say that. I just started working on a short ms. - probably to submit to MEE.
DeleteWith regards to your second question - yes in fact! See here: http://blog.phytools.org/2016/08/co-phylogenetic-plotting-for-trees-with.html.
Thanks for the comments Dave. - Liam
1) Liam, yeah, you mentioned that you were working on such a manuscript in this post. I was agreeing that it was a good idea.
Delete2) Ah, I missed this while I was off where I had no internet for a week. Cool! I'll have to try the multifurcation rotating out... when some manuscripts return from review.
Cheers,
-Dave
Dear Liam,
ReplyDeleteIs there a way to display boostrap values to a cophylogenetic tree, in panel A and B, capped at a certain threshold (say 80% bootstrap), and doing this without invoking separate "nodelabels.cophylo(node=c(19,20,24), ..." commands?
Thanks in advance for the support.
Bw,
David
Thank you very much Liam, really useful!
ReplyDeleteQuick question, is there a way to save the cophylo in the environment as a whole? (with the node labels and coloured lines etc) or is there a way to transform it to phylo so we can add more data to the cophylo file?
Thank you very much in advance!!