Examples¶
Three runnable examples that span the package's API surface. Each is shown with a static screenshot of the rendered output and a link to the fully interactive Altair-rendered version, with tooltips, selection bindings, and (for the Kikawa charts) the cohort-toggle legend.
1. Synthetic 8-tip tree — minimum end-to-end¶
The smallest possible call to
tree_annotated_plot.plot:
8 tips in two clades, four titer values per tip, a flat alt.Chart
with strain:N on the y-axis. Useful when you're sanity-checking an
installation or understanding the API at minimum complexity.
The source for this example lives in
examples/synthetic_example.py.
Running it produces examples/data/synthetic_tree.json (the Auspice
tree) and examples/data/synthetic_chart.json (the saved Vega-Lite
chart spec); the CLI invocation below consumes those two files.
Open the interactive chart in a new tab →
Reproduce — command line¶
python examples/synthetic_example.py
tree-annotated-plot \
--tree examples/data/synthetic_tree.json \
--chart examples/data/synthetic_chart.json \
--output examples/data/synthetic_example.html \
--chart-strain-field strain \
--tree-strain-field name \
--branch-length div \
--tree-size 140
Reproduce — Python API¶
This can also be done via the Python API using:
import tree_annotated_plot
# `synthetic_auspice()` returns an Auspice JSON dict; build_chart()
# returns an alt.Chart with strain on y. Both live in
# examples/synthetic_example.py.
out = tree_annotated_plot.plot(
synthetic_auspice(),
build_chart(synthetic_titers()),
chart_strain_field="strain",
tree_strain_field="name",
branch_length="div",
tree_size=140,
)
2. Kikawa H3N2 — vertical layout, real Auspice tree¶
The realistic case: HA neutralization titers for ~50 H3N2 strains
across multiple sera cohorts, paired with the matching Nextstrain
Auspice tree from
jbloomlab/flu-seqneut-2025to2026.
You can view the tree on Nextstrain
or download the
raw Auspice JSON
that this example feeds into tree-annotated-plot.
The chart is a VConcatChart wrapping a FacetChart wrapping a
LayerChart (errorband + median-points), with a strain encoding on
each layer — tree_annotated_plot.plot walks all of it and rewrites
every encoding's sort to the tree's tip order in a single deepcopy.
This example also demonstrates the case the package was designed
for: the chart's strain values and the tree's tip identifiers come
from different fields but join by value. The chart encodes
axis_label:N (a haplotype label like K:S96C,K207Q,V223I); the
tree carries the same label at node_attrs.derived_haplotype.value.
You name them with chart_strain_field and tree_strain_field
independently.
The titer chart on its own (no tree):
Open the interactive chart in a new tab →
With the tree panel added by tree-annotated-plot:
Open the interactive chart in a new tab →
The strain axis is on y, so tree_annotated_plot.plot auto-picks
the vertical layout (tree_location defaults to "left" on a
y-encoded strain): result is an HConcatChart with the tree on the
left, tips flush against the chart's strain labels, and a centered
scale bar at the bottom of the tree panel.
Reproduce — command line¶
python examples/fetch_auspice_data.py
python examples/flu-seqneut-2025to2026_titer_charts.py
tree-annotated-plot \
--tree examples/data/flu-seqneut-2025to2026_H3N2.json \
--chart examples/data/flu-seqneut-2025to2026_H3N2_titers.json \
--chart-strain-field axis_label \
--tree-strain-field derived_haplotype \
--branch-length div \
--tree-size 140 \
--scale-bar \
--branch-length-units substitutions \
--output examples/data/h3n2_combined.json
Reproduce — Python API¶
This can also be done via the Python API using:
out = tree_annotated_plot.plot(
"examples/data/flu-seqneut-2025to2026_H3N2.json",
chart, # built by examples/flu-seqneut-2025to2026_titer_charts.py
chart_strain_field="axis_label",
tree_strain_field="derived_haplotype",
branch_length="div",
tree_size=140,
scale_bar=True,
branch_length_units="substitutions",
)
3. Kikawa H1N1 — horizontal layout, tree below the chart¶
Same data shape, same VConcat(Facet(Layer)) chart structure, but
the chart-builder encodes axis_label on x instead of y. The
strain labels then render at the bottom of the chart panel, so
tree_annotated_plot.plot's default tree_location="bottom" puts
the tree underneath the chart with its tips at the top — flush
against the strain labels above. The output is a VConcatChart.
Branches in the tree grow upward (root at the bottom of the tree
panel, tips at the top) and the scale bar's text rotates 270° to
read parallel to the now-vertical bar.
You can view the H1N1 tree on Nextstrain or download the raw Auspice JSON fed into the example.
This example demonstrates layout auto-detection: nothing about the
call differs from the H3N2 case beyond the chart itself. The package
detects which axis carries chart_strain_field and dispatches.
The titer chart on its own (no tree):
Open the interactive chart in a new tab →
With the tree panel added by tree-annotated-plot:
Open the interactive chart in a new tab →
The chart-builder script puts the cohort legend above the H1N1 faceted chart specifically so that the strain labels land on the chart panel's bottom edge — which is where the tree's tips sit when vconcat'd underneath. For the H3N2 case (strain labels on the left) the legend stays below the chart panel.
Reproduce — command line¶
python examples/fetch_auspice_data.py
python examples/flu-seqneut-2025to2026_titer_charts.py
tree-annotated-plot \
--tree examples/data/flu-seqneut-2025to2026_H1N1.json \
--chart examples/data/flu-seqneut-2025to2026_H1N1_titers.json \
--chart-strain-field axis_label \
--tree-strain-field derived_haplotype \
--branch-length div \
--tree-size 140 \
--scale-bar \
--branch-length-units substitutions \
--output examples/data/h1n1_combined.json
Reproduce — Python API¶
This can also be done via the Python API using:
out = tree_annotated_plot.plot(
"examples/data/flu-seqneut-2025to2026_H1N1.json",
chart, # H1N1 chart; strain encoded on x
chart_strain_field="axis_label",
tree_strain_field="derived_haplotype",
branch_length="div",
tree_size=140,
scale_bar=True,
branch_length_units="substitutions",
)
Notes¶
Input chart format: prefer .json over .html
This applies to the chart you pass into tree-annotated-plot
via --chart / the Python chart argument — not to the output.
The chart you feed in can be saved by altair as either a portable
Vega-Lite JSON spec (chart.save("titers.json")) or an HTML page
(chart.save("titers.html")). We recommend .json for the input:
it's the canonical Vega-Lite exchange format and tree-annotated-plot
parses it directly. The .html path works only with altair's default
save template — custom template= arguments aren't supported, so
JSON is the more robust choice.
For the output that tree-annotated-plot writes via --output,
.html is a perfectly good choice if you want a self-contained
interactive page that opens in any browser. .json is smaller and
portable across Vega-Lite hosts; pick whichever fits your downstream
use.
Charts must come from altair 6+ (Vega-Lite v6)
The --chart / chart argument must be a Vega-Lite v6 spec.
Re-save older charts from an altair 6+ environment with
chart.save(...). --no-strict-version /
strict_version=False lets you proceed anyway, at the risk of
rendering bugs from cross-version spec drift.