|
| 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") |
0 commit comments