agentsclimarketplace

Tree visualization

Skill FridrichMethod/awesome-skills/skills/tree-visualization

Curated, auto-synced collection of 2,000+ Claude Code & Codex skills for AI4Protein, bioinformatics, AI development, and academic paper writing. One curl command installs them all.

Install
npx -y skills add FridrichMethod/awesome-skills --skill tree-visualization

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 11 stars11 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Draw and export phylogenetic trees with Bio.Phylo plus matplotlib, and route rich figures to ggtree, ETE4, or iTOL. Covers why a tree figure is an argument not a neutral picture, the cladogram-vs-phylogram-vs-chronogram choice that hides or reveals rate and time, how ladderization manufactures a false arrow of progress, why an unlabeled support number always flatters the result (bootstrap vs posterior vs SH-aLRT vs UFBoot are different scales), why Bio.Phylo silently drops BEAST HPD bars so annotated Bayesian trees must go through treeio plus ggtree, and the tip-count and raster-vs-vector thresholds for legible publication figures. Use when drawing a tree, choosing a layout, coloring branches, showing or labeling support, exporting vector figures, or deciding a drawing tool. Routes annotation-preserving reads to tree-io, and rooting and ladderizing to tree-manipulation.

SKILL.md

20.4 KB, as published. Nobody here has run it

Version Compatibility

Reference examples tested with: BioPython 1.83+, matplotlib 3.8+. Rich-figure alternatives: ggtree 3.12+ / treeio 1.28+ / ggtreeExtra 1.14+ (Bioconductor), ete4 4.x (Python), iTOL v6 (web), FigTree (desktop).

Before using code patterns, verify installed versions match. If versions differ:

  • Python: pip show biopython then help(Phylo.draw) to check signatures
  • R: packageVersion('ggtree') then ?ggtree to verify layout and geom arguments

If code throws ImportError, AttributeError, or TypeError, introspect the installed package and adapt the example to match the actual API rather than retrying.

Bio.Phylo draw() renders rectangular layouts only and parses no BEAST/MrBayes annotation block; circular/fan/unrooted layouts and HPD bars require ggtree, ETE4, or iTOL. The ETE3->ETE4 migration changed APIs; introspect rather than assume ETE3 signatures.

Tree Visualization -- A Figure Is an Argument

"Draw and export my tree figure" -> Render a topology under a chosen layout and branch-length encoding, decide which support to show and how to label it, and export in a format that survives print.

  • Python quick look: Phylo.draw(tree, axes=ax) (Bio.Phylo + matplotlib)
  • Publication with metadata, rich support, or BEAST HPD bars: ggtree + treeio (R)

Scope: drawing, styling, annotating, and exporting tree figures, and choosing the drawing tool. Reading/converting files and preserving [&...] annotations before plotting -> tree-io. Rooting, ladderizing, pruning, collapsing low-support nodes -> tree-manipulation. ggplot2 grammar, themes, and ggsave underlying ggtree -> data-visualization/ggplot2-fundamentals. Composing a tree plus aligned panels into one figure -> data-visualization/multipanel-figures.

The Single Most Important Modern Insight

