Supplemental code for Toytree manuscript


In [1]:
# conda install toytree -c eaton-lab
# conda install scipy

In [2]:
import toytree
import toyplot
import toyplot.pdf
import toyplot.svg

# used to generate data to add to plots
import numpy as np
import scipy.stats as sc

In [3]:
print(toytree.__version__)


0.2.3

Figure 1

Figure 1A


In [4]:
# load tree from URL and root on outgroup
tre = toytree.tree("https://eaton-lab.org/data/Cyathophora.tre")
rtre = tre.root(wildcard="prz")

# default style tree drawing
rtre.draw();


38362_rex39618_rex35236_rex35855_rex40578_rex30556_thamno33413_thamno41478_cyathophylloides41954_cyathophylloides30686_cyathophylla29154_superba33588_przewalskii32082_przewalskii

Figure 1B

This code may look a bit complex, but that is because I wanted to demonstrate how to modify many different styles of the plot in one drawing. You can often make very nice plots using many fewer styling options.


In [5]:
# load tree from URL and root on outgroup
tre = toytree.tree("https://eaton-lab.org/data/Cyathophora.tre")
rtre = tre.root(wildcard="prz")

# add styling to nodes, edges, tips and axes
rtre.draw(
    
    # style tip labels, align, italicize, and trim accession
    tip_labels_align=True,
    tip_labels=[
        "<i>P. {}</i>".format(i.split("_")[1]) 
        for i in rtre.get_tip_labels()
    ],
    
    # style nodes, exclude root and tips
    node_sizes=[
        8 if i else 0 for i in rtre.get_node_values(
        show_tips=False, show_root=False
    )],
    node_colors=[
        "grey" if i==100 else "lightgrey" for i in 
        rtre.get_node_values("support")
    ],
    node_style={"stroke": "#262626"},
    
    # style node labels, shift support values from nodes
    node_labels=rtre.get_node_values("support"),
    node_labels_style={
        "-toyplot-anchor-shift": "-10px",
        "baseline-shift": "5px"
    },
    
    # style edge labels, color descendants of nodes by index
    edge_colors=rtre.get_edge_values_from_dict({
        24: toytree.colors[0], 
        16: toytree.colors[1],
    }),
    
    # add a scale bar to the x axis
    scalebar=True,
);


P. rexP. rexP. rexP. rexP. rexP. thamnoP. thamnoP. cyathophylloidesP. cyathophylloidesP. cyathophyllaP. superbaP. przewalskiiP. przewalskii10096100991001001001001001001000.000.020.04

Figure 1C

This drawing combines a simple tree plot with a somewhat complex plot of overlapping histograms next to it on the same coordinate grid. The key is setting up to axes objects on the same canvas from the start that have the same range.


In [6]:
# load tree from URL and root on outgroup
tre = toytree.tree("https://eaton-lab.org/data/Cyathophora.tre")
rtre = tre.root(wildcard="prz")

# set up canvas for two panel plot
canvas = toyplot.Canvas(width=300, height=300)
ax0 = canvas.cartesian(
    bounds=(50, 180, 50, 250),
    ymin=0, ymax=rtre.ntips, padding=15)
ax1 = canvas.cartesian(
    bounds=(220, 300, 50, 250), 
    ymin=0, ymax=rtre.ntips, padding=15)

# list of colors
tcols = [toytree.colors[0]] * 7 + [toytree.colors[1]] * 4 + [toytree.colors[2]] * 2

# draw tree with styles tips
rtre.draw(
    axes=ax0, 
    tip_labels_align=True,
    tip_labels_colors=tcols, 
    tip_labels=[i.split("_")[0] for i in rtre.get_tip_labels()],
)
ax0.show = False

# generate a random distribution between -10 and 10 for each tip in the tree
np.random.seed(1234)
points = np.linspace(-10, 10, 50)
dists = {}
for tip in rtre.get_tip_labels():
    dists[tip] = sc.norm.pdf(
        points, 
        loc=np.random.randint(-5, 5, 1), 
        scale=np.random.normal(2, 0.5)
    )

