I do a lot of work in phylogenetics, which means that for just about every paper I’ve written I’ve had at least one figure that is a phylogenetic tree. Making pretty looking trees for a publication is tedious and my previous workflow involved using ARB for actually drawing the tree and producing an initial file in postscript, and then loading that into Adobe Illustrator to make everything beautiful.
The problem with this is that it is not an automated process so any time I need to change the tree I need to redo all of the ‘beautifying’ manually. Recently I coded up an alternative approach using the excellent ete package for python to draw trees exactly how I want.
One of the nicest things about drawing trees in ARB is that you can collapse clades into wedges. Unfortunately, while ete does allow you to collapse clades it doesn’t provide a way to show the collapsed node as a wedge, the only options are a square or a circle. But there is the option to create a custom face which is exactly what I did. Below is a function to create ARB-style wedges:
def polygon_name_face(node, width, height, width_percent):
"""create a wedge shaped face in the style of ARB
width (int): size in pixels for the width of the wedge
height (int): size in pixels for the height of the wedge
width_percent (float): change the angle of the point of the wedge.
This must be a number between 0 and 1
QGraphicsRectItem: The Qt graphics item of the polygon
points = [
(0.0, 0.0), # top left point
(width, 0.0), # top right point
(width * width_percent, height), # bottom right point
(0.0, height), # bottom left point
(0.0, 0.0) # back to the beginning
shape = QPolygonF()
for i in points:
shape << QtCore.QPointF(*i)
## Creates a main master Item that will contain all other elements
## Items can be standard QGraphicsItem
masterItem = QGraphicsRectItem(0, 0, width, height)
# Keep a link within the item to access node info
masterItem.node = node
# I dont want a border around the masterItem
polygon = QGraphicsPolygonItem(shape, masterItem)
# Make the wedge grey in color
# Print the name of the node
text = QGraphicsSimpleTextItem(node.name)
# Center text according to masterItem size
tw = text.boundingRect().width()
th = text.boundingRect().height()
center = masterItem.boundingRect().center()
text.setPos(center.x() + tw/2, center.y() - th/2)
And then to actually use it in a script, set up the tree style. I like to mark internal nodes with bootstrap support >70% with a grey circle and >90% with a black circle as well. Below is the function that I use to add in the groups.
style = NodeStyle()
style['shape'] = 'circle'
if node.support >= .90:
style['size'] = 5
style['fgcolor'] = 'black'
elif node.support >= .70:
style['size'] = 5
style['fgcolor'] = 'grey'
style['size'] = 0
if node in grouping_nodes:
style['draw_descendants'] = False
# Create an ItemFAce. First argument must be the pointer to
# the constructor function that returns a QGraphicsItem. It
# will be used to draw the Face. Next arguments are arbitrary,
# and they will be forwarded to the constructor Face function.
# in this case we pass through the width, height, and width_percent for
# the wedge.
F = faces.DynamicItemFace(polygon_name_face, 60, 30, 0.25)
faces.add_face_to_node(F, node, 0)
Finally putting it all together
from ete3 import Tree, faces, NodeStyle, TreeStyle
# We will need to create Qt4 items for making our custom polygon
from PyQt4 import QtCore
from PyQt4.QtGui import QGraphicsRectItem, QGraphicsSimpleTextItem, \
QGraphicsPolygonItem, QPolygonF, QColor, QPen, QBrush
# Populate this list with the root node of a clade
# that should be turned into a wedge
grouping_nodes = 
# load in your tree from somewhere, this is for fake data
t = Tree()
ancestor = t.get_common_ancestor("aaaaaaaaa", "aaaaaaaaac")
ts = TreeStyle()
ts.layout_fn = master_ly
# order the subtrees in ascending order
The default ete style
my style with some of the nodes grouped
There are improvements to be made with the way I’m drawing the wedge. First, there isn’t any border between the top of the wedge and the next leaf — you can see the name “aaaaaaaaad” is a bit cramped. Second, ARB has a nice feature which changes the wedge dimensions based on the number of grouped leaves which I haven’t yet implemented.