A phylogenetic tree figure is a rhetorical and modeling artifact, not a neutral picture of the data. A Newick or Nexus file encodes a topology, optionally branch lengths, optionally one node label; it does NOT encode which child is drawn on top, whether the horizontal axis means evolutionary distance or just nesting depth, where the root sits, the aspect ratio, or which of several support measures a bare "95" refers to. Every one of those is supplied by the person drawing the tree, so the figure is the data filtered through a dozen interpretive choices the reader cannot see. Four argument-level decisions dominate, and the same file can be drawn to tell mutually contradictory stories that are all technically correct renderings of one topology:

  1. The geometry is a claim about what the lengths mean. A cladogram asserts only nesting; a phylogram asserts nesting plus amount of change (substitutions per site); a chronogram asserts timing and demands a clock model plus calibrations. Drawing a phylogram as a cladogram erases rate variation, the very signal that flags long-branch attraction; drawing untrusted branch lengths as a phylogram fabricates a quantitative claim.
  2. Ladderization implies a directionality that is not there. Sibling subclades are unordered, so any node rotation is the same tree, but the eye reads top-to-bottom ordering as a march of progress toward the bottom-most tip. Rotation can also bury non-monophyly: a paraphyletic group can be rotated to look contiguous, or a clean clade made to look scattered.
  3. Which support is shown, and whether it is labeled, changes the conclusion. Support is not accuracy, and the measures are not interchangeable: a posterior of 0.95 is generally weaker evidence than a bootstrap of 95 for the same data, and UFBoot 95, SH-aLRT 80, and TBE live on different scales. A bare integer with no legend is the quietest lie because the reader assumes bootstrap and the ambiguity always flatters the result.
  4. The drawing tool is decided by the I/O layer, not the surrounding language. If the tree carries BEAST/MrBayes HPD intervals and posteriors, only a tool whose data model can carry those annotations (treeio + ggtree) can plot them; Bio.Phylo flattens the tree to topology + length + one label and silently drops the uncertainty that was the result.

The skill's job is to make these choices conscious and DECLARE them in the caption, not to default into them. Default ladderization plus cladogram-ish rendering plus unlabeled nodes is the path of least resistance, and it is an argument the user did not intend to make.

Tool Taxonomy

The central decision is which drawing tool to reach for, and it is coupled to the I/O decision: a tool that flattens the tree to topology plus one label cannot draw BEAST HPD bars or posteriors it cannot represent. The plotting path through treeio + ggtree is the same I/O choice made in tree-io.

Tool (lang)CitationLayouts and annotation powerWhen to choose
Bio.Phylo + matplotlib (Py)Talevich 2012rectangular phylogram/cladogram only; one label per node via label_func/branch_labels; no circular/unrooted, no BEAST-block parsea fast scripted look inside a Python pipeline; headless/CI rendering of simple trees; NOT publication figures with metadata or HPD bars
ggtree + treeio + ggtreeExtra (R)Yu 2017; Wang 2020; Xu 2021rectangular, slanted, circular, fan, radial/unrooted; %<+% attaches a data.frame, geom_tiplab/geom_nodelab/geom_cladelab, gheatmap, geom_facet (Cartesian) or geom_fruit rings (circular); treeio reads BEAST/MrBayes/IQ-TREE so HPD bars and posterior plot directlythe publication standard whenever a figure integrates metadata, rich/dual support, HPD bars, or aligned panels; the only clean path for BEAST HPD bars and posterior
ETE4 (Py; ETE3 paper)Huerta-Cepas 2016rectangular and circular; programmatic per-node NodeStyle/faces and layout functions; integrated NCBI taxonomystyling thousands of nodes by rule, taxonomy-driven annotation, or rendering many trees headlessly in Python
iTOL v6 (web)Letunic 2024rectangular, circular, unrooted, very large trees; tab-delimited dataset templates for color strips, heatmaps, bars, binary symbols, clade collapse; SVG/PDF/PNG/EPS exportlarge trees and template-based metadata with no code; collaborative web-driven polished figures
FigTree (desktop GUI)Rambaut (software)rectangular, polar, radial; reads BEAST NEXUS node annotations to display HPD bars and posterior on screen; manual rotate/collapse/colorinteractive inspection of a single BEAST/MrBayes tree before scripting the final figure; not reproducible, use for exploration not the pipeline

Decision rule: large tree or no-code web annotation -> iTOL v6. Publication figure with metadata, dual support, HPD bars, or aligned heatmaps -> ggtree + treeio (+ ggtreeExtra for circular rings). Programmatic styling of thousands of nodes or NCBI-taxonomy annotation -> ETE4. Fast topology look from a Python script -> Bio.Phylo. Interactive rooting/inspection of one BEAST tree -> FigTree, then move the reproducible figure to ggtree. The recurring mistake is defaulting to Bio.Phylo because the pipeline is Python, then discovering it cannot draw a circular layout, place a heatmap, or show the BEAST HPD bars that are the whole point.

