Skip to content

Commit 966c00d

Browse files
feat(plotly): implement tree-phylogenetic (#3094)
## Implementation: `tree-phylogenetic` - plotly Implements the **plotly** version of `tree-phylogenetic`. **File:** `plots/tree-phylogenetic/implementations/plotly.py` **Parent Issue:** #3070 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20620332029)* --------- 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 3854b23 commit 966c00d

2 files changed

Lines changed: 244 additions & 0 deletions

File tree

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
""" pyplots.ai
2+
tree-phylogenetic: Phylogenetic Tree Diagram
3+
Library: plotly 6.5.0 | Python 3.13.11
4+
Quality: 92/100 | Created: 2025-12-31
5+
"""
6+
7+
import plotly.graph_objects as go
8+
9+
10+
# Primate phylogenetic tree based on mitochondrial DNA divergence
11+
# Newick format: ((((Human:0.1,Chimpanzee:0.1):0.05,Gorilla:0.15):0.1,Orangutan:0.25):0.15,Gibbon:0.4);
12+
13+
# Species and their evolutionary distances from root
14+
species = ["Human", "Chimpanzee", "Gorilla", "Orangutan", "Gibbon"]
15+
16+
# Cumulative distances from root (calculated from tree topology)
17+
# Root -> African_ape (0.15) -> HCG_ancestor (0.1) -> HC_ancestor (0.05) -> Human/Chimp (0.1)
18+
distances_from_root = {
19+
"Human": 0.15 + 0.1 + 0.05 + 0.1, # 0.4
20+
"Chimpanzee": 0.15 + 0.1 + 0.05 + 0.1, # 0.4
21+
"Gorilla": 0.15 + 0.1 + 0.15, # 0.4
22+
"Orangutan": 0.15 + 0.25, # 0.4
23+
"Gibbon": 0.4, # 0.4
24+
}
25+
26+
# Internal node positions (x = distance from root)
27+
internal_x = {"Root": 0.0, "African_ape": 0.15, "HCG_ancestor": 0.25, "HC_ancestor": 0.30}
28+
29+
# Y positions for species (leaf nodes)
30+
species_y = {"Human": 5, "Chimpanzee": 4, "Gorilla": 3, "Orangutan": 2, "Gibbon": 1}
31+
32+
# Y positions for internal nodes (average of children)
33+
internal_y = {
34+
"HC_ancestor": (species_y["Human"] + species_y["Chimpanzee"]) / 2,
35+
"HCG_ancestor": (species_y["Human"] + species_y["Chimpanzee"] + species_y["Gorilla"]) / 3,
36+
"African_ape": (species_y["Human"] + species_y["Chimpanzee"] + species_y["Gorilla"] + species_y["Orangutan"]) / 4,
37+
"Root": 3,
38+
}
39+
40+
# Build edges for rectangular phylogram
41+
edge_x = []
42+
edge_y = []
43+
44+
# Tree structure connections (child, parent)
45+
connections = [
46+
("Human", "HC_ancestor"),
47+
("Chimpanzee", "HC_ancestor"),
48+
("HC_ancestor", "HCG_ancestor"),
49+
("Gorilla", "HCG_ancestor"),
50+
("HCG_ancestor", "African_ape"),
51+
("Orangutan", "African_ape"),
52+
("African_ape", "Root"),
53+
("Gibbon", "Root"),
54+
]
55+
56+
# Create edge traces for rectangular phylogram
57+
for child, parent in connections:
58+
# Get child position
59+
child_x = distances_from_root[child] if child in species else internal_x[child]
60+
child_y = species_y[child] if child in species else internal_y[child]
61+
62+
# Get parent position
63+
parent_x = internal_x[parent]
64+
parent_y = internal_y[parent]
65+
66+
# Horizontal line from child to parent x position
67+
edge_x.extend([child_x, parent_x, None])
68+
edge_y.extend([child_y, child_y, None])
69+
70+
# Vertical line at parent x position
71+
edge_x.extend([parent_x, parent_x, None])
72+
edge_y.extend([child_y, parent_y, None])
73+
74+
# Create figure
75+
fig = go.Figure()
76+
77+
# Add branch lines (edges)
78+
fig.add_trace(
79+
go.Scatter(
80+
x=edge_x, y=edge_y, mode="lines", line={"color": "#306998", "width": 3}, hoverinfo="skip", showlegend=False
81+
)
82+
)
83+
84+
# Add leaf nodes (species)
85+
leaf_x = [distances_from_root[s] for s in species]
86+
leaf_y = [species_y[s] for s in species]
87+
88+
fig.add_trace(
89+
go.Scatter(
90+
x=leaf_x,
91+
y=leaf_y,
92+
mode="markers+text",
93+
marker={"size": 18, "color": "#FFD43B", "line": {"width": 2, "color": "#306998"}},
94+
text=species,
95+
textposition="middle right",
96+
textfont={"size": 20, "color": "#333333"},
97+
hovertemplate="%{text}<br>Distance: %{x:.2f}<extra></extra>",
98+
showlegend=False,
99+
)
100+
)
101+
102+
# Add internal nodes
103+
internal_nodes_x = list(internal_x.values())
104+
internal_nodes_y = [internal_y.get(n, 3) for n in internal_x.keys()]
105+
internal_labels = list(internal_x.keys())
106+
107+
fig.add_trace(
108+
go.Scatter(
109+
x=internal_nodes_x,
110+
y=internal_nodes_y,
111+
mode="markers",
112+
marker={"size": 12, "color": "#306998", "symbol": "circle"},
113+
hovertemplate="%{text}<br>Distance: %{x:.2f}<extra></extra>",
114+
text=internal_labels,
115+
showlegend=False,
116+
)
117+
)
118+
119+
# Add scale bar
120+
scale_bar_y = 0.3
121+
scale_bar_length = 0.1
122+
fig.add_trace(
123+
go.Scatter(
124+
x=[0, scale_bar_length],
125+
y=[scale_bar_y, scale_bar_y],
126+
mode="lines",
127+
line={"color": "#333333", "width": 3},
128+
showlegend=False,
129+
hoverinfo="skip",
130+
)
131+
)
132+
133+
# Scale bar label
134+
fig.add_annotation(
135+
x=scale_bar_length / 2,
136+
y=scale_bar_y - 0.15,
137+
text="0.1 substitutions/site",
138+
showarrow=False,
139+
font={"size": 16, "color": "#333333"},
140+
)
141+
142+
# Update layout
143+
fig.update_layout(
144+
title={
145+
"text": "Primate Evolution · tree-phylogenetic · plotly · pyplots.ai",
146+
"font": {"size": 28, "color": "#333333"},
147+
"x": 0.5,
148+
"xanchor": "center",
149+
},
150+
xaxis={
151+
"title": {"text": "Evolutionary Distance (substitutions per site)", "font": {"size": 22}},
152+
"tickfont": {"size": 18},
153+
"range": [-0.05, 0.55],
154+
"showgrid": True,
155+
"gridwidth": 1,
156+
"gridcolor": "rgba(0,0,0,0.1)",
157+
"zeroline": False,
158+
},
159+
yaxis={
160+
"title": {"text": "", "font": {"size": 22}},
161+
"tickfont": {"size": 18},
162+
"range": [0, 6],
163+
"showticklabels": False,
164+
"showgrid": False,
165+
"zeroline": False,
166+
},
167+
template="plotly_white",
168+
plot_bgcolor="white",
169+
paper_bgcolor="white",
170+
margin={"l": 80, "r": 150, "t": 100, "b": 100},
171+
showlegend=False,
172+
)
173+
174+
# Add clade annotations
175+
fig.add_annotation(
176+
x=0.32,
177+
y=4.5,
178+
text="Hominini",
179+
showarrow=True,
180+
arrowhead=2,
181+
arrowsize=1,
182+
arrowwidth=2,
183+
arrowcolor="#888888",
184+
ax=40,
185+
ay=0,
186+
font={"size": 16, "color": "#555555", "style": "italic"},
187+
)
188+
189+
fig.add_annotation(
190+
x=0.27,
191+
y=3.8,
192+
text="Homininae<br>(African Apes)",
193+
showarrow=True,
194+
arrowhead=2,
195+
arrowsize=1,
196+
arrowwidth=2,
197+
arrowcolor="#888888",
198+
ax=50,
199+
ay=-20,
200+
font={"size": 16, "color": "#555555", "style": "italic"},
201+
)
202+
203+
fig.add_annotation(
204+
x=0.17,
205+
y=2.8,
206+
text="Hominidae<br>(Great Apes)",
207+
showarrow=True,
208+
arrowhead=2,
209+
arrowsize=1,
210+
arrowwidth=2,
211+
arrowcolor="#888888",
212+
ax=50,
213+
ay=-30,
214+
font={"size": 16, "color": "#555555", "style": "italic"},
215+
)
216+
217+
# Save outputs
218+
fig.write_image("plot.png", width=1600, height=900, scale=3)
219+
fig.write_html("plot.html", include_plotlyjs="cdn")
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
library: plotly
2+
specification_id: tree-phylogenetic
3+
created: '2025-12-31T13:53:32Z'
4+
updated: '2025-12-31T14:01:02Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20620332029
7+
issue: 3070
8+
python_version: 3.13.11
9+
library_version: 6.5.0
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/tree-phylogenetic/plotly/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/tree-phylogenetic/plotly/plot_thumb.png
12+
preview_html: https://storage.googleapis.com/pyplots-images/plots/tree-phylogenetic/plotly/plot.html
13+
quality_score: 92
14+
review:
15+
strengths:
16+
- Excellent use of Plotly annotation system with arrows to label taxonomic clades
17+
- Scientifically accurate primate phylogeny with correct topology and plausible
18+
branch lengths
19+
- Clean rectangular phylogram layout with proportional branch lengths
20+
- Scale bar with proper units enhances scientific communication
21+
- Interactive hover tooltips showing species name and evolutionary distance
22+
- Appropriate colorblind-safe color scheme (Python blue/yellow)
23+
weaknesses:
24+
- Grid lines extend into the tree area which could be cleaner
25+
- No legend present, though this is standard for phylogenetic trees

0 commit comments

Comments
 (0)