If I create a phylogenetic tree using Biopython Phylo module, it is oriented from left to right, e.g.:
import matplotlib.pyplot as plt
from Bio import Phylo
from io import StringIO
handle = StringIO("(((A,B),(C,D)),(E,F,G));")
tree = Phylo.read(handle, "newick")
fig, ax = plt.subplots(1, 1, figsize=(6, 6))
Phylo.draw(tree, axes=ax, do_show=False)
plt.savefig("./tree_rect.png")
plt.close(fig)
If I now try to convert it to a circular tree using the built-in polar transformation of matplotlib, I obtain:
fig, ax = plt.subplots(subplot_kw=dict(polar=True), figsize=(6, 6))
Phylo.draw(tree, axes=ax, do_show=False)
plt.savefig("./tree_circ.png")
plt.close(fig)
I obtain
that is matplotlib takes y-axis as the radial axis and x-axis as the angle. This is the opposite of what I need for obtaining a correct layout, which I can do, e.g., using ete3:
from ete3 import Tree, TreeStyle
t = Tree("(((A,B),(C,D)),(E,F,G));")
circular_style = TreeStyle()
circular_style.mode = "c"
circular_style.scale = 20
t.render("tree_ete3.png", w=183, units="mm", tree_style=circular_style)
(How) could this be done using Bio.Phylo? I suppose the possibilities are
Phylo.draw
, so that the tree is directed upwardsbut I don't know how to do either (and whether it can be done.)
Related question: Python: How to convert a phylogenetic tree into a circular format?
x
" and "y
" coordinates in a matplotlib polar plot so that a radial phylogenetic tree is correctly branchedBelow, I show how varying a scaling parameter in the code ('Scale'
→ z
) affects the resulting tree, and also provide for comparison the normal Bio.Phylo.draw(tree)
version of the map.
import matplotlib.pyplot as plt
import numpy as np
from Bio import Phylo
from io import StringIO
def transform_coordinates(
clade, r=0, theta=0, delta_theta=np.pi / 32, coordinates=None
):
if coordinates is None:
coordinates = {}
coordinates[clade] = (r, theta)
if clade.is_terminal():
theta += delta_theta
else:
for child in clade:
coordinates, theta = transform_coordinates(
child, r + 1, theta, delta_theta, coordinates
)
return coordinates, theta
def plot_tree(tree, coordinates, scale=1):
fig, ax = plt.subplots(subplot_kw=dict(polar=True), figsize=(6, 6))
for clade in tree.find_clades():
if not clade.is_terminal():
for child in clade:
x1, y1 = coordinates[clade]
x2, y2 = coordinates[child]
ax.plot([y1, y2], [x1, x2], "k-")
if child.is_terminal():
# print(child.name, x2, y2, np.cos(x2), np.cos(y2))
shift_y = 0.2 if np.cos(y2) < 0 else 0.05
ax.annotate(
child.name, xy=(y2 * 1.02, x2 + shift_y), color="red"
)
ax.grid("off")
ax.patch.set_visible(False)
ax.spines["polar"].set_visible(False)
ax.xaxis.grid(False)
ax.xaxis.set_visible(False)
ax.yaxis.grid(False)
ax.yaxis.set_visible(False)
plt.title(f"Scale = {scale}\n")
plt.show()
handle = StringIO("((((A,B),(X,Y,Z),(H,I,J))),(((C,L),(D,K)),(E,F,G)));")
tree = Phylo.read(handle, "newick")
for z in range(12, 36, 4):
coordinates, _ = transform_coordinates(tree.clade, delta_theta=np.pi / z)
plot_tree(tree, coordinates, scale=z)
Note: The swapping of x & y coordinates within plot_tree
:
ax.plot([y1, y2], [x1, x2], "k-")
.