What Each Layout Reveals and Hides

LayoutRevealsHides or distortsGood forBad for
Rectangular phylogrambranch lengths read directly off a linear axis; honest distance comparisontip labels overplot past a few hundred tips; uses vertical spacethe default when length matters and tip count is moderate (<~150)very large trees
Slanted / triangularcompactness; quick topology readthe diagonal implies a ladder/progression; imprecise length readingquick schematic topology viewsprecise branch-length comparison
Circular / fanhundreds to thousands of tips in one panel; metadata ringsradial distortion -- the same length subtends a larger arc near the rim, so distance is perceptually compressed near the root; near-root structure crampedbig trees where the message is broad structure plus aligned metadataquantitative branch-length comparison
Unrooted / radialhonestly shows NO assumed root or time direction; overall shape and long-branch outliersno time direction; clade membership harder to trace; clusters over-read as cladesexploratory views, "no root committed"directional stories; any "basal/early-diverging" claim
Chronogram (time-tree)node timing on a time axisrequires a clock model and calibrations the reader must trust; without HPD bars timing looks falsely precisedated BEAST/treePL/MCMCtree trees where timing is the messageany tree lacking a clock; never draw without age uncertainty bars

Circular is still a ROOTED tree bent into a ring (center = root = past); unrooted/radial explicitly refuses a root. Presenting an unrooted radial tree and narrating "X is basal" is a contradiction.

Bio.Phylo + matplotlib Recipes

Quick text and ASCII inspection, no figure needed:

from Bio import Phylo

tree = Phylo.read('tree.nwk', 'newick')
print(tree)                  # indented text summary
Phylo.draw_ascii(tree)       # ASCII-art diagram, useful in a terminal or log

Draw to a vector file (always pass an axes and do_show=False for headless/scripted use):

from Bio import Phylo
import matplotlib.pyplot as plt

tree = Phylo.read('tree.nwk', 'newick')
tree.ladderize()             # legibility only; ordering carries NO phylogenetic meaning -- say so in the caption

fig, ax = plt.subplots(figsize=(10, 8))
Phylo.draw(tree, axes=ax, do_show=False)
ax.set_title('Phylogenetic tree (phylogram, branch length = subs/site)')
fig.savefig('tree.pdf', bbox_inches='tight')   # vector: text and lines stay sharp at any size
plt.close(fig)

Label tips, and show support with its measure named (never a bare integer):

def tip_only(clade):
    return clade.name if clade.is_terminal() else ''

def support_label(clade):
    # the measure MUST be stated in the legend/caption; here values are bootstrap percentages
    if not clade.is_terminal() and clade.confidence is not None:
        return f'{clade.confidence:.0f}'
    return ''

fig, ax = plt.subplots(figsize=(12, 10))
Phylo.draw(tree, axes=ax, do_show=False, label_func=tip_only, branch_labels=support_label)
ax.set_title('Bootstrap support shown at internal nodes')
fig.savefig('supported_tree.svg', bbox_inches='tight')
plt.close(fig)

Color branches by group (convert to phyloXML for native color support):

from Bio.Phylo.PhyloXML import BranchColor

xtree = tree.as_phyloxml()                     # phyloXML carries branch color through draw()
for clade in xtree.find_clades():
    if clade.name and clade.name.startswith('Homo'):
        clade.color = BranchColor.from_name('red')

fig, ax = plt.subplots(figsize=(10, 8))
Phylo.draw(xtree, axes=ax, do_show=False)
fig.savefig('colored_tree.pdf', bbox_inches='tight')
plt.close(fig)

Scale the panel to tip count so labels stay legible, and drop the axis frame:

n_tips = len(tree.get_terminals())
height = max(8, n_tips * 0.25)                  # ~0.25 in/tip keeps ~6-8 pt labels from colliding

fig, ax = plt.subplots(figsize=(10, height))
Phylo.draw(tree, axes=ax, do_show=False)
ax.axis('off')
fig.savefig('scaled_tree.pdf', bbox_inches='tight')
plt.close(fig)