# iterate from top to bottom (ntips to 0) so that plots are stacked
for tip in range(tre.ntips)[::-1]:

    # select color for hist
    color = tcols[tip]

    # get tip name and get hist from dict
    tipname = tre.get_tip_labels()[tip]
    probs = dists[tipname]

    # fill histogram with slightly overlapping histograms
    ax1.fill(
        points, probs / probs.max() * 1.25,
        baseline=[tip] * len(points),
        style={"fill": color, "stroke": "white", "stroke-width": 0.5, "opacity": 0.9},
        title=tipname,
            )

    # add horizontal line at base
    ax1.hlines(tip, opacity=0.5, color="grey", style={"stroke-width": 0.5})
    
ax1.y.show = False


3836239618352363585540578305563341341478419543068629154335883208241954_cyathophylloides41478_cyathophylloides29154_superba30686_cyathophylla32082_przewalskii33588_przewalskii33413_thamno30556_thamno40578_rex35855_rex35236_rex39618_rex38362_rex-10-50510

Figure 1: A,B,C

This simply combined the tree plots from above onto a shared canvas.


In [7]:
canvas = toyplot.Canvas(width=850, height=300)

ax0 = canvas.cartesian(bounds=(25, 250, 50, 250))
ax1 = canvas.cartesian(bounds=(300, 525, 50, 250))
ax2 = canvas.cartesian(bounds=(550, 685, 50, 250),
                       ymin=0, ymax=rtre.ntips, padding=15)
ax3 = canvas.cartesian(bounds=(725, 800, 50, 250), 
                       ymin=0, ymax=rtre.ntips, padding=15)

# (A) simple tree
rtre.draw(axes=ax0);

# (B) tree with extensive styling
rtre.draw(
    axes=ax1,
    tip_labels_align=True,
    tip_labels=[
        "<i>P. {}</i>".format(i.split("_")[1]) 
        for i in rtre.get_tip_labels()
    ],
    node_sizes=[8 if i else 0 for i in rtre.get_node_values(show_tips=False)],
    node_style={"stroke": "#262626"},
    node_colors=[
        'grey' if i==100 else 'lightgrey' 
        for i in rtre.get_node_values('support')
    ],
    node_labels=rtre.get_node_values('support', show_root=False, show_tips=False),
    node_labels_style={
        "-toyplot-anchor-shift": "-10px",
        "baseline-shift": "5px"
    },
    edge_colors=rtre.get_edge_values_from_dict({
        24: toytree.colors[0], 
        16: toytree.colors[1],
    }),
    scalebar=True,
)

# (C) tree combined with other Toyplot data plots
# get list of tip colors
tcols = [toytree.colors[0]] * 7 + [toytree.colors[1]] * 4 + [toytree.colors[2]] * 2
rtre.draw(
    axes=ax2, 
    tip_labels_align=True,
    tip_labels_colors=tcols,
    tip_labels=[i.split("_")[0] for i in rtre.get_tip_labels()],
)

# add other toyplot plots
# generate a distribution between -10 and 10 for each tip in the tree
np.random.seed(1234)
points = np.linspace(-10, 10, 50)
dists = {}
for tip in rtre.get_tip_labels():
    dists[tip] = sc.norm.pdf(
        points, 
        loc=np.random.randint(-5, 5, 1), 
        scale=np.random.normal(2, 0.5)
    )

# iterate from top to bottom (ntips to 0) so that plots are stacked
for tip in range(tre.ntips)[::-1]:

    # select a color for hist
    color = tcols[tip]

    # get tip name and get hist from dict
    tipname = tre.get_tip_labels()[tip]
    probs = dists[tipname]

    # fill histogram with slightly overlapping histograms
    ax3.fill(
        points, probs / probs.max() * 1.25,
        baseline=[tip] * len(points),
        style={"fill": color, "stroke": "white", "stroke-width": 0.5, "opacity": 0.9},
        title=tipname,
            )

    # add horizontal line at base
    ax3.hlines(tip, opacity=0.5, color="grey", style={"stroke-width": 0.5})

    
