{"spec_id":"tree-phylogenetic","library":"seaborn","language":"python","code":"\"\"\" anyplot.ai\ntree-phylogenetic: Phylogenetic Tree Diagram\nLibrary: seaborn 0.13.2 | Python 3.13.13\nQuality: 94/100 | Updated: 2026-05-15\n\"\"\"\n\nimport os\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport seaborn as sns\nfrom matplotlib.patches import Patch\nfrom scipy.cluster.hierarchy import dendrogram, linkage\nfrom scipy.spatial.distance import squareform\n\n\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\n\n# Okabe-Ito palette for 5 clades\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\"]\n\n# Define primate species for phylogenetic tree\nspecies = [\"Human\", \"Chimpanzee\", \"Gorilla\", \"Orangutan\", \"Gibbon\", \"Baboon\", \"Macaque\", \"Marmoset\", \"Lemur\", \"Tarsier\"]\n\n# Create evolutionary distance matrix (symmetric)\n# Based on approximate mitochondrial DNA divergence (millions of years ago)\nbase_distances = np.array(\n    [\n        [0, 6, 9, 14, 18, 25, 25, 35, 55, 58],  # Human\n        [6, 0, 9, 14, 18, 25, 25, 35, 55, 58],  # Chimpanzee\n        [9, 9, 0, 14, 18, 25, 25, 35, 55, 58],  # Gorilla\n        [14, 14, 14, 0, 18, 25, 25, 35, 55, 58],  # Orangutan\n        [18, 18, 18, 18, 0, 25, 25, 35, 55, 58],  # Gibbon\n        [25, 25, 25, 25, 25, 0, 10, 35, 55, 58],  # Baboon\n        [25, 25, 25, 25, 25, 10, 0, 35, 55, 58],  # Macaque\n        [35, 35, 35, 35, 35, 35, 35, 0, 55, 58],  # Marmoset\n        [55, 55, 55, 55, 55, 55, 55, 55, 0, 50],  # Lemur\n        [58, 58, 58, 58, 58, 58, 58, 58, 50, 0],  # Tarsier\n    ]\n)\n\n# Convert distance matrix to condensed form for hierarchical clustering\ncondensed_distances = squareform(base_distances)\n\n# Perform hierarchical clustering using UPGMA (average linkage)\nlinkage_matrix = linkage(condensed_distances, method=\"average\")\n\n# Map species to clade index for consistent coloring\nclade_mapping = {\n    \"Human\": 0,  # Great Apes -> Okabe-Ito[0]\n    \"Chimpanzee\": 0,\n    \"Gorilla\": 0,\n    \"Orangutan\": 1,  # Lesser Apes -> Okabe-Ito[1]\n    \"Gibbon\": 1,\n    \"Baboon\": 2,  # Old World Monkeys -> Okabe-Ito[2]\n    \"Macaque\": 2,\n    \"Marmoset\": 3,  # New World Monkeys -> Okabe-Ito[3]\n    \"Lemur\": 4,  # Prosimians -> Okabe-Ito[4]\n    \"Tarsier\": 4,\n}\n\nclade_names = [\"Great Apes\", \"Lesser Apes\", \"Old World Monkeys\", \"New World Monkeys\", \"Prosimians\"]\nclade_colors = [IMPRINT[i] for i in range(5)]\n\n# Build color mapping for dendrogram links\nleaf_colors = [IMPRINT[clade_mapping[s]] for s in species]\nn = len(species)\n\n# Create inline link color list for all links in dendrogram\nlink_colors = []\nfor i in range(len(linkage_matrix)):\n    cluster_idx = i\n    left_child = int(linkage_matrix[cluster_idx, 0])\n    right_child = int(linkage_matrix[cluster_idx, 1])\n\n    # Get colors of both children\n    def get_color(node_id):\n        if node_id < n:\n            return leaf_colors[node_id]\n        else:\n            child_idx = int(node_id - n)\n            left_id = int(linkage_matrix[child_idx, 0])\n            right_id = int(linkage_matrix[child_idx, 1])\n            left_color = get_color(left_id)\n            right_color = get_color(right_id)\n            return left_color if left_color == right_color else INK_SOFT\n\n    left_color = get_color(left_child)\n    right_color = get_color(right_child)\n    link_colors.append(left_color if left_color == right_color else INK_SOFT)\n\n# Create figure with theme-adaptive styling\nfig, ax = plt.subplots(figsize=(16, 9), facecolor=PAGE_BG)\n\nsns.set_theme(\n    style=\"ticks\",\n    rc={\n        \"figure.facecolor\": PAGE_BG,\n        \"axes.facecolor\": PAGE_BG,\n        \"axes.edgecolor\": INK_SOFT,\n        \"axes.labelcolor\": INK,\n        \"text.color\": INK,\n        \"xtick.color\": INK_SOFT,\n        \"ytick.color\": INK_SOFT,\n        \"grid.color\": INK,\n        \"grid.alpha\": 0.10,\n        \"legend.facecolor\": ELEVATED_BG,\n        \"legend.edgecolor\": INK_SOFT,\n    },\n)\n\n# Plot dendrogram (phylogenetic tree) with custom clade colors\ndendro = dendrogram(\n    linkage_matrix,\n    labels=species,\n    orientation=\"left\",\n    ax=ax,\n    leaf_font_size=18,\n    link_color_func=lambda k: link_colors[k - n] if k >= n else leaf_colors[k],\n)\n\n# Make branch lines thicker for improved visibility\nfor line_collection in ax.collections:\n    line_collection.set_linewidth(3)\n\n# Style the dendrogram\nax.set_xlabel(\"Evolutionary Distance (Million Years)\", fontsize=20, fontweight=\"bold\", color=INK)\nax.set_title(\"tree-phylogenetic · seaborn · anyplot.ai\", fontsize=24, fontweight=\"bold\", pad=20, color=INK)\n\n# Adjust tick parameters for readability\nax.tick_params(axis=\"x\", labelsize=16, colors=INK_SOFT)\nax.tick_params(axis=\"y\", labelsize=18, colors=INK_SOFT)\n\n# Add subtle grid on x-axis only\nax.grid(axis=\"x\", alpha=0.15, linestyle=\"-\", linewidth=0.8, color=INK_SOFT)\nax.set_axisbelow(True)\n\n# Add scale bar annotation\nax.annotate(\n    \"Scale: branch length = evolutionary distance\",\n    xy=(0.98, 0.02),\n    xycoords=\"axes fraction\",\n    fontsize=14,\n    ha=\"right\",\n    va=\"bottom\",\n    style=\"italic\",\n    color=INK_SOFT,\n)\n\n# Color the species labels based on clade\nfor label in ax.get_yticklabels():\n    species_name = label.get_text()\n    if species_name in clade_mapping:\n        label.set_color(IMPRINT[clade_mapping[species_name]])\n        label.set_fontweight(\"bold\")\n\n# Add legend for clades (positioned to avoid overlap with tree)\nlegend_elements = [Patch(facecolor=IMPRINT[i], edgecolor=\"none\", label=clade_names[i]) for i in range(5)]\nax.legend(\n    handles=legend_elements,\n    loc=\"upper right\",\n    fontsize=14,\n    title=\"Clades\",\n    title_fontsize=16,\n    framealpha=0.9,\n    facecolor=ELEVATED_BG,\n    edgecolor=INK_SOFT,\n)\n\n# Remove top and right spines for cleaner look\nsns.despine(ax=ax, top=True, right=True)\n\n# Adjust layout\nplt.tight_layout()\nplt.savefig(f\"plot-{THEME}.png\", dpi=300, bbox_inches=\"tight\", facecolor=PAGE_BG)\n"}