For circular/fan/unrooted layouts, metadata heatmaps, dual support, or BEAST HPD bars, Bio.Phylo cannot help -- route to ggtree + treeio (R), ETE4, or iTOL.

Per-Method Failure Modes

Drawing Meaningless Branch Lengths as a Phylogram

Trigger: Rendering a constraint tree, a supertree, or a tree whose lengths are non-comparable with the horizontal axis proportional to length. Mechanism: The phylogram geometry asserts a quantitative claim about evolutionary distance that the lengths do not support. Symptom: A topology-only or arbitrary-length tree appears to make precise distance statements. Fix: Draw as a cladogram and SAY so (branch.length='none' in ggtree); or, if lengths are real, keep them and state the unit (subs/site vs time) plus a scale bar.

Unlabeled or Mislabeled Support

Trigger: A node shows "98" with no legend, or two measures are printed without saying which is which. Mechanism: The reader defaults to assuming bootstrap, but it may be a posterior (much weaker for the same number), an SH-aLRT (cutoff 80), a UFBoot (cutoff 95, not the BP-70 scale), or a TBE. Symptom: A weakly resolved bush reads as a confident comb because the displayed number is over-read. Fix: Always state the measure(s) and their order (e.g. "SH-aLRT/UFBoot" at each node); collapse nodes below threshold into polytomies rather than drawing fake resolution, since bootstrap, posterior, SH-aLRT, and UFBoot sit on different scales.

Tip-Label Overplotting on Large Trees

Trigger: A rectangular layout past a few hundred tips. Mechanism: Horizontal labels collide into an unreadable black band; authors then shrink the font to illegibility or silently drop labels. Symptom: Tip labels are unreadable or missing. Fix: Rotate labels, switch to circular/fan with radial labels, collapse uninformative clades, annotate by colored strips/rings (ggtreeExtra, iTOL datasets) instead of text, or move to iTOL which is built for large trees.

Non-Monophyly Hidden by Ladderization or Rotation

Trigger: Rotating nodes to make a paraphyletic or polyphyletic group look contiguous (or a clade look scattered). Mechanism: Node rotation is information-free with respect to the tree, but the eye reads contiguity as a clade and ordering as direction. Symptom: A group that is not monophyletic appears unified, or a directional narrative is implied. Fix: Color by group and let the topology speak; never narrate ordering as meaning; state "tips ladderized for legibility, ordering carries no phylogenetic meaning."

Chronogram Without Age Uncertainty

Trigger: Drawing a BEAST/MrBayes time-tree with point-estimate node ages and no HPD bars. Mechanism: The 95% HPD intervals on node heights ARE the result; omitting them asserts false precision. Symptom: Node ages look known to the day; a reviewer asks where the credible intervals went. Fix: Draw the HPD bars via treeio read.beast() -> ggtree geom_range('height_0.95_HPD') (introspect the column name; it varies by source program and treeio version), or enable node bars in FigTree; never draw an annotated Bayesian tree with Bio.Phylo, which drops the annotations silently.

Raster Export for Publication

Trigger: Saving a tree as a 150-dpi PNG for a paper. Mechanism: Raster pixelates thin branches and small tip labels at print size. Symptom: Fuzzy labels and pixelated branches; journal rejection or blurry print. Fix: Export SVG/PDF/EPS (vector) so text and lines stay sharp at any scale; only if forced to rasterize, do so at final size with >=600 dpi for line art.

Quantitative Thresholds

These are operational defaults for a standard portrait panel with ~6-8 pt labels; adapt to font, page, and journal specs.