ax0.show = False
ax1.y.show = False
ax2.show = False
ax3.y.show = False

# add subfigure text labels to Canvas
canvas.text(25, 25, "(a)", style={"font-size": "18px"});
canvas.text(300, 25, "(b)", style={"font-size": "18px"});
canvas.text(550, 25, "(c)", style={"font-size": "18px"});

# save canvas
toyplot.pdf.render(canvas, "ToyTree-figure.pdf")
toyplot.svg.render(canvas, "ToyTree-figure.svg")
canvas


Out[7]:
38362_rex39618_rex35236_rex35855_rex40578_rex30556_thamno33413_thamno41478_cyathophylloides41954_cyathophylloides30686_cyathophylla29154_superba33588_przewalskii32082_przewalskiiP. rexP. rexP. rexP. rexP. rexP. thamnoP. thamnoP. cyathophylloidesP. cyathophylloidesP. cyathophyllaP. superbaP. przewalskiiP. przewalskii10096100991001001001001001001000.000.020.043836239618352363585540578305563341341478419543068629154335883208241954_cyathophylloides41478_cyathophylloides29154_superba30686_cyathophylla32082_przewalskii33588_przewalskii33413_thamno30556_thamno40578_rex35855_rex35236_rex39618_rex38362_rex-10-50510(a)(b)(c)

Figure 2

Figure 2AB


In [8]:
tstring = """\
(((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.25,c:3.25);
(((b:1.5,d:1.5):0.75,(a:1,e:1):1.25):1.5,c:3.75);
(((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);
(((a:1,b: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);
"""

In [9]:
# load trees into a multitree object
mtre = toytree.mtree(tstring)

# draw a grid of trees
mtre.draw_tree_grid(
    nrows=1, ncols=3, start=0,
    width=400,
    height=200,
    edge_type='c',
);

mtre.draw_tree_grid(
    nrows=1, ncols=3, start=0,
    width=400,
    height=200,
    edge_type='c',
    fixed_order=True,
    edge_style={"stroke": "#888888"}
);


edbacebdaceadbc
edbacedbacedbac

Figure 2C


In [10]:
# get consensus tree
mtre = toytree.mtree(tstring)
ctre = mtre.get_consensus_tree().root("c")

# set styles on the individual trees
for tre in mtre.treelist:
    rf = tre.treenode.robinson_foulds(ctre.treenode)[0]
    if rf == 0:
        tre.style.edge_style["stroke"] = toytree.colors[1]
    else:
        tre.style.edge_style["stroke"] = toytree.colors[2]

mtre.draw_cloud_tree(width=250, height=300, edge_style={"stroke-opacity": 0.2});


Figure 2D


In [11]:
fish = toytree.mtree("https://eaton-lab.org/data/densitree.nex")
print(len(fish))


160

In [12]:
customorder = [
    "Priapella",
    "Psjonesii",
    "Xmayae",
    "Xalvarezi",
    "Xhellerii",
    "Xsignum",
    "Xmonticolus",
    "Xclemenciae_G",
    "Xbirchmanni_GARC",
    "Xmalinche_CHIC2",
    "Xcortezi",
    "Xnezahuacoyotl",
    "Xmontezumae",
    "Xcontinens",
    "Xpygmaeus",
    "Xmultilineatus",
    "Xnigrensis",
    "Xgordoni",
    "Xmeyeri",
    "Xcouchianus",
    "Xxiphidium",
    "Xvariatus",
    "Xevelynae",
    "Xmilleri",
    "Xandersi",
    "Xmaculatus_JpWild",
]

In [13]:
# draw the cloudtree
canvas, axes = fish.draw_cloud_tree(
    height=450,
    width=450,
    edge_style={
        'stroke': toyplot.color.brewer.palette("BlueGreen")[4],
        'opacity': 0.05,
    },
    fixed_order=customorder,
    tip_labels={
        i: "<i>{}. {}</i>".format(i[0], i[1:]) for i in customorder
    }
);

