---
title: "Node support values using treestructure (v2)"
author: "Vinicius Franceschi, Fabricia F. Nascimento, and Erik Volz"
date: "`r Sys.Date()`"
output:
  rmarkdown::html_vignette:
  #rmarkdown::html_vignette
  #bookdown::pdf_book:
    toc: TRUE
pkgdown:
  as_is: true
fontsize: 12pt
vignette: >
  %\VignetteIndexEntry{Node support values using treestructure}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  fig.width = 7,
  fig.height = 11,
  warning = FALSE,
  message = FALSE
)
```

# Introduction

This tutorial uses the public data available for Ebola [here](https://github.com/ebov/space-time) to demonstrate the use of node support
values (e.g. bootstrap and posterior probability) to avoid designating population
structure in badly supported clades.

We will use their [time-scaled phylogenetic tree](https://github.com/ebov/space-time/blob/master/Data/Makona_1610_cds_ig.GLM.MCC.tree) estimated with [BEAST](https://beast.community).

Throughout we use the default analysis, in which the split threshold is calibrated to
a target false discovery rate (`fdr = 0.2`), with `minCladeSize = 10`.


First, we need to load the R package we will use in this tutorial.


```{r message=FALSE}
library(treeio)
library(ggtree)
library(treestructure)
```

The tree-construction and `trestruct` steps below are shown for reference but are not run
when this vignette is built (to avoid network access and keep the build fast); each
analysis loads a precomputed result instead.


Now, we will load the time-scaled phylogenetic tree with posterior probability
support values:

```{r eval=FALSE}
#get the dated tree by first downloading it from the URL below
tree_url <- "https://raw.githubusercontent.com/ebov/space-time/master/Data/Makona_1610_cds_ig.GLM.MCC.tree"
tmp_file <- tempfile(fileext = ".tree")

#Download BEAST tree to a tmp file
download.file(tree_url, tmp_file, mode = "wb")

#read the downloaded tree
beast_tree <- read.beast(tmp_file)
```


Then we need to convert the tree to a `phylo` object and add the posterior
probability information from the Bayesian tree to the `phylo` object tree.

If we use the `ape:read.nexus` to read the tree that was estimated with BEAST,
we won't get the posterior probability associated to the clades.

We now convert the dated tree to `phylo` object.

```{r eval=FALSE}
dated_tre <- as.phylo(beast_tree)
```

Now we add the posterior probability from the BEAST tree to the dated_tree
object.

```{r eval=FALSE}
# Get number of tips
n_tips <- length(dated_tre$tip.label)

# Get BEAST tree as tibble (it will include node numbers and posterior probabilities)
tree_data <- as_tibble(beast_tree)

#get posterior probability
posterior <- as.numeric(tree_data$posterior[(n_tips + 1):nrow(tree_data)])

#add the posterior probability information to the `phylo` object
dated_tre$node.label <- posterior
```



## Assign clusters without using node support

Firstly, we will assign clusters without using node support values. Note that
the parameter `nodeSupportValues` is set to FALSE.

```{r eval=FALSE}
trestruct_res_nobt <- trestruct(dated_tre,
                                fdr = 0.2,
                                minCladeSize = 10,
                                nodeSupportValues = FALSE)
```

Here, we load the precomputed result.

```{r}
trestruct_res_nobt <- readRDS( system.file('trestruct_res_nobt.rds',
                                           package='treestructure') )