QuantityThresholdRationale / source
Tips, rectangular horizontal labels comfortable<=~50labels do not collide at legible font
Tips, start rotating/shrinking labels~50-150label height x tip count approaches panel height
Tips, switch to circular/fan or collapse clades~150-500rectangular labels collide; radial labels recover space
Tips, per-tip text impractical; use strips/rings>~500-1000annotate by color/groups (iTOL, ggtreeExtra); iTOL is built for large trees
Publication exportvector (SVG/PDF/EPS)text and lines stay sharp at any scale; the default
Forced raster, line-art/tree>=600 dpi (many journals 600-1200)~300 dpi is the photo floor but too coarse for thin branches and small labels
Scale bar on any phylogrammandatory (subs/site or a time axis)without it the reader cannot recover true distances under aspect-ratio distortion
Bootstrap strong>=95 (>=70 moderate)Hillis-Bull heuristic; state the measure (Felsenstein 1985)
UFBoot strong>=95 (not the BP-70 scale)recalibrated; IQ-TREE joint rule SH-aLRT>=80 AND UFBoot>=95
SH-aLRT strong>=80IQ-TREE recommendation
Posterior probability strong>=0.95, but WEAKER than bootstrap 95PP systematically higher for the same data; do not equate

Lock the branch-length scale; do not let the figure engine non-uniformly stretch a phylogram to fill a fixed panel, which distorts the lengths the figure exists to communicate.

Common Errors

Error / symptomCauseSolution
BEAST HPD bars absent from a Python figuredrew an annotated tree with Bio.Phyloroute through treeio read.beast + ggtree geom_range
Figure not saving / blankdo_show=True opens a window instead of writingpass do_show=False, then fig.savefig(...)
Branch colors not appearingplain Newick tree has no color slotconvert with tree.as_phyloxml() and set clade.color
Labels overlap into a black bandtoo many tips for rectangular layoutincrease panel height, rotate labels, or switch to circular/iTOL
"Basal" claim on a radial treenarrated an unrooted layout as if rootedroot explicitly (tree-manipulation) and show the root before any directional claim
Support number misreadbare integer with no measure statedlabel the measure(s) and order; collapse sub-threshold nodes to polytomies
Phylo.draw has no circular optionBio.Phylo is rectangular-onlyuse ggtree layout='circular', ETE4, or iTOL

References

Talevich E, Invergo BM, Cock PJA, Chapman BA. 2012. Bio.Phylo: a unified toolkit for processing, analyzing and visualizing phylogenetic trees in Biopython. BMC Bioinformatics 13:209. Yu G, Smith DK, Zhu H, Guan Y, Lam TT-Y. 2017. ggtree: an R package for visualization and annotation of phylogenetic trees with their covariates and other associated data. Methods in Ecology and Evolution 8(1):28-36. Yu G. 2020. Using ggtree to visualize data on tree-like structures. Current Protocols in Bioinformatics 69(1):e96. Wang L-G, Lam TT-Y, Xu S, Dai Z, Zhou L, Feng T, Guo P, Dunn CW, Jones BR, Bradley T, Zhu H, Guan Y, Jiang Y, Yu G. 2020. treeio: an R package for phylogenetic tree input and output with richly annotated and associated data. Molecular Biology and Evolution 37(2):599-603. Xu S, Dai Z, Guo P, Fu X, Liu S, Zhou L, Tang W, Feng T, Chen M, Zhan L, Wu T, Hu E, Jiang Y, Bo X, Yu G. 2021. ggtreeExtra: compact visualization of richly annotated phylogenetic data. Molecular Biology and Evolution 38(9):4039-4042. Huerta-Cepas J, Serra F, Bork P. 2016. ETE 3: reconstruction, analysis, and visualization of phylogenomic data. Molecular Biology and Evolution 33(6):1635-1638. Letunic I, Bork P. 2024. Interactive Tree of Life (iTOL) v6: recent updates to the phylogenetic tree display and annotation tool. Nucleic Acids Research 52(W1):W78-W82. Felsenstein J. 1985. Confidence limits on phylogenies: an approach using the bootstrap. Evolution 39(4):783-791.

Related Skills

  • tree-io - parsing and preserving BEAST/MrBayes/IQ-TREE annotations so they survive into the figure; the tightest coupling
  • tree-manipulation - rooting, ladderizing, pruning, and collapsing low-support nodes before drawing
  • data-visualization/ggplot2-fundamentals - the grammar, themes, and ggsave vector export underlying ggtree
  • data-visualization/multipanel-figures - composing a tree plus aligned metadata panels into one figure

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.