# add colored nodes at the tips (x-axis=0) (y-axis=0-ntips)
xlocs = np.zeros(fish.ntips)
ylocs = np.arange(fish.ntips)
colors = np.concatenate([
    [toytree.colors[2]] * 2,
    [toytree.colors[1]] * 6,
    [toytree.colors[5]] * 9,
    [toytree.colors[0]] * 9,
])
axes.scatterplot(
    xlocs + 0.05,
    ylocs,
    color=colors,
    mstyle={"stroke": "black", "stroke-width": 0.5},
    size=6,
);


Figure 2 A,B,C,D


In [15]:
canvas = toyplot.Canvas(width=600, height=500)

axA = canvas.cartesian(bounds=(50, 250, 50, 150))
axB = canvas.cartesian(bounds=(50, 250, 175, 275))
axC = canvas.cartesian(bounds=(85, 200, 300, 450))
axD = canvas.cartesian(bounds=(330, 620, 75, 425))

# FIGURE 2AB
# load trees into a multitree object
mtre = toytree.mtree(tstring)

# tree grid
mtre.draw_tree_grid(
    axes=axA, 
    ncols=3, nrows=1,
    layout='right',
    edge_type='c', 
    xbaseline=2.5,
    edge_style={"stroke": "#262626"},
)
mtre.draw_tree_grid(
    axes=axB, 
    ncols=3, nrows=1,
    layout='right', 
    edge_type='c', 
    xbaseline=2.5,
    fixed_order=True,
    edge_style={"stroke": "#888888"},
)
    
# FIGURE 2C
# set styles on the individual trees
for tre in mtre.treelist:
    rf = tre.treenode.robinson_foulds(ctre.treenode)[0]
    if rf == 0:
        tre.style.edge_style["stroke"] = toytree.colors[1]
    else:
        tre.style.edge_style["stroke"] = toytree.colors[2]

mtre.draw_cloud_tree(
    axes=axC, 
    edge_style={"stroke-opacity": 0.2},
    tip_labels_style={"font-size": "14px"}
)

# FIGURE 2D (fish and customorder defined above)
fish.draw_cloud_tree(
    axes=axD,
    edge_style={
        'stroke': toyplot.color.brewer.palette("BlueGreen")[4],
        'opacity': 0.05,
    },
    fixed_order=customorder,
    tip_labels={
        i: "<i>{}. {}</i>".format(i[0], i[1:]) for i in customorder
    },
    tip_labels_style={"font-size": "11px"},
);

# add colored nodes at the tips (x-axis=0) (y-axis=0-ntips)
xlocs = np.zeros(fish.ntips)
ylocs = np.arange(fish.ntips)
colors = np.concatenate([
    [toytree.colors[2]] * 2,
    [toytree.colors[1]] * 6,
    [toytree.colors[5]] * 9,
    [toytree.colors[0]] * 9,
])
axD.scatterplot(
    xlocs + 0.05,
    ylocs,
    color=colors,
    mstyle={"stroke": "black", "stroke-width": 0.5},
    size=6,
);

# add subfigure text labels to Canvas
canvas.text(25, 35, "(a)", style={"font-size": "18px"});
canvas.text(25, 170, "(b)", style={"font-size": "18px"});
canvas.text(25, 300, "(c)", style={"font-size": "18px"});
canvas.text(320, 35, "(d)", style={"font-size": "18px"});

# save canvas in PDF SVG
toyplot.pdf.render(canvas, "MultiTree-figure.pdf")
toyplot.svg.render(canvas, "MultiTree-figure.svg")
canvas


Out[15]:
edbacebdaceadbcedbacedbacedbacedbacP. riapellaP. sjonesiiX. mayaeX. alvareziX. helleriiX. signumX. monticolusX. clemenciae_GX. birchmanni_GARCX. malinche_CHIC2X. corteziX. nezahuacoyotlX. montezumaeX. continensX. pygmaeusX. multilineatusX. nigrensisX. gordoniX. meyeriX. couchianusX. xiphidiumX. variatusX. evelynaeX. milleriX. andersiX. maculatus_JpWild(a)(b)(c)(d)