Skip to content

Commit 9547839

Browse files
feat(bokeh): implement tree-phylogenetic (#3102)
## Implementation: `tree-phylogenetic` - bokeh Implements the **bokeh** version of `tree-phylogenetic`. **File:** `plots/tree-phylogenetic/implementations/bokeh.py` **Parent Issue:** #3070 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20620334569)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 174fa5f commit 9547839

2 files changed

Lines changed: 213 additions & 0 deletions

File tree

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
""" pyplots.ai
2+
tree-phylogenetic: Phylogenetic Tree Diagram
3+
Library: bokeh 3.8.1 | Python 3.13.11
4+
Quality: 91/100 | Created: 2025-12-31
5+
"""
6+
7+
from bokeh.io import export_png
8+
from bokeh.models import ColumnDataSource, HoverTool, Label, Legend, LegendItem
9+
from bokeh.plotting import figure, output_file, save
10+
11+
12+
# Phylogenetic tree data - Primate species (mitochondrial DNA based)
13+
# Manually define tree structure with node positions and branch connections
14+
# Structure: ((((Human, Chimp), Gorilla), Orangutan), Gibbon)
15+
# Using rectangular cladogram layout
16+
17+
# Node positions (x = evolutionary distance, y = vertical position)
18+
species = ["Human", "Chimpanzee", "Gorilla", "Orangutan", "Gibbon"]
19+
20+
# Leaf node positions - shifted left to better center the tree
21+
leaf_y = [5, 4, 3, 2, 1]
22+
leaf_x = [0.75, 0.75, 0.65, 0.45, 0.25]
23+
24+
# Internal node positions - shifted left to match
25+
# Node 1: Human-Chimp ancestor
26+
# Node 2: Node1-Gorilla ancestor
27+
# Node 3: Node2-Orangutan ancestor
28+
# Node 4: Root - Node3-Gibbon ancestor
29+
30+
internal_x = [0.60, 0.40, 0.20, 0.00]
31+
internal_y = [4.5, 3.75, 2.875, 1.9375]
32+
33+
# Branch lines (horizontal and vertical segments)
34+
# Format: lists of x and y coordinates for each branch segment
35+
36+
# Horizontal branches from leaves to their ancestors
37+
h_branch_x = [
38+
[internal_x[0], leaf_x[0]], # Human
39+
[internal_x[0], leaf_x[1]], # Chimp
40+
[internal_x[1], leaf_x[2]], # Gorilla
41+
[internal_x[2], leaf_x[3]], # Orangutan
42+
[internal_x[3], leaf_x[4]], # Gibbon
43+
[internal_x[1], internal_x[0]], # Node1 to Node2
44+
[internal_x[2], internal_x[1]], # Node2 to Node3
45+
[internal_x[3], internal_x[2]], # Node3 to Root
46+
]
47+
48+
h_branch_y = [
49+
[leaf_y[0], leaf_y[0]], # Human
50+
[leaf_y[1], leaf_y[1]], # Chimp
51+
[leaf_y[2], leaf_y[2]], # Gorilla
52+
[leaf_y[3], leaf_y[3]], # Orangutan
53+
[leaf_y[4], leaf_y[4]], # Gibbon
54+
[internal_y[0], internal_y[0]], # Node1 to Node2
55+
[internal_y[1], internal_y[1]], # Node2 to Node3
56+
[internal_y[2], internal_y[2]], # Node3 to Root
57+
]
58+
59+
# Vertical branches connecting nodes
60+
v_branch_x = [
61+
[internal_x[0], internal_x[0]], # Node1 vertical (Human-Chimp)
62+
[internal_x[1], internal_x[1]], # Node2 vertical (Node1-Gorilla)
63+
[internal_x[2], internal_x[2]], # Node3 vertical (Node2-Orangutan)
64+
[internal_x[3], internal_x[3]], # Root vertical (Node3-Gibbon)
65+
]
66+
67+
v_branch_y = [
68+
[leaf_y[0], leaf_y[1]], # Node1 vertical
69+
[internal_y[0], leaf_y[2]], # Node2 vertical
70+
[internal_y[1], leaf_y[3]], # Node3 vertical
71+
[internal_y[2], leaf_y[4]], # Root vertical
72+
]
73+
74+
# Create figure with better centered x_range
75+
p = figure(
76+
width=4800,
77+
height=2700,
78+
title="Primate Evolution · tree-phylogenetic · bokeh · pyplots.ai",
79+
x_axis_label="Evolutionary Distance (substitutions per site)",
80+
y_axis_label="",
81+
x_range=(-0.15, 1.05),
82+
y_range=(0.3, 5.7),
83+
)
84+
85+
# Style the figure
86+
p.title.text_font_size = "28pt"
87+
p.xaxis.axis_label_text_font_size = "22pt"
88+
p.xaxis.major_label_text_font_size = "18pt"
89+
p.yaxis.visible = False
90+
p.grid.visible = False
91+
p.outline_line_color = None
92+
93+
# Draw horizontal branches
94+
for hx, hy in zip(h_branch_x, h_branch_y, strict=True):
95+
p.line(hx, hy, line_width=4, line_color="#306998")
96+
97+
# Draw vertical branches
98+
for vx, vy in zip(v_branch_x, v_branch_y, strict=True):
99+
p.line(vx, vy, line_width=4, line_color="#306998")
100+
101+
# Draw leaf nodes with hover tooltips
102+
leaf_source = ColumnDataSource(
103+
data={
104+
"x": leaf_x,
105+
"y": leaf_y,
106+
"species": species,
107+
"type": ["Leaf Node"] * len(species),
108+
"info": [
109+
"Modern human (Homo sapiens)",
110+
"Chimpanzee (Pan troglodytes)",
111+
"Western gorilla (Gorilla gorilla)",
112+
"Bornean orangutan (Pongo pygmaeus)",
113+
"White-handed gibbon (Hylobates lar)",
114+
],
115+
}
116+
)
117+
leaf_scatter = p.scatter(
118+
"x", "y", source=leaf_source, size=24, color="#FFD43B", line_color="#306998", line_width=3, name="leaf_nodes"
119+
)
120+
121+
# Draw internal nodes with hover tooltips
122+
internal_names = ["Human-Chimp Ancestor", "Great Ape Ancestor", "Hominid Ancestor", "Root (Common Ancestor)"]
123+
internal_source = ColumnDataSource(
124+
data={"x": internal_x, "y": internal_y, "type": ["Internal Node"] * len(internal_x), "info": internal_names}
125+
)
126+
internal_scatter = p.scatter("x", "y", source=internal_source, size=18, color="#306998", name="internal_nodes")
127+
128+
# Add hover tool for interactivity
129+
hover = HoverTool(
130+
renderers=[leaf_scatter, internal_scatter], tooltips=[("Type", "@type"), ("Info", "@info")], mode="mouse"
131+
)
132+
p.add_tools(hover)
133+
134+
# Add species labels
135+
for i, sp in enumerate(species):
136+
label = Label(
137+
x=leaf_x[i] + 0.02, y=leaf_y[i], text=sp, text_font_size="20pt", text_baseline="middle", text_color="#333333"
138+
)
139+
p.add_layout(label)
140+
141+
# Add scale bar
142+
scale_bar_y = 0.6
143+
p.line([0, 0.1], [scale_bar_y, scale_bar_y], line_width=4, line_color="#333333")
144+
scale_label = Label(
145+
x=0.0, y=scale_bar_y - 0.15, text="0.1 substitutions/site", text_font_size="16pt", text_color="#333333"
146+
)
147+
p.add_layout(scale_label)
148+
149+
# Add clade annotations with more prominent styling
150+
clade_labels = [
151+
{"x": 0.58, "y": 4.5, "text": "Hominini"},
152+
{"x": 0.38, "y": 3.75, "text": "Homininae"},
153+
{"x": 0.18, "y": 2.875, "text": "Hominidae"},
154+
]
155+
156+
for clade in clade_labels:
157+
bracket_label = Label(
158+
x=clade["x"] - 0.15,
159+
y=clade["y"],
160+
text=clade["text"],
161+
text_font_size="20pt",
162+
text_font_style="italic",
163+
text_color="#444444",
164+
text_baseline="middle",
165+
)
166+
p.add_layout(bracket_label)
167+
168+
# Add legend for node types
169+
legend = Legend(
170+
items=[
171+
LegendItem(label="Extant Species (Leaf Nodes)", renderers=[leaf_scatter]),
172+
LegendItem(label="Ancestral Nodes (Internal)", renderers=[internal_scatter]),
173+
],
174+
location="top_right",
175+
label_text_font_size="18pt",
176+
spacing=10,
177+
padding=15,
178+
background_fill_alpha=0.8,
179+
)
180+
p.add_layout(legend)
181+
182+
# Save PNG
183+
export_png(p, filename="plot.png")
184+
185+
# Save HTML for interactivity
186+
output_file("plot.html")
187+
save(p)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
library: bokeh
2+
specification_id: tree-phylogenetic
3+
created: '2025-12-31T13:54:21Z'
4+
updated: '2025-12-31T14:22:11Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20620334569
7+
issue: 3070
8+
python_version: 3.13.11
9+
library_version: 3.8.1
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/tree-phylogenetic/bokeh/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/tree-phylogenetic/bokeh/plot_thumb.png
12+
preview_html: https://storage.googleapis.com/pyplots-images/plots/tree-phylogenetic/bokeh/plot.html
13+
quality_score: 91
14+
review:
15+
strengths:
16+
- Excellent use of Bokeh interactive features including HoverTool with informative
17+
tooltips showing scientific names
18+
- Clean, professional visual design with appropriate color scheme (Python blue/yellow)
19+
- Well-structured clade annotations with italic styling for taxonomic names
20+
- Scale bar properly indicates evolutionary distance units
21+
- Comprehensive legend explaining node types
22+
weaknesses:
23+
- Legend positioned in far top-right corner could be closer to the tree for better
24+
visual association
25+
- Title format includes descriptive prefix instead of strictly following spec-id
26+
format

0 commit comments

Comments
 (0)