--- title: "Analysing landscapes of phylogenetic trees" author: "[Martin R. Smith](https://smithlabdurham.github.io/)" output: rmarkdown::html_vignette: fig_width: 6 fig_height: 4 bibliography: ../inst/REFERENCES.bib csl: https://raw.githubusercontent.com/citation-style-language/styles/master/apa-old-doi-prefix.csl vignette: > %\VignetteIndexEntry{Analysing landscapes of phylogenetic trees} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- Landscapes of trees are [mappings of tree space](treespace.html) that are contoured according to some optimality criterion -- often, but not necessarily, a tree's score under a phylogenetic reconstruction technique [@Bastert2002]. Detecting "islands" or "terraces" of trees can illuminate the nature of the space of optimal trees and thus inform tree search strategy [@Maddison1991; @Sanderson2011]. For simplicity (and to avoid scoring trees against a dataset), this example uses a tree's balance (measured using the total cophenetic index) as its score [@Mir2013]. We assume that mappings have already been shown to be [adequate]( treespace.html) [@SmithSpace]. A landscape is most simply visualized by colouring each tree according to its score: ```{r col-trees-by-score} # Load required libraries library("TreeTools", quietly = TRUE) library("TreeDist") # Generate a set of trees trees <- as.phylo(as.TreeNumber(BalancedTree(16)) + 0:100 - 15, 16) # Create a 2D mapping distances <- ClusteringInfoDist(trees) mapping <- cmdscale(distances, 2) # Score trees according to their balance scores <- TotalCopheneticIndex(trees) # Normalize scores scoreMax <- TCIContext(trees[[1]])[["maximum"]] scoreMin <- TCIContext(trees[[1]])[["minimum"]] scores <- scores - scoreMin scores <- scores / (scoreMax - scoreMin) # Generate colour palette col <- colorRamp(c("orange", "blue"))(scores) rgbCol <- rgb(col, maxColorValue = 255) # Plot trees, coloured by their score plot( mapping, asp = 1, # Preserve aspect ratio - do not distort distances ann = FALSE, axes = FALSE, # Don't label axes: dimensions are meaningless col = rgbCol, # Colour trees by score pch = 16 # Plotting character: Filled circle ) # Add a legend PlotTools::SpectrumLegend( "left", title = "Tree balance", palette = rgb(colorRamp(c("orange", "blue"))(0:100 / 100) / 255), legend = floor(seq(scoreMax, scoreMin, length.out = 6)), bty = "n" ) ``` A more sophisticated output can be produced using contours, interpolating between adjacent trees. This example uses a simple inverse distance weighting function for interpolation; more sophisticated techniques such as [kriging](https://desktop.arcgis.com/en/arcmap/10.3/tools/3d-analyst-toolbox/how-kriging-works.htm) or (in continuous tree spaces) the use of geodesics [@Khodaei2022] can produce even better results. ```{r contoured-plot} # Use an inverse distance weighting to interpolate between measured points Predict <- function (x, y) { Distance <- function (a, b) { apply(a, 2, function (pt) sqrt(colSums((pt - b) ^ 2))) } predXY <- rbind(x, y) dists <- Distance(t(mapping), predXY) invDist <- 1 / dists weightings <- invDist / rowSums(invDist) # Return: colSums(scores * t(weightings)) } # Generate grid for contour plot resolution <- 32 xLim <- range(mapping[, 1]) * 1.1 yLim <- range(mapping[, 2]) * 1.11 x <- seq(xLim[1], xLim[2], length.out = resolution) y <- seq(yLim[1], yLim[2], length.out = resolution) z <- outer(x, y, Predict) # Predicted values for each grid square # Plot filled.contour( x, y, z, asp = 1, # Preserve aspect ratio - do not distort distances ann = FALSE, axes = FALSE, # Don't label axes: dimensions are meaningless plot.axes = {points(mapping, xpd = NA)} # Use filled.contour coordinates ) ``` A variety of R add-on packages facilitate three-dimensional plots. ```{r plot-3d, message = FALSE} if (requireNamespace("plotly", quietly = TRUE)) { library("plotly", quietly = TRUE) fig <- plot_ly(x = x, y = y, z = z) fig <- fig %>% add_surface() fig } else { print("Run `install.packages('plotly')` to view this output") } ``` (Use the mouse to reorient) # References