plot(trestruct_res_nobt, use_ggtree = T) + ggtree::geom_tippoint()
```

The `treestructure` analysis resulted in `r nlevels(trestruct_res_nobt$clustering)` clusters.

Although `treestructure` uses only the shape of the tree, the clusters largely
correspond to country. The tip labels encode the sampling country (Guinea, Sierra
Leone or Liberia):

```{r}
country <- sapply(strsplit(trestruct_res_nobt$tree$tip.label, "\\|"), `[`, 4)
country[!country %in% c("SLE", "LBR", "GIN")] <- "other"
table(cluster = trestruct_res_nobt$clustering, country = country)
```

Most clusters are dominated by a single country, recovering the introductions and
cross-border spread of the epidemic from the coalescent pattern alone.


## Sensitivity to the minimum clade size

`minCladeSize` sets the smallest cluster that can be designated. Repeating the same
analysis with `minCladeSize = 20`:

```{r eval = FALSE}
trestruct_res_mcs20 <- trestruct(dated_tre, fdr = 0.2, minCladeSize = 20,
                                 nodeSupportValues = FALSE)
```

```{r}
trestruct_res_mcs20 <- readRDS( system.file('trestruct_res_nobt_mcs20.rds',
                                            package='treestructure') )

plot(trestruct_res_mcs20, use_ggtree = T) + ggtree::geom_tippoint()
```

This finds `r nlevels(trestruct_res_mcs20$clustering)` clusters, fewer than the
`r nlevels(trestruct_res_nobt$clustering)` found with `minCladeSize = 10`: raising the
minimum size drops the smallest clusters and gives a coarser partition. `minCladeSize`
also controls how many candidate clades are tested at each step, which feeds into the
multiple-testing threshold, so the two partitions do not simply nest — each resolves
somewhat different structure. It is worth trying more than one.


## Assign clusters using branch support

Now, we will assign clusters using the information on branch support.
As we are analyzing a dated tree estimated with BEAST, the branch support is
the posterior probability.


We designate clusters that have at least 0.95 posterior probability. This
is achieved by setting to 95 the parameter _nodeSupportThreshold_ in the **trestruct**
function.

Note that now the parameter `nodeSupportValues` is set to TRUE, which tells
the algorithm that the node support values is provided with the `phylo` object
as `node.label`.

You can also provide the `nodeSupportValues` as a vector with length equal to
the number of internal nodes in the tree.

```{r eval=FALSE}
trestruct_res <- trestruct(dated_tre,
                           fdr = 0.2,
                           minCladeSize = 10,
                           nodeSupportValues = TRUE,
                           nodeSupportThreshold = 95)
```

Here, we load the precomputed result.

```{r}
trestruct_res <- readRDS( system.file('trestruct_res.rds',
                                      package='treestructure') )

plot(trestruct_res, use_ggtree = T) + ggtree::geom_tippoint()
```


Now we have only `r nlevels(trestruct_res$clustering)` well-supported clusters with differences in coalescent patterns.

Note that this might change if you use a higher or lower value for the
*nodeSupportThreshold* in the **trestruct** function.


## Using the CH-index

As an alternative to a target false discovery rate, the CH-index provides an
automatic way to choose the significance `level`: `trestruct` is run over a range of
levels (`level = NULL` with lower and upper bounds), and the level maximizing the
[Calinski–Harabasz index](https://en.wikipedia.org/wiki/Calinski–Harabasz_index) —
the ratio of between- to within-cluster variance in node heights — is selected. We
show it here *without* node support, because under strong support filtering the level
barely changes the result (the CH index is flat).

```{r eval = FALSE}
trestruct_chindex <- trestruct(dated_tre,
                               minCladeSize = 10,
                               nodeSupportValues = FALSE,
                               level = NULL,
                               levellb = 0.0001,
                               levelub = 0.01)
```

The CH optimisation runs `trestruct` at several levels, so it is slower; here we
load the precomputed result.

```{r}
trestruct_chindex <- readRDS( system.file('trestruct_chindex.rds',
                                      package='treestructure') )

plot(trestruct_chindex, use_ggtree = T) + ggtree::geom_tippoint()
```

The CH-index selected a significance level of
`r signif(trestruct_chindex$chdf$level[trestruct_chindex$chdf$optimal == "***"], 3)`,
giving `r nlevels(trestruct_chindex$clustering)` clusters.
