Module asymmetree.visualize.TreeVis
Species and gene tree visualization.
Expand source code
# -*- coding: utf-8 -*-
"""Species and gene tree visualization."""
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
__author__ = 'David Schaller'
def assign_species_colors(tree):
"""Assign a unique color to each (extant) species in a species tree.
Parameters
----------
tree : Tree
The species tree with uniquely labeled (non-loss) leaves.
Returns
-------
dict
A dictonary containing the labels of the extant species as keys and
the assigned colors as values.
"""
return _assign_cmap({v.label for v in tree.leaves() if v.event != 'L'})
def assign_gene_colors(tree, species_colors=None):
"""Assign a color to each (extant) gene in a gene tree according to its
species.
Parameters
----------
tree : Tree
The gene tree whose (non-loss) leaves have the 'reconc' attribute, i.e,
the information to which species they belong.
species_colors : dict, optional
A dictonary containing the values of the 'color' attribute appearing
in the tree as keys and assigned colors as values.
Returns
-------
dict
A dictonary containing the labels of the extant genes as keys and
the assigned colors as values.
"""
if species_colors is None:
species_colors = _assign_cmap({v.reconc for v in tree.leaves()
if v.event != 'L'})
return {v.label: species_colors[v.reconc] for v in tree.leaves()
if v.event != 'L'}
def assign_colors(species_tree, gene_tree):
"""Assign a color to each (extant) species and gene.
Parameters
----------
species_tree : Tree
The species tree with uniquely labeled (non-loss) leaves.
gene_tree : Tree
The gene tree whose (non-loss) leaves have the 'reconc' attribute, i.e,
the information to which species they belong, that furthermore appear
as labels of the (non-loss) leaves in the species tree.
Returns
-------
tuple of two dicts
Dictonaries containing the labels of the extant species/genes as keys
and the assigned colors as values.
"""
species_colors = assign_species_colors(species_tree)
return species_colors, assign_gene_colors(gene_tree,
species_colors=species_colors)
def _assign_cmap(colors):
color_dict = {}
if len(colors) <= 10:
cmap = plt.get_cmap('tab10')(np.arange(len(colors), dtype=int))
else:
cmap = plt.get_cmap('jet')(np.linspace(0, 1.0, len(colors)))
for label, color in zip(colors, cmap):
color_dict[label] = color
return color_dict
def visualize(tree, color_dict=None, save_as=False, scale_symbols=1.0,
fontsize='medium'):
"""Visualize a tree.
Parameters
----------
tree : Tree
A tree whose nodes have the 'dist' attribute.
color_dict : dict, optional
A dictonary containing as values the assigned color for each label
of the (non-loss) leaves.
save_as : str, optional
Save the image to a file. The default is False, in which case the
image is not saved.
scale_symbols : float, optional
Adjust the size of the event type symbols. The default is 1.0.
fontsize : str or float or int, optional
Adjust the size of the text. The default is 'medium'.
"""
vis = Visualizer(tree, color_dict=color_dict, scale_symbols=scale_symbols,
fontsize=fontsize)
vis.draw(save_as=save_as)
class Visualizer:
"""Species and gene tree visualization."""
def __init__(self, tree, color_dict=None,
scale_symbols=1.0, fontsize='medium',
species_info=False):
"""
Parameters
----------
tree : Tree
A tree whose nodes have the 'dist' attribute.
color_dict : dict, optional
A dictonary containing as values the assigned color for each label
of the (non-loss) leaves.
scale_symbols : float, optional
Adjust the size of the event type symbols. The default is 1.0.
fontsize : str or float or int, optional
Adjust the size of the text. The default is 'medium'.
species_info : bool, False
If True, write the reconciliation information behind the leaf
label if available. The default is False.
"""
self.tree = tree
if color_dict:
self.colors = color_dict
else:
self.colors = {v.label: 'white' for v in tree.preorder()}
self.species_info = species_info
self.symbolsize = 0.03 * scale_symbols
self.fontsize = fontsize
self.symbollw = 0.04
self.leafs_per_vertical_unit = 15
self.symbol_zorder = 3
self.distance_dict = {}
#self.divtime_dict {}
self.leaf_counter = sum(1 for _ in tree.leaves())
self.node_positions = {}
def draw(self, distance_mode='evolutionary', save_as=False):
"""Draw the tree and optionally save it to file.
Parameters
----------
distance_mode : str, optional
Information that is used for the length of the species edges.
Currently only 'evolutionary' is supported, in which case the
'dist' attribute is used.
save_as : str, optional
Save the image to a file. The default is False, in which case the
image is not saved.
"""
self.fig, self.ax = plt.subplots()
self.ax.set_aspect('equal')
self.ax.invert_yaxis()
self.initial_traversal(distance_mode)
# self.assign_colors()
self.assign_positions()
self.draw_edges()
self.draw_nodes()
self.ax.axvline(x=0, linewidth=1, color='grey', linestyle='--')
self.ax.set_yticks([])
self.ax.spines['top'].set_visible(False)
self.ax.spines['left'].set_visible(False)
self.ax.spines['right'].set_visible(False)
self.ax.tick_params(axis='x', labelsize=self.fontsize)
xmin, xmax = self.ax.get_xlim()
ymin, ymax = self.ax.get_ylim()
self.fig.set_size_inches(5*abs(xmax-xmin), 5*abs(ymax-ymin)+0.4)
plt.tight_layout()
if save_as:
plt.savefig(save_as, dpi=300)
else:
plt.show()
def initial_traversal(self, distance_mode):
xmax = 0.0
for v in self.tree.preorder():
if distance_mode == 'evolutionary':
if not v.parent:
self.distance_dict[v] = 0.0
else:
self.distance_dict[v] = self.distance_dict[v.parent] + v.dist
if self.distance_dict[v] > xmax:
xmax = self.distance_dict[v]
elif distance_mode == 'divergence time':
raise ValueError('divergence time not yet implemented')
else:
raise ValueError(f"distance mode '{distance_mode}' not supported")
self.ax.set_xlim(-0.1,xmax+0.5)
def assign_positions(self):
ymax = (self.leaf_counter-1)/self.leafs_per_vertical_unit
self.ax.set_ylim(ymax+0.1, -self.symbolsize*0.6)
yposition = 0
for v in self.tree.postorder():
if not v.children:
self.node_positions[v] = (self.distance_dict[v],
yposition)
yposition += 1/self.leafs_per_vertical_unit
else:
ymean = (self.node_positions[v.children[0]][1] +
self.node_positions[v.children[-1]][1])/2
self.node_positions[v] = (self.distance_dict[v],
ymean)
def draw_edges(self):
for v in self.tree.preorder():
if v.parent:
if hasattr(v, 'transferred') and v.transferred:
color = 'red'
else:
color = 'black'
self.ax.plot([self.node_positions[v.parent][0],
self.node_positions[v][0]],
[self.node_positions[v][1],
self.node_positions[v][1]],
color=color,
linestyle='-', linewidth=1)
if v.children:
self.ax.plot([self.node_positions[v][0],
self.node_positions[v][0]],
[self.node_positions[v.children[0]][1],
self.node_positions[v.children[-1]][1]],
color='black',
linestyle='-', linewidth=1)
def draw_nodes(self):
for v in self.tree.preorder():
if not v.parent and not v.event:
self.draw_root(*self.node_positions[v])
elif v.children:
if v.event == 'D':
self.draw_dupl(*self.node_positions[v])
elif v.event == 'S':
self.draw_spec(*self.node_positions[v])
elif v.event == 'H':
self.draw_hgt(*self.node_positions[v])
elif v.event == 'GC':
self.draw_gc(*self.node_positions[v])
else:
if v.event == 'L':
self.draw_loss(*self.node_positions[v])
else:
x, y = self.node_positions[v]
self.draw_leaf(x, y, color=self.colors[v.label])
if not self.species_info:
text = str(v.label)
elif hasattr(v, 'reconc'):
text = '{} <{}>'.format(v.label, v.reconc)
self.write_label(x+self.symbolsize+0.02, y, text)
def draw_leaf(self, x, y, color='white', leftalign=False):
if leftalign:
x += self.symbolsize/2
fill = mpatches.Circle((x, y), self.symbolsize/2,
color=color, fill=True,
zorder=self.symbol_zorder)
self.ax.add_patch(fill)
outer = mpatches.Circle((x, y), self.symbolsize/2,
color='black', fill=False,
lw=self.symbolsize/self.symbollw,
zorder=self.symbol_zorder)
self.ax.add_patch(outer)
little = mpatches.Circle((x, y), self.symbolsize/8,
color='black', fill=True,
zorder=self.symbol_zorder)
self.ax.add_patch(little)
def draw_loss(self, x, y):
self.ax.plot([x,x],
[y-self.symbolsize/2, y+self.symbolsize/2],
color='black',
linestyle='-', linewidth=1)
def draw_root(self, x, y, rightalign=False):
if rightalign:
x -= self.symbolsize/2
fill = mpatches.Circle((x, y), self.symbolsize/2,
color='white', fill=True,
zorder=self.symbol_zorder)
self.ax.add_patch(fill)
outer = mpatches.Circle((x, y), self.symbolsize/2,
color='black', fill=False,
lw=self.symbolsize/self.symbollw,
zorder=self.symbol_zorder)
self.ax.add_patch(outer)
little = mpatches.Circle((x, y), self.symbolsize/5,
color='black', fill=False,
lw=self.symbolsize/self.symbollw,
zorder=self.symbol_zorder)
self.ax.add_patch(little)
def draw_spec(self, x, y):
fill = mpatches.Circle((x, y), self.symbolsize/2,
color='black', fill=True,
zorder=self.symbol_zorder)
self.ax.add_patch(fill)
def draw_dupl(self, x, y):
square = mpatches.Rectangle((x-self.symbolsize/2,
y-self.symbolsize/2),
width=self.symbolsize,
height=self.symbolsize,
color='white', fill=True,
zorder=self.symbol_zorder)
self.ax.add_patch(square)
border = mpatches.Rectangle((x-self.symbolsize/2,
y-self.symbolsize/2),
width=self.symbolsize,
height=self.symbolsize,
color='black', fill=False,
lw=self.symbolsize/self.symbollw,
zorder=self.symbol_zorder)
self.ax.add_patch(border)
def draw_hgt(self, x, y):
coord = np.asarray([[x,y-self.symbolsize/2],
[x+self.symbolsize/2,y+self.symbolsize/2],
[x-self.symbolsize/2,y+self.symbolsize/2]])
inner = mpatches.Polygon(coord, closed=True,
color='white', fill=True,
zorder=self.symbol_zorder)
self.ax.add_patch(inner)
outer = mpatches.Polygon(coord, closed=True,
color='black', fill=False,
lw=self.symbolsize/self.symbollw,
zorder=self.symbol_zorder)
self.ax.add_patch(outer)
def draw_gc(self, x, y):
coord = []
R = self.symbolsize/2
for i in range(5):
coord.append([x + 0.5 * R * np.cos(np.radians(54+i*72)),
y - 0.5 * R * np.sin(np.radians(54+i*72))])
coord.append([x + R * np.cos(np.radians(90+i*72)),
y - R * np.sin(np.radians(90+i*72))])
inner = mpatches.Polygon(coord, closed=True,
color='black', fill=True,
zorder=self.symbol_zorder)
self.ax.add_patch(inner)
def write_label(self, x, y, text):
self.ax.text(x, y, text,
fontsize=self.fontsize,
horizontalalignment='left',
verticalalignment='center')
Functions
def assign_colors(species_tree, gene_tree)-
Assign a color to each (extant) species and gene.
Parameters
species_tree:Tree- The species tree with uniquely labeled (non-loss) leaves.
gene_tree:Tree- The gene tree whose (non-loss) leaves have the 'reconc' attribute, i.e, the information to which species they belong, that furthermore appear as labels of the (non-loss) leaves in the species tree.
Returns
tupleoftwo dicts- Dictonaries containing the labels of the extant species/genes as keys and the assigned colors as values.
Expand source code
def assign_colors(species_tree, gene_tree): """Assign a color to each (extant) species and gene. Parameters ---------- species_tree : Tree The species tree with uniquely labeled (non-loss) leaves. gene_tree : Tree The gene tree whose (non-loss) leaves have the 'reconc' attribute, i.e, the information to which species they belong, that furthermore appear as labels of the (non-loss) leaves in the species tree. Returns ------- tuple of two dicts Dictonaries containing the labels of the extant species/genes as keys and the assigned colors as values. """ species_colors = assign_species_colors(species_tree) return species_colors, assign_gene_colors(gene_tree, species_colors=species_colors) def assign_gene_colors(tree, species_colors=None)-
Assign a color to each (extant) gene in a gene tree according to its species.
Parameters
tree:Tree- The gene tree whose (non-loss) leaves have the 'reconc' attribute, i.e, the information to which species they belong.
species_colors:dict, optional- A dictonary containing the values of the 'color' attribute appearing in the tree as keys and assigned colors as values.
Returns
dict- A dictonary containing the labels of the extant genes as keys and the assigned colors as values.
Expand source code
def assign_gene_colors(tree, species_colors=None): """Assign a color to each (extant) gene in a gene tree according to its species. Parameters ---------- tree : Tree The gene tree whose (non-loss) leaves have the 'reconc' attribute, i.e, the information to which species they belong. species_colors : dict, optional A dictonary containing the values of the 'color' attribute appearing in the tree as keys and assigned colors as values. Returns ------- dict A dictonary containing the labels of the extant genes as keys and the assigned colors as values. """ if species_colors is None: species_colors = _assign_cmap({v.reconc for v in tree.leaves() if v.event != 'L'}) return {v.label: species_colors[v.reconc] for v in tree.leaves() if v.event != 'L'} def assign_species_colors(tree)-
Assign a unique color to each (extant) species in a species tree.
Parameters
tree:Tree- The species tree with uniquely labeled (non-loss) leaves.
Returns
dict- A dictonary containing the labels of the extant species as keys and the assigned colors as values.
Expand source code
def assign_species_colors(tree): """Assign a unique color to each (extant) species in a species tree. Parameters ---------- tree : Tree The species tree with uniquely labeled (non-loss) leaves. Returns ------- dict A dictonary containing the labels of the extant species as keys and the assigned colors as values. """ return _assign_cmap({v.label for v in tree.leaves() if v.event != 'L'}) def visualize(tree, color_dict=None, save_as=False, scale_symbols=1.0, fontsize='medium')-
Visualize a tree.
Parameters
tree:Tree- A tree whose nodes have the 'dist' attribute.
color_dict:dict, optional- A dictonary containing as values the assigned color for each label of the (non-loss) leaves.
save_as:str, optional- Save the image to a file. The default is False, in which case the image is not saved.
scale_symbols:float, optional- Adjust the size of the event type symbols. The default is 1.0.
fontsize:strorfloatorint, optional- Adjust the size of the text. The default is 'medium'.
Expand source code
def visualize(tree, color_dict=None, save_as=False, scale_symbols=1.0, fontsize='medium'): """Visualize a tree. Parameters ---------- tree : Tree A tree whose nodes have the 'dist' attribute. color_dict : dict, optional A dictonary containing as values the assigned color for each label of the (non-loss) leaves. save_as : str, optional Save the image to a file. The default is False, in which case the image is not saved. scale_symbols : float, optional Adjust the size of the event type symbols. The default is 1.0. fontsize : str or float or int, optional Adjust the size of the text. The default is 'medium'. """ vis = Visualizer(tree, color_dict=color_dict, scale_symbols=scale_symbols, fontsize=fontsize) vis.draw(save_as=save_as)
Classes
class Visualizer (tree, color_dict=None, scale_symbols=1.0, fontsize='medium', species_info=False)-
Species and gene tree visualization.
Parameters
tree:Tree- A tree whose nodes have the 'dist' attribute.
color_dict:dict, optional- A dictonary containing as values the assigned color for each label of the (non-loss) leaves.
scale_symbols:float, optional- Adjust the size of the event type symbols. The default is 1.0.
fontsize:strorfloatorint, optional- Adjust the size of the text. The default is 'medium'.
species_info:bool, False- If True, write the reconciliation information behind the leaf label if available. The default is False.
Expand source code
class Visualizer: """Species and gene tree visualization.""" def __init__(self, tree, color_dict=None, scale_symbols=1.0, fontsize='medium', species_info=False): """ Parameters ---------- tree : Tree A tree whose nodes have the 'dist' attribute. color_dict : dict, optional A dictonary containing as values the assigned color for each label of the (non-loss) leaves. scale_symbols : float, optional Adjust the size of the event type symbols. The default is 1.0. fontsize : str or float or int, optional Adjust the size of the text. The default is 'medium'. species_info : bool, False If True, write the reconciliation information behind the leaf label if available. The default is False. """ self.tree = tree if color_dict: self.colors = color_dict else: self.colors = {v.label: 'white' for v in tree.preorder()} self.species_info = species_info self.symbolsize = 0.03 * scale_symbols self.fontsize = fontsize self.symbollw = 0.04 self.leafs_per_vertical_unit = 15 self.symbol_zorder = 3 self.distance_dict = {} #self.divtime_dict {} self.leaf_counter = sum(1 for _ in tree.leaves()) self.node_positions = {} def draw(self, distance_mode='evolutionary', save_as=False): """Draw the tree and optionally save it to file. Parameters ---------- distance_mode : str, optional Information that is used for the length of the species edges. Currently only 'evolutionary' is supported, in which case the 'dist' attribute is used. save_as : str, optional Save the image to a file. The default is False, in which case the image is not saved. """ self.fig, self.ax = plt.subplots() self.ax.set_aspect('equal') self.ax.invert_yaxis() self.initial_traversal(distance_mode) # self.assign_colors() self.assign_positions() self.draw_edges() self.draw_nodes() self.ax.axvline(x=0, linewidth=1, color='grey', linestyle='--') self.ax.set_yticks([]) self.ax.spines['top'].set_visible(False) self.ax.spines['left'].set_visible(False) self.ax.spines['right'].set_visible(False) self.ax.tick_params(axis='x', labelsize=self.fontsize) xmin, xmax = self.ax.get_xlim() ymin, ymax = self.ax.get_ylim() self.fig.set_size_inches(5*abs(xmax-xmin), 5*abs(ymax-ymin)+0.4) plt.tight_layout() if save_as: plt.savefig(save_as, dpi=300) else: plt.show() def initial_traversal(self, distance_mode): xmax = 0.0 for v in self.tree.preorder(): if distance_mode == 'evolutionary': if not v.parent: self.distance_dict[v] = 0.0 else: self.distance_dict[v] = self.distance_dict[v.parent] + v.dist if self.distance_dict[v] > xmax: xmax = self.distance_dict[v] elif distance_mode == 'divergence time': raise ValueError('divergence time not yet implemented') else: raise ValueError(f"distance mode '{distance_mode}' not supported") self.ax.set_xlim(-0.1,xmax+0.5) def assign_positions(self): ymax = (self.leaf_counter-1)/self.leafs_per_vertical_unit self.ax.set_ylim(ymax+0.1, -self.symbolsize*0.6) yposition = 0 for v in self.tree.postorder(): if not v.children: self.node_positions[v] = (self.distance_dict[v], yposition) yposition += 1/self.leafs_per_vertical_unit else: ymean = (self.node_positions[v.children[0]][1] + self.node_positions[v.children[-1]][1])/2 self.node_positions[v] = (self.distance_dict[v], ymean) def draw_edges(self): for v in self.tree.preorder(): if v.parent: if hasattr(v, 'transferred') and v.transferred: color = 'red' else: color = 'black' self.ax.plot([self.node_positions[v.parent][0], self.node_positions[v][0]], [self.node_positions[v][1], self.node_positions[v][1]], color=color, linestyle='-', linewidth=1) if v.children: self.ax.plot([self.node_positions[v][0], self.node_positions[v][0]], [self.node_positions[v.children[0]][1], self.node_positions[v.children[-1]][1]], color='black', linestyle='-', linewidth=1) def draw_nodes(self): for v in self.tree.preorder(): if not v.parent and not v.event: self.draw_root(*self.node_positions[v]) elif v.children: if v.event == 'D': self.draw_dupl(*self.node_positions[v]) elif v.event == 'S': self.draw_spec(*self.node_positions[v]) elif v.event == 'H': self.draw_hgt(*self.node_positions[v]) elif v.event == 'GC': self.draw_gc(*self.node_positions[v]) else: if v.event == 'L': self.draw_loss(*self.node_positions[v]) else: x, y = self.node_positions[v] self.draw_leaf(x, y, color=self.colors[v.label]) if not self.species_info: text = str(v.label) elif hasattr(v, 'reconc'): text = '{} <{}>'.format(v.label, v.reconc) self.write_label(x+self.symbolsize+0.02, y, text) def draw_leaf(self, x, y, color='white', leftalign=False): if leftalign: x += self.symbolsize/2 fill = mpatches.Circle((x, y), self.symbolsize/2, color=color, fill=True, zorder=self.symbol_zorder) self.ax.add_patch(fill) outer = mpatches.Circle((x, y), self.symbolsize/2, color='black', fill=False, lw=self.symbolsize/self.symbollw, zorder=self.symbol_zorder) self.ax.add_patch(outer) little = mpatches.Circle((x, y), self.symbolsize/8, color='black', fill=True, zorder=self.symbol_zorder) self.ax.add_patch(little) def draw_loss(self, x, y): self.ax.plot([x,x], [y-self.symbolsize/2, y+self.symbolsize/2], color='black', linestyle='-', linewidth=1) def draw_root(self, x, y, rightalign=False): if rightalign: x -= self.symbolsize/2 fill = mpatches.Circle((x, y), self.symbolsize/2, color='white', fill=True, zorder=self.symbol_zorder) self.ax.add_patch(fill) outer = mpatches.Circle((x, y), self.symbolsize/2, color='black', fill=False, lw=self.symbolsize/self.symbollw, zorder=self.symbol_zorder) self.ax.add_patch(outer) little = mpatches.Circle((x, y), self.symbolsize/5, color='black', fill=False, lw=self.symbolsize/self.symbollw, zorder=self.symbol_zorder) self.ax.add_patch(little) def draw_spec(self, x, y): fill = mpatches.Circle((x, y), self.symbolsize/2, color='black', fill=True, zorder=self.symbol_zorder) self.ax.add_patch(fill) def draw_dupl(self, x, y): square = mpatches.Rectangle((x-self.symbolsize/2, y-self.symbolsize/2), width=self.symbolsize, height=self.symbolsize, color='white', fill=True, zorder=self.symbol_zorder) self.ax.add_patch(square) border = mpatches.Rectangle((x-self.symbolsize/2, y-self.symbolsize/2), width=self.symbolsize, height=self.symbolsize, color='black', fill=False, lw=self.symbolsize/self.symbollw, zorder=self.symbol_zorder) self.ax.add_patch(border) def draw_hgt(self, x, y): coord = np.asarray([[x,y-self.symbolsize/2], [x+self.symbolsize/2,y+self.symbolsize/2], [x-self.symbolsize/2,y+self.symbolsize/2]]) inner = mpatches.Polygon(coord, closed=True, color='white', fill=True, zorder=self.symbol_zorder) self.ax.add_patch(inner) outer = mpatches.Polygon(coord, closed=True, color='black', fill=False, lw=self.symbolsize/self.symbollw, zorder=self.symbol_zorder) self.ax.add_patch(outer) def draw_gc(self, x, y): coord = [] R = self.symbolsize/2 for i in range(5): coord.append([x + 0.5 * R * np.cos(np.radians(54+i*72)), y - 0.5 * R * np.sin(np.radians(54+i*72))]) coord.append([x + R * np.cos(np.radians(90+i*72)), y - R * np.sin(np.radians(90+i*72))]) inner = mpatches.Polygon(coord, closed=True, color='black', fill=True, zorder=self.symbol_zorder) self.ax.add_patch(inner) def write_label(self, x, y, text): self.ax.text(x, y, text, fontsize=self.fontsize, horizontalalignment='left', verticalalignment='center')Methods
def assign_positions(self)-
Expand source code
def assign_positions(self): ymax = (self.leaf_counter-1)/self.leafs_per_vertical_unit self.ax.set_ylim(ymax+0.1, -self.symbolsize*0.6) yposition = 0 for v in self.tree.postorder(): if not v.children: self.node_positions[v] = (self.distance_dict[v], yposition) yposition += 1/self.leafs_per_vertical_unit else: ymean = (self.node_positions[v.children[0]][1] + self.node_positions[v.children[-1]][1])/2 self.node_positions[v] = (self.distance_dict[v], ymean) def draw(self, distance_mode='evolutionary', save_as=False)-
Draw the tree and optionally save it to file.
Parameters
distance_mode:str, optional- Information that is used for the length of the species edges. Currently only 'evolutionary' is supported, in which case the 'dist' attribute is used.
save_as:str, optional- Save the image to a file. The default is False, in which case the image is not saved.
Expand source code
def draw(self, distance_mode='evolutionary', save_as=False): """Draw the tree and optionally save it to file. Parameters ---------- distance_mode : str, optional Information that is used for the length of the species edges. Currently only 'evolutionary' is supported, in which case the 'dist' attribute is used. save_as : str, optional Save the image to a file. The default is False, in which case the image is not saved. """ self.fig, self.ax = plt.subplots() self.ax.set_aspect('equal') self.ax.invert_yaxis() self.initial_traversal(distance_mode) # self.assign_colors() self.assign_positions() self.draw_edges() self.draw_nodes() self.ax.axvline(x=0, linewidth=1, color='grey', linestyle='--') self.ax.set_yticks([]) self.ax.spines['top'].set_visible(False) self.ax.spines['left'].set_visible(False) self.ax.spines['right'].set_visible(False) self.ax.tick_params(axis='x', labelsize=self.fontsize) xmin, xmax = self.ax.get_xlim() ymin, ymax = self.ax.get_ylim() self.fig.set_size_inches(5*abs(xmax-xmin), 5*abs(ymax-ymin)+0.4) plt.tight_layout() if save_as: plt.savefig(save_as, dpi=300) else: plt.show() def draw_dupl(self, x, y)-
Expand source code
def draw_dupl(self, x, y): square = mpatches.Rectangle((x-self.symbolsize/2, y-self.symbolsize/2), width=self.symbolsize, height=self.symbolsize, color='white', fill=True, zorder=self.symbol_zorder) self.ax.add_patch(square) border = mpatches.Rectangle((x-self.symbolsize/2, y-self.symbolsize/2), width=self.symbolsize, height=self.symbolsize, color='black', fill=False, lw=self.symbolsize/self.symbollw, zorder=self.symbol_zorder) self.ax.add_patch(border) def draw_edges(self)-
Expand source code
def draw_edges(self): for v in self.tree.preorder(): if v.parent: if hasattr(v, 'transferred') and v.transferred: color = 'red' else: color = 'black' self.ax.plot([self.node_positions[v.parent][0], self.node_positions[v][0]], [self.node_positions[v][1], self.node_positions[v][1]], color=color, linestyle='-', linewidth=1) if v.children: self.ax.plot([self.node_positions[v][0], self.node_positions[v][0]], [self.node_positions[v.children[0]][1], self.node_positions[v.children[-1]][1]], color='black', linestyle='-', linewidth=1) def draw_gc(self, x, y)-
Expand source code
def draw_gc(self, x, y): coord = [] R = self.symbolsize/2 for i in range(5): coord.append([x + 0.5 * R * np.cos(np.radians(54+i*72)), y - 0.5 * R * np.sin(np.radians(54+i*72))]) coord.append([x + R * np.cos(np.radians(90+i*72)), y - R * np.sin(np.radians(90+i*72))]) inner = mpatches.Polygon(coord, closed=True, color='black', fill=True, zorder=self.symbol_zorder) self.ax.add_patch(inner) def draw_hgt(self, x, y)-
Expand source code
def draw_hgt(self, x, y): coord = np.asarray([[x,y-self.symbolsize/2], [x+self.symbolsize/2,y+self.symbolsize/2], [x-self.symbolsize/2,y+self.symbolsize/2]]) inner = mpatches.Polygon(coord, closed=True, color='white', fill=True, zorder=self.symbol_zorder) self.ax.add_patch(inner) outer = mpatches.Polygon(coord, closed=True, color='black', fill=False, lw=self.symbolsize/self.symbollw, zorder=self.symbol_zorder) self.ax.add_patch(outer) def draw_leaf(self, x, y, color='white', leftalign=False)-
Expand source code
def draw_leaf(self, x, y, color='white', leftalign=False): if leftalign: x += self.symbolsize/2 fill = mpatches.Circle((x, y), self.symbolsize/2, color=color, fill=True, zorder=self.symbol_zorder) self.ax.add_patch(fill) outer = mpatches.Circle((x, y), self.symbolsize/2, color='black', fill=False, lw=self.symbolsize/self.symbollw, zorder=self.symbol_zorder) self.ax.add_patch(outer) little = mpatches.Circle((x, y), self.symbolsize/8, color='black', fill=True, zorder=self.symbol_zorder) self.ax.add_patch(little) def draw_loss(self, x, y)-
Expand source code
def draw_loss(self, x, y): self.ax.plot([x,x], [y-self.symbolsize/2, y+self.symbolsize/2], color='black', linestyle='-', linewidth=1) def draw_nodes(self)-
Expand source code
def draw_nodes(self): for v in self.tree.preorder(): if not v.parent and not v.event: self.draw_root(*self.node_positions[v]) elif v.children: if v.event == 'D': self.draw_dupl(*self.node_positions[v]) elif v.event == 'S': self.draw_spec(*self.node_positions[v]) elif v.event == 'H': self.draw_hgt(*self.node_positions[v]) elif v.event == 'GC': self.draw_gc(*self.node_positions[v]) else: if v.event == 'L': self.draw_loss(*self.node_positions[v]) else: x, y = self.node_positions[v] self.draw_leaf(x, y, color=self.colors[v.label]) if not self.species_info: text = str(v.label) elif hasattr(v, 'reconc'): text = '{} <{}>'.format(v.label, v.reconc) self.write_label(x+self.symbolsize+0.02, y, text) def draw_root(self, x, y, rightalign=False)-
Expand source code
def draw_root(self, x, y, rightalign=False): if rightalign: x -= self.symbolsize/2 fill = mpatches.Circle((x, y), self.symbolsize/2, color='white', fill=True, zorder=self.symbol_zorder) self.ax.add_patch(fill) outer = mpatches.Circle((x, y), self.symbolsize/2, color='black', fill=False, lw=self.symbolsize/self.symbollw, zorder=self.symbol_zorder) self.ax.add_patch(outer) little = mpatches.Circle((x, y), self.symbolsize/5, color='black', fill=False, lw=self.symbolsize/self.symbollw, zorder=self.symbol_zorder) self.ax.add_patch(little) def draw_spec(self, x, y)-
Expand source code
def draw_spec(self, x, y): fill = mpatches.Circle((x, y), self.symbolsize/2, color='black', fill=True, zorder=self.symbol_zorder) self.ax.add_patch(fill) def initial_traversal(self, distance_mode)-
Expand source code
def initial_traversal(self, distance_mode): xmax = 0.0 for v in self.tree.preorder(): if distance_mode == 'evolutionary': if not v.parent: self.distance_dict[v] = 0.0 else: self.distance_dict[v] = self.distance_dict[v.parent] + v.dist if self.distance_dict[v] > xmax: xmax = self.distance_dict[v] elif distance_mode == 'divergence time': raise ValueError('divergence time not yet implemented') else: raise ValueError(f"distance mode '{distance_mode}' not supported") self.ax.set_xlim(-0.1,xmax+0.5) def write_label(self, x, y, text)-
Expand source code
def write_label(self, x, y, text): self.ax.text(x, y, text, fontsize=self.fontsize, horizontalalignment='left', verticalalignment='center')