Drawing Tree Grids¶
When using toytree.MultiTree
objects you can easily draw multiple trees displayed as a grid on a canvas by using the .draw
function. Here a number of useful examples are demonstrated.
import toytree
Example dataset¶
# a MultiTree containing 50 random coalescent trees with 10 tips each
mtree = toytree.mtree([toytree.rtree.coaltree(10) for i in range(50)])
mtree.draw();
# a multi-newick string
NEWICKS = """\
(((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 a multitree object
mtree = toytree.mtree(NEWICKS)
Grid tree drawings¶
The .draw
function takes similar styling arguments as the ToyTree.draw
function but accepts a few additional arguments to also describe how to plot and organize multiple trees on a shared Canvas. This includes shape
, separate_axes
, and idxs
, described below. The MultiTree.draw()
function returns three objects, similar to ToyTree.draw()
, but instead of returning (Canvas, Cartesian, Mark)
it returns (Canvas, List[Cartesian], List[Mark])
, with the List elements composing the length of the number of trees drawn.
As you can see, the drawing below appears as a grid of multiple trees spaced on a canvas. A similar plot an be made by using toyplot
Canvas and Cartesian arguments directly to create a layout, and then adding tree drawings to each Cartesian axes, as explained in the [Advanced Drawing] section. The draw function here simply provides a convenient shortcut. Similar to normal tree drawings, many styling arguments are available. It is possible to apply style arguments to each tree individually, or to apply one style to all, as will be demonstrated below.
mtree.draw();
shape -- The argument shape
can be used to layout a grid describing how the trees should be arranged. It takes a tuple of ints as input in the form (rows, columns), similar to describing the shape of a numpy array. In the example below we draw 8 trees in a grid that has two rows and four columns. (See 'margin' below for further styling of spacing).
mtree.draw(shape=(2, 2), width=400, height=400);
shared_axes -- The shared_axes
arg accepts a boolean and can be used to set the same shared time scale on the time-associated axis of tree plots (this varies depending on the layout direction). This is useful for comparing the node heights or edge lengths among trees visually. This is demonstrated for five random coalescent trees below which differ in coalescent times.
mtree.draw(scale_bar=True, layout='d', shared_axes=True);
margin -- You can set the margin size in pixel units between the trees in a grid. The default value is automatically adjusted, but usually around 30. Larger values increase the spacing between trees, which can be useful for fitting longer tip names, while smaller values will pack trees closer together. A single value can be entered to apply to margins on all four sides, or a tuple of four values can be entered to modify the (top, right, bottom, left) margins in order.
mtree.draw(margin=(10, 30, 10, 30));
style and label axes -- Just as you can style the axes of a Cartesian object in a tree drawing you can similarly use axes styling to modify the scale, set tick marks, change tick or axes label styles, or add titles to subplots. Here we add a title to each plot.
# draw a tree grid while using shared_axes=True to share time axis
canvas, axes, marks = mtree.draw(
layout='d', edge_type='c', node_sizes=5, node_mask=False,
shape=(1, 4), width=650, height=250, scale_bar=True,
shared_axes=True,
);
# add a label to each subplot
for adx, ax in enumerate(axes):
ax.label.text = f"tree {adx}"
# add a y-axis label to only the first subplot
axes[0].y.label.text = " time (generations)"
axes[0].y.label.offset = 25
Tree grid styling¶
Trees in MultiTree grid drawing can be styled individually by setting the style dictionary attribute of each ToyTree in the treelist. Additionally, most styles can be applied as arguments to the draw_tree_grid() function to apply styles to all trees at once.
# load nexus trees data from ...
fish = toytree.mtree("https://eaton-lab.org/data/densitree.nex")
# draw with default styling
fish.draw(shape=(1, 4), height=350);
For example, here we set a different color to the tip labels style attribute for each tree in the MultiTree object.
# set different 'tip_labels_colors' for each tree
colors = toytree.color.color_cycler()
for tree in fish.treelist[:4]:
tree.style.tip_labels_colors = next(colors)
# draw several trees
fish.draw(shape=(1, 4), height=350);
The argument fixed_order
is especially useful for multitree drawings for easily comparing trees visually. This fixes the order in which tips in the tree will be ordered, which makes differences between trees visible as conflicts. Here we infer a consensus tree from the full set of trees and use its tip order as the fixed order on which to plot all other trees.
# get majority-rule consensus tree
consfish = fish.get_consensus_tree()
# draw tree grid and use consensus tree order as a fixed_order of tips
fish.draw(
shape=(2, 3),
height=600,
width=600,
fixed_order=True,
edge_type='c',
shared_axes=True,
);