如果我使用 Biopython Phylo 模块创建系统发育树,它是从左到右定向的,例如:
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)
如果我现在尝试使用 matplotlib 的内置极坐标变换将其转换为圆形树,我会得到:
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)
我得到了
即matplotlib以y轴为径向轴,以x轴为角度。这与我获得正确布局所需的相反,我可以这样做,例如使用 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)
(如何)使用 Bio.Phylo 可以完成此操作?我想可能性是
Phylo.draw
中的布局,使树朝上但我也不知道该怎么做(以及是否可以做到。)
好吧,这里是一个示例,演示了至少正确交换 matplotlib 极坐标图中的“
x
”和“y
”坐标,以便至少树正确分支。需要做进一步的工作来模拟在分支点处绘制极圆圆周(即弧)的部分,就像通常在径向系统发育图中看到的那样。
下面,我展示了代码中不同的缩放参数 (
'Scale'
→ r
) 会影响生成的树,并且还提供了与地图的正常 Bio.Phylo.draw(tree)
版本的比较。
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:
theta = transform_coordinates(
child, r + 1, theta, delta_theta, coordinates
)
return 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.05
shift_x = 0.025
if np.cos(y2) < 0:
shift_y += 0.15
shift_x *= 2
else:
shift_x *= -1
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")
coordinates = {}
for r in range(12, 36, 4):
transform_coordinates(
tree.clade, coordinates=coordinates, delta_theta=np.pi / r
)
plot_tree(tree, coordinates, scale=r)
注意:
plot_tree
内x和y坐标的交换:
ax.plot([y1, y2], [x1, x2], "k-")
。