MultiTree¶
The toytree.MultiTree
class object is used to represent a collection of ToyTree
objects and includes attributes and methods for describing this set or performing operations on it. Common examples of tree sets include bootstrap replicate samples or posterior distributions of sampled trees; common operations on sets of trees include consensus tree inference, computing discordance or distance statistics, and plotting tree grids or cloud trees.
import toytree
Generating MultiTrees¶
MultiTree objects can be generated from a list of Toytrees or newick strings, or by parsing a file, url, or string of text that includes newick trees separated by newlines. The convenience function toytree.mtree()
can be used to parse multitree input data similar to how the function toytree.tree
is used to parse individual trees, and supports the same file formats.
From tree data¶
Below is an example multi-newick string representing multiple trees as newick strings separated by newlines. You can create a MultiTree from this input data, entered as a string or filepath, by passing it to the toytree.mtree()
convenience parsing function. Each tree will be parsed individually and stored as a list of ToyTree
objects contained within a returned MultiTree
object.
multinewick = """\
(((a:1,b:1):1,(d:1.5,e:1.5):0.5):1,c:3);
(((a:1,d:1):1,(b:1,e:1):1):1,c:3);
(((a:1.5,b:1.5):1,(d:1,e:1):1.5):1,c:3.5);
(((a:1.25,b:1.25):0.75,(d:1,e:1):1):1,c:3);
(((a:1,b:1):1,(d:1.5,e:1.5):0.5):1,c:3);
(((b:1,a:1):1,(d:1.5,e:1.5):0.5):2,c:4);
(((a:1.5,b:1.5):0.5,(d:1,e:1):1):1,c:3);
(((b:1.5,d:1.5):0.5,(a:1,e:1):1):1,c:3);
"""
# create an mtree from a string, list of strings, url, or file.
mtree1 = toytree.mtree(multinewick)
mtree1
<toytree.MultiTree ntrees=8>
From a collection of trees¶
Similarly, you can create a MultiTree
by providing a collection of ToyTree
objects to the toytree.mtree
function. Here we generate a list of 50 random coalescent trees and pass the list as input to create a new MultiTree
.
# generate 50 random coalescent trees each with 6 tips
coaltrees = [toytree.rtree.coaltree(k=6) for i in range(50)]
# create a MultiTree from a list of ToyTrees
mtree2 = toytree.mtree(coaltrees)
mtree2
<toytree.MultiTree ntrees=50>
Indexable and Iterable¶
One or more trees can be indexed or sliced from a MultiTree
, and sequential trees can be accessed through iteration. The trees themselves are stored in the .treelist
attribute of the MultiTree
object as a list. This can be modified to remove, add, or reorder the trees. Several example operations are shown below for accessing one or more trees.
# get first tree
mtree1[0]
<toytree.ToyTree at 0x7f03c5aba7a0>
# get all trees
mtree1[:]
[<toytree.ToyTree at 0x7f03c5aba7a0>, <toytree.ToyTree at 0x7f03c5ab97e0>, <toytree.ToyTree at 0x7f03c5ab9270>, <toytree.ToyTree at 0x7f03c5ab8b20>, <toytree.ToyTree at 0x7f03c5ab8550>, <toytree.ToyTree at 0x7f03c5abb7f0>, <toytree.ToyTree at 0x7f03c5abbbe0>, <toytree.ToyTree at 0x7f03c58ec190>]
# slice the first three trees
mtree1[:3]
[<toytree.ToyTree at 0x7f03c5aba7a0>, <toytree.ToyTree at 0x7f03c5ab97e0>, <toytree.ToyTree at 0x7f03c5ab9270>]
# iterate over ToyTrees in a MultiTree
for tree in mtree1:
print(tree)
<toytree.ToyTree at 0x7f03c5aba7a0> <toytree.ToyTree at 0x7f03c5ab97e0> <toytree.ToyTree at 0x7f03c5ab9270> <toytree.ToyTree at 0x7f03c5ab8b20> <toytree.ToyTree at 0x7f03c5ab8550> <toytree.ToyTree at 0x7f03c5abb7f0> <toytree.ToyTree at 0x7f03c5abbbe0> <toytree.ToyTree at 0x7f03c58ec190>
# re-arrange trees in the treelist to send the first to be last
mtree1.treelist = mtree1.treelist[1:] + [mtree1.treelist[0]]
mtree1[:]
[<toytree.ToyTree at 0x7f03c5ab97e0>, <toytree.ToyTree at 0x7f03c5ab9270>, <toytree.ToyTree at 0x7f03c5ab8b20>, <toytree.ToyTree at 0x7f03c5ab8550>, <toytree.ToyTree at 0x7f03c5abb7f0>, <toytree.ToyTree at 0x7f03c5abbbe0>, <toytree.ToyTree at 0x7f03c58ec190>, <toytree.ToyTree at 0x7f03c5aba7a0>]
Attributes and types of tree sets¶
Most of the time MultiTree
objects are used to hold a collection of trees that all share the same tip labels, such as a collection of bootstrap replicates. But, in other cases, a MultiTree
could hold a collection of unrelated trees, in which case some of the built-in functions for comparing trees (such as consensus tree inference) will raise an error, but it still provides a useful container for drawing trees. These methods will raise a ToyTreeError when attempted if the tree set is a mixed collection of trees. The MultiTree
class contains several functions to quickly check attributes of the tree set to examine the number of trees, whether they share the same tip names, and whether the trees are rooted or ultrametric.
mtree1.ntrees
8
mtree1.all_tree_tip_labels_same()
True
mtree1.all_tree_topologies_same()
False
mtree1.all_tree_tips_aligned()
False
Consensus trees¶
A majority-rule consensus tree is a tree that represents the most common non-conflicting splits among a set of input trees. In toytree
this can be inferred from a MultiTree
object using the .get_consensus_tree
, which returns a ToyTree
with the consensus topology on which several types of statistics have been stored to nodes. This can be useful in several contexts, but one of the simplest is to find the most common order of tip names among a set of trees, so that when you make a MultiTree
plot you can order tips in the same way to more easily visualize variation among trees. There are many options and uses for consensus trees, check out the Consensus Trees documentation section for more details.
In the example below we get a consensus tree from the mtree1
multitree object which contains 8 trees. The support values on this returned tree represent the proportion of trees in the input set that contain each clade in the consensus tree. By default, the returned tree is unrooted, since the input trees may vary in their rooting. Here the trees exhibit variation in the relationships among (a,b,d,e)
with (a,b)
and (d,e)
each occurring in 75% of trees.
# get a consensus tree
ctree = mtree1.get_consensus_tree()
# plot the tree showing 'support' values
ctree.draw(
ts='r', layout="unrooted", width=350, height=350,
node_labels="support", node_sizes=14, node_as_edge_data=True);
Unique trees¶
Given a set of trees it is useful to be able to pull out just the unique topologies from the set. The function get_unique_topologies()
returns a list of (tree, int)
tuples from a MultiTree
with each unique topology paired with its number of occurrences in the set. Note, this condenses all trees with the same topology into a single representative, using the first occurrence as the returned tree, thus branch length variation is not retained. See the toytree.distance
subpackage for many additional methods for comparing trees and computing differences/distances between them.
# get (tree, count) for each unique topology in the MultiTree
mtree1.get_unique_topologies()
[[<toytree.ToyTree at 0x7f03c5ab9270>, 6], [<toytree.ToyTree at 0x7f03c5ab97e0>, 1], [<toytree.ToyTree at 0x7f03c58ec190>, 1]]
Drawing with MultiTrees¶
There are two main ways to draw groups of trees from MultiTree
objects. The first is simply called .draw()
and is used to plot multiple trees onto a shared canvas arranged into a grid. These trees can be arranged each on their own axes, or on shared axes to better compare scales. The second method is .draw_cloud_tree
, which plots a set of trees over top of one another, usually at low opacity, as a way of examining discordance and variation among a set of trees. These are sometimes called cloud trees, densitrees, or other similar names. A simple example of each is shown below.
Grid tree drawings¶
The .draw()
function of a MultiTree
returns multiple tree drawings layed out on a grid. The shape of the grid, and which trees from the tree set are drawn, can be set with the shape
and idxs
arguments. This also accepts all of the standard drawing arguments that can be used when drawing individual trees. See Drawing Tree Grids.
# draw a 2x4 grid of trees with 30px margins between them
mtree1.draw(ts='o', shape=(2, 4), width=600, height=300, margin=25, fixed_order=['c', 'b', 'e', 'a', 'd']);
Cloud tree drawings¶
It is sometimes even more informative to plot a number of trees on top of each other to visualize their discordance. These are sometimes called “densitree” plots, or here, “cloud tree plots”. See the documentation section on Drawing Cloud Trees for more details and examples.
# draw a cloud tree
mtree1.draw_cloud_tree(
scale_bar=True,
edge_style={
"stroke-opacity": 0.1,
"stroke-width": 3,
},
);