| ihct | R Documentation |
This function builds a hierarchical tree of sites from a matrix of pairwise dissimilarities, using the Iterative Hierarchical Consensus Tree (IHCT) algorithm. Unlike an ordinary hierarchical clustering, the tree does not depend on the order in which the sites happen to be stored in the matrix.
Most users should use hclu_hierarclust() and not this function.
ihct() is an algorithm used in hclu_hierarclust(), exported for
users who want the algorithm itself for their purposes.
ihct(
dist_mat,
method = "average",
n_runs = 100,
top_n_trees = 2,
variation_drop = 0.2,
sites_drop = 10,
height_rule = c("least_squares", "max_child"),
tie_block_resolution = TRUE,
n_workers = 1,
size_parallel = 200,
verbose = TRUE
)
dist_mat |
A square |
method |
The name of the linkage method, as in
hclust. It is used both to build the randomized trees
and to give every division of the final tree its height. Should be one of
|
n_runs |
The number of randomized trees built to decide one division
( |
top_n_trees |
An |
variation_drop |
A |
sites_drop |
A |
height_rule |
A |
tie_block_resolution |
A |
n_workers |
An |
size_parallel |
An |
verbose |
A |
A dissimilarity matrix computed from species composition contains a great many identical values, because many pairs of sites share exactly the same number of species. Hierarchical clustering has to break those ties somehow, and hclust breaks them by taking whichever pair comes first in the matrix. Shuffle the rows of the matrix and you get a different tree out of the very same data (Dapporto et al., 2013). IHCT removes that arbitrariness by generating a true consensus tree from many randomizations.
IHCT does not join sites into ever larger groups, the way ordinary hierarchical clustering does. It starts with a single group containing every site, cuts that group in two, then cuts each of the two in two, and carries on until every group holds a single site. Each of those cuts is called a division, and the whole algorithm is about deciding one division well. Divisions are always strictly binary (i.e., one branch is always cut into two branches).
Take a group of sites that has to be divided. Four things happen to it:
Its sites are put in a random order and a tree is built on them, with
hclust and the linkage of method. This is repeated
n_runs times, giving n_runs trees that differ only in how the ties were
broken.
Each of those trees is scored by its cophenetic correlation
coefficient, that is,
by how closely the distances read off the tree reproduce the dissimilarities
the tree was built from. The top_n_trees best-scoring trees are kept and
the rest are discarded.
From the kept trees, we take the top division to decide how to split
the group of sites into two. With
top_n_trees = 1 we take the top division of the best tree. With more,
we take a consensus division based on how often sites are on the same side
across the kept trees. The rule we used is that the consensus is based on
the majority decision, sites are grouped together if theyr are together
in > 50% of trees.
The division is given a height, computed from the dissimilarities
between the two groups it separates in the way method prescribes: their
mean for "average" (UPGMA), their smallest value for "single", their
largest for "complete", and so on.
For each of the two groups that come out of the division, the algorithm run them through the same four steps, and so on for their own halves, until we reach single sites.
This why the algorithm is called "iterative": it randomizes trees again at every division from top to bottom. It is why this method provides better performance compareds to other approaches: a tie broken one way at the top of the tree does not force the same choice at the divisions underneath it.
Since the randomization uses R's random number generator, call
set.seed() before ihct() if you want the same tree twice.
Step 1 above is where nearly all of the computing time goes: n_runs trees
are built at every single division. However, the trees usually peel a handful of
sites off the rest at each division rather than splitting the data in half.
This means randomizing everything at every division can be inefficient, resulting
in large computation times, making the function too long to use on large datasets.
To make the function usable on large datasets, we provide two optimization parameters. These parameters make the algorithm reuse previously randomized trees at new division, unless a threshold of change is reached:
variation_drop is based on the amount of variation from the
dissimilarity matrix.
It triggers a new tree randomization only when the amount of variation
in the group being divided has reached a threshold since last randomization
(default: 20% drop in variation). In other words, new randomizations happen
only when
tree divisions reach a certain threshold of variation since the last
randomization. For example, when a tree peels off only 1 site at a time,
this argument makes sure no new randomization trigger unless variability
reaches the desired threshold.
sites_dropis based on how many sites the group has lost.
It triggers new randomizations only when a certain number of sites have been
excluded since the last randomization.
These two parameters with their defaults (variation_drop = 0.2,
sites_drop = 10) result in marginal
changes in algorithm performance (loss in CCC <0.001) and make the tree
1.5 faster to build. In our tests, the larger the datasets, the higher
the savings with these two optimization parameters. Note, however,
that reusing trees only works with UPGMA currently, so it only applies to
method = "average".
The two arguments work as a pair, and each of them can be set so that the other no longer has any effect. A new randomization is made as soon as either of them asks for one, so whichever of the two asks more often is the one that decides:
sites_drop = 1 (or 0) means new randomizations every time
a site is treated (so randomizations at every division, and
variation_drop is never used).
variation_drop = 0 likewise means new randomizations at
every division, and
sites_drop is then never used.
variation_drop = 1 never triggers new randomizations, leaving
sites_drop to decide on its own, and sites_drop = Inf never
triggers randomizations, leaving variation_drop to decide on its own.
Both switched off (variation_drop = 1 and
sites_drop = Inf) randomizes once, at the first division, and reuses
those trees for the whole tree. This is the fastest setting and the one that
fits the data least well.
Some groups have all their dissimilarities equal to one single value: every
pair of sites inside the group is exactly as different as every other pair.
These are tied blocks, and they are common in presence-absence data, where
indices such as Simpson saturate at 1 as soon as two sites share no species.
A tied block means randomization brings nothing useful
and the n_runs trees are wasted computation time.
To avoid this, the argument tie_block_resolution = TRUE (the default)
recognises such a group and
resolves it directly, peeling its sites off one at a time with every
division sitting at the common value. Setting it to FALSE divides tied
blocks from randomized trees
like any other group (pre-1.4.0 behaviour).
The height of nodes in a tree must be monotonous, i.e. a child node cannot be have a higher height than its parents. However, this situation can happen when building the tree, which is why all tree construction algorithms have a monotonicity section where node height is recalculated.
IHCT corrects the
heights once the whole tree is built, with a method that depends on
height_rule:
"max_child" raises every division to the highest division inside it. It is
simple, and it is what bioregion 1.4.0 and earlier did, but a single high
division buried deep inside a group drags all of its parents up with it,
well above the dissimilarities those divisions actually summarize. We
found out it provides lower quality (lower cophenetic correlation
coefficient) than "least_squares", so we changed the default after 1.4.0.
"least_squares" (the default) instead moves the heights as little as it
can: among all sets of heights with no branch doubling back, it takes the
one that stays closest to the heights the divisions were given, weighting
each division by the number of site pairs it stands for. With
method = "average" these are provably the heights that fit the
dissimilarities best on the tree shape at hand, so the cophenetic
correlation is never below what "max_child" gives and is usually a little
above it.
Most of the waiting is spent building the randomized trees, and the runs of
one group do not depend on each other, so ihct_n_workers can share them
between several processes of your computer. This only pays on large
matrices, where a single run is slow enough to be worth sending to another
process: groups of fewer than 200 sites are always done in one process, and
small datasets should be left at ihct_n_workers = 1. We found that
4 workers give good gains (about three times faster on a 5,000-site
matrix); beyond that the processes spend their time waiting for memory
rather than computing, and on a 10,000-site matrix going from 4 workers
to 8 provided only limited gains while doubling the memory needed.
Each worker also needs its own copy of the
dissimilarity matrix on Windows, about 200 MB for 5,000 sites and 800 MB
for 10,000, so ask for fewer workers than your memory allows copies.
Whatever you set, the tree is the same: the random shuffles are always drawn
in the same order by the main process, and only the building of the trees is
handed out to workers.
For the same seed, the following settings give the tree that bioregion 1.4.0 and earlier produced:
ihct(dist_mat,
method = "average",
n_runs = 100,
top_n_trees = 2,
variation_drop = 0,
height_rule = "max_child",
tie_block_resolution = FALSE)
sites_drop may be left at any value here, since variation_drop = 0
already rebuilds the trees at every division.
An hclust object, whose method element is set to
"Iterative Hierarchical Consensus Tree". Its heights are monotone (no
division is lower than a division it contains) and its labels are the site
names of dist_mat, in alphabetical order. It can be plotted, cut with
cut_tree() or cutree, and used anywhere an hclust
object is expected.
Boris Leroy (leroy.boris@gmail.com)
Pierre Denelle (pierre.denelle@gmail.com)
Maxime Lenormand (maxime.lenormand@inrae.fr)
Dapporto L, Ramazzotti M, Fattorini S, Talavera G, Vila R & Dennis RLH (2013) Recluster: an unbiased clustering procedure for beta-diversity turnover. Ecography 36, 1070–1075.
Dapporto L, Ciolli G, Dennis RLH, Fox R & Shreeve TG (2015) A new procedure for extrapolating turnover regionalization at mid-small spatial scales, tested on British butterflies. Methods in Ecology and Evolution 6, 1287–1297.
Kreft H & Jetz W (2010) A framework for delineating biogeographical regions based on species distributions. Journal of Biogeography 37, 2029–2053.
For more details illustrated with a practical example, see the vignette: https://biorgeo.github.io/bioregion/articles/a4_1_hierarchical_clustering.html.
Associated functions: hclu_hierarclust cut_tree
comat <- matrix(sample(0:1000, size = 500, replace = TRUE, prob = 1/1:1001),
20, 25)
rownames(comat) <- paste0("Site",1:20)
colnames(comat) <- paste0("Species",1:25)
dissim <- dissimilarity(comat, metric = "Simpson")
dist_mat <- net_to_mat(dissim[, 1:3],
weight = TRUE,
squared = TRUE,
symmetrical = TRUE)
set.seed(1)
tree <- ihct(dist_mat,
n_runs = 20,
verbose = FALSE)
plot(tree)
cut_tree(tree, n_clust = 3)
# Fastest setting: build the randomized trees once, at the first division,
# and reuse them all the way down
set.seed(1)
fast_tree <- ihct(dist_mat,
n_runs = 20,
variation_drop = 1,
sites_drop = Inf,
verbose = FALSE)
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.