Use TreeViz from Python¶
treeviz-phylo lets Python scripts and notebooks build TreeViz-native
.treeviz.json sessions. It covers tree input, metadata binding, schema
validation, notebook iframe views, and basic tree inspection. It also handles
static export through an external renderer command.
The package does not vendor the TreeViz browser app or frontend source. It ships Python helpers and the TreeViz session schema.
Published package compatibility
The examples on this page target treeviz-phylo 0.8.2, the current PyPI
release. Browser-side styling is documented in
Tree styling and Browser API.
Install¶
The package needs Python 3.10 or later. Its only required dependency is
jsonschema.
Optional extras:
pip install "treeviz-phylo[notebook]" # IPython display for notebook views
pip install "treeviz-phylo[dataframe]" # pandas, for DataFrame metadata
Import name and version:
The package ships a py.typed marker, so type checkers use its annotations.
Schema compatibility¶
validate_session checks a session against the schema bundled in the package.
The 0.8.2 package schema matches the schema of the hosted app, so a session
saved from the app validates with validate_session. To check a session
against the live schema directly, download it:
curl -A treeviz-docs -o treeviz-session.schema.json \
https://treeviz.newlineages.com/treeviz-session.schema.json
and pass that file to jsonschema.validate.
Minimal session¶
from treeviz import build_session, save_session, validate_session, view_session
tree = "(A:0.1,B:0.2,(C:0.3,D:0.4):0.5);"
metadata = [
{"id": "A", "group": "alpha", "value": 1.2},
{"id": "B", "group": "alpha", "value": 0.8},
{"id": "C", "group": "beta", "value": 2.1},
{"id": "D", "group": "beta", "value": 1.6},
]
tracks = [
{"kind": "color_strip", "column_key": "group", "title": "Group"},
{"kind": "gradient", "column_key": "value", "title": "Value"},
]
session = build_session(tree, metadata=metadata, tracks=tracks, row_key_column="id")
validate_session(session)
save_session(session, "example.treeviz.json")
view = view_session(session, open_browser=False)
view.url
Open the saved .treeviz.json in the browser, or display view in a notebook.
Notebook use¶
from IPython.display import display
from treeviz import view_tree
view = view_tree(
"(A,B,(C,D));",
metadata=metadata,
tracks=tracks,
row_key_column="id",
open_browser=False,
)
display(view)
The session is carried in the iframe URL fragment (gzip + base64). A session
of up to 256 KB encoded, roughly 1,500 tips with a few tracks, displays
inline. Anything larger is too large for inline display. Save it as
.treeviz.json instead and open the file in the browser.
Tree inputs¶
build_session(...), view_tree(...), and render_tree(...) accept:
- Newick strings;
- Newick or Nexus file paths;
- Nexus text that starts with
#NEXUS; - supported tree objects with Newick export methods;
- Biopython tree objects when Biopython is installed.
Passing a list of trees to build_session(...) returns a list of independent
session dictionaries.
The parser follows the browser app's Newick and Nexus rules:
[...]comments are skipped wherever they appear, including a leading[&R]or[&U]. They never become part of a label or branch length.[&&NHX:key=value:...]and BEAST-style[&key=value,...]comments become nodemeta. The valuestrueandfalsebecome booleans, and numbers become numbers. View settings such asnodeCircleColorAttributecan select these nodemetakeys.- A number after
)is a support value, not a label. - For Nexus input, the first
TREE(orUTREE) statement is used and itsTRANSLATEtable is applied, so numeric leaf aliases become the real names. An alias that is missing from the table raises aUserWarning.
Metadata inputs¶
Metadata can be:
- a list or iterable of row dictionaries;
- a pandas
DataFrame(install thedataframeextra); - a CSV or TSV file path (
.tsvand.tabfiles are read as tab-separated, all other paths as CSV); None.
Each row should describe one leaf. One column should match the tree leaf
labels exactly. Pass that column as row_key_column.
metadata = [
{"sample_id": "A", "lineage": "alpha", "load": 1.2, "detected": True,
"score_a": 0.9, "score_b": 0.4, "day": 3, "note": "reference"},
{"sample_id": "B", "lineage": "alpha", "load": 0.8, "detected": False,
"score_a": 0.2, "score_b": 0.7, "day": 5, "note": "candidate"},
]
session = build_session("(A,B);", metadata=metadata, row_key_column="sample_id")
If row_key_column is omitted, the package chooses the metadata column with
the most exact leaf-name matches. If no column matches any leaf, it uses the
first column. A row with a missing or blank row key raises a ValueError. A
row key that appears twice keeps the last row and raises a UserWarning that
names the key.
Cell values may be strings, numbers, booleans, None, or empty strings. Empty
strings, None, pandas missing values, and float NaN or infinity are
missing values and are stored as JSON null. Saved sessions and share URLs
are always strict JSON. A NaN anywhere else in a session, for example in
view, raises a ValueError when the session is serialized.
Column types follow the browser app:
- A column whose non-missing values are all finite numbers is continuous. The
text
"NaN"or"inf"is not a number, so a column that holds it is not continuous. - A column of booleans, or of the tokens
true/false,yes/no,y/n,1/0, andpresent/absentin any case, is binary. A column of only1and0numbers is continuous, because the number rule comes first. - A column with up to 32 distinct values is categorical.
- Any other column is text.
Binding leaves to rows¶
Leaf labels are matched to row keys after the normalization that
binding_flags sets. By default only whitespace is trimmed, so a row key
"A " binds to leaf A with confidence normalized. The snake_case and
camelCase flag names both work:
session = build_session(
tree,
metadata=metadata,
binding_flags={
"case_insensitive": True,
"strip_underscores": True,
"strip_quoted_label_decorations": True,
},
)
An unknown flag name raises a ValueError. A leaf without a label never
binds. When a leaf matches more than one row after normalization, for example
A and A under trim, the exact row is the recorded match and the leaf is
listed in the duplicates diagnostics.
Track definitions¶
Tracks map metadata columns to visual encodings.
| Kind | Required key | Typical use |
|---|---|---|
color_strip |
column_key |
categorical group bands |
gradient |
column_key |
continuous values |
heatmap |
column_keys |
multiple continuous columns |
bar |
column_key |
continuous bar tracks |
stacked_bar |
column_keys |
per-tip composition, one segment per column |
text |
column_key |
labels from metadata |
binary_dots |
column_key |
boolean presence/absence symbols |
Example:
tracks = [
{"kind": "color_strip", "column_key": "lineage", "title": "Lineage"},
{"kind": "gradient", "column_key": "load", "title": "Load", "palette": "Viridis"},
{"kind": "heatmap", "column_keys": ["score_a", "score_b"], "title": "Scores"},
{"kind": "bar", "column_key": "day", "title": "Collection day", "show_axis": True},
{"kind": "binary_dots", "column_key": "detected", "title": "Detected", "shape": "circle"},
{"kind": "text", "column_key": "note", "title": "Note"},
]
Underscores and hyphens are both accepted in track kinds:
color_strip and color-strip are equivalent.
Track fields also accept camelCase, for example columnKey for column_key.
The TOML spelling column is not accepted. A track without its column, or
with a column that is not in the metadata, raises a ValueError that lists
the available columns.
Stacked bars normalize each row by default. Set normalize=False to show
unnormalized values. color_strip and stacked_bar tracks take
category_colors, a map from category or column name to color. gradient,
heatmap, and bar tracks take domain as [low, high], two finite numbers
that fix the value range.
Fields left out take the defaults that the app uses when a track is added:
| Kind | Defaults |
|---|---|
color_strip |
width 20, palette okabe-ito, missing color #cccccc |
gradient |
width 30, palette Viridis, clip true |
heatmap |
cell width 14, palette Viridis |
bar |
width 80, color #4393c3, axis on top, helper lines off |
stacked_bar |
width 90, palette okabe-ito, normalized, missing color #e2e8f0 |
text |
width 100, Inter 12 px, align start |
binary_dots |
width 16, shape circle, color #2166ac |
color_strip tracks take display_mode values strip, symbol, and
wedge. bar tracks take bar, symbol, and wedge, with manual bins or
automatic auto_bins. symbol_shape sets the symbol. Each of these fields
also has a camelCase spelling. Palette ids are listed in the
palette registry.
View settings¶
Pass a view dictionary to set layout defaults:
view = {
"layout": "rectangular",
"showSupport": True,
"branchScale": 0.8,
"leafSpacing": 0.9,
"metadataGap": 0,
"labelFontSize": 11,
"internalNodeMarkerAttribute": "support",
"internalNodeMarkerEncoding": "shade",
}
session = build_session(tree, metadata=metadata, tracks=tracks, view=view)
The browser can further adjust and save view settings.
view keys are the camelCase names in the session schema's view object. An
unknown key raises a ValueError that names it and the closest valid names,
so layuot suggests layout. Setting branchScale or leafSpacing without
branchScaleMode switches the session to manual width, as the browser
controls do. Automatic mode would otherwise ignore both settings.
Browser styling fields¶
In the hosted app, metadata can map to exact node circles and to branch
width/color. The app can also apply conditional rules, draw compact symbol or
wedge lanes, and style terminal branches. treeviz-phylo 0.8.2 validates
these view fields in Python. Pass them through the view argument, or apply
them later through window.__treeviz. Metadata branch colors extend to the
MRCA stem of each same-colored clade (see Tree styling).
Session fields¶
build_session(...), view_tree(...), and render_tree(...) take
keyword-only arguments for the optional session fields:
| Argument | Session field | Content |
|---|---|---|
legends |
legends |
Swatch legends: [{"title": ..., "entries": [{"label": ..., "color": ...}]}]. |
attribute_labels |
attributeLabels |
Node-metadata key to the display name the app's pickers show. |
connections |
connections |
Link sets: [{"pairs": [{"from": ..., "to": ...}]}]. id, title, visible, and geometry are optional and default to connections-0 (by position), Connections, True, and ribbon. Other fields raise a ValueError. |
node_metadata |
nodeMetadata |
Rows that describe internal nodes, as records, a DataFrame, or a path. Rows bind by internal node label or by an mrca_of column of |-separated leaf names. |
node_row_key_column |
Key column of node_metadata. Default: the first column. |
|
node_marks |
nodeMarks |
Pie marks from node_metadata columns: [{"columns": [...]}], with optional style, palette, size_by, and max_radius. |
saved_views |
views |
Named views: [{"name": ..., "view": {...}}], with optional id and is_default. Each view is applied on top of the session view. |
binding_flags |
binding flags | How leaf labels match metadata row keys. The default trims whitespace. case_insensitive, strip_underscores, and strip_quoted_label_decorations are opt-in. |
from treeviz import build_session, validate_session
session = build_session(
"((A,B)AB,(C,D)CD)root;",
metadata=[
{"id": "A", "group": "alpha", "gc": 0.4, "at": 0.6},
{"id": "B", "group": "alpha", "gc": 0.5, "at": 0.5},
{"id": "C", "group": "beta", "gc": 0.3, "at": 0.7},
{"id": "D", "group": "beta", "gc": 0.6, "at": 0.4},
],
row_key_column="id",
tracks=[
{"kind": "color_strip", "column_key": "group", "title": "Group"},
{"kind": "stacked_bar", "column_keys": ["gc", "at"], "title": "Composition"},
],
legends=[{"title": "Group", "entries": [{"label": "alpha", "color": "#1b9e77"}]}],
attribute_labels={"soil": "Soil samples"},
connections=[{"pairs": [{"from": "A", "to": "D"}]}],
node_metadata=[
{"node": "AB", "soil": 3, "marine": 1},
{"node": "CD", "soil": 0, "marine": 4},
],
node_row_key_column="node",
node_marks=[{"columns": ["soil", "marine"]}],
saved_views=[{"name": "Circular", "view": {"layout": "circular"}}],
binding_flags={"case_insensitive": True},
)
validate_session(session)
A numeric node-metadata row key is never a node label, because a number on an
internal node is a support value; such a row needs mrca_of. Rows that do not
bind, and labels shared by several nodes, are listed in
session["nodeBinding"]["diagnostics"] with the same codes as the app.
A TOML config can also define legends and attribute names; see Legends and attribute names.
Tree inspection¶
from treeviz import binding_diagnostics, leaf_names, tree_stats
leaf_names(session)
tree_stats(session)
binding_diagnostics(session)
binding_diagnostics(session) returns unmatchedLeaves (stable keys),
unmatchedRows (row keys), and duplicates. Each duplicates entry is a
{"leafStableKey", "rowKeys"} pair for a leaf that matches more than one row
after normalization. Row keys repeated in the input are not listed there; see
Binding leaves to rows.
tree_stats returns leaf, internal-node, and total-node counts, the maximum
depth, the tree height, rooted and binary flags, and a branch-length summary.
Static export¶
render_tree(...) writes a temporary .treeviz.json session and then calls
a compatible external renderer command, with support for svg, png, and
pdf. That renderer is not installed by the PyPI package or by this public
repository. By default, auto_crop=None follows the renderer default, which
fits the output to its content. For a fixed-viewport output, pass
auto_crop=False.
from treeviz import render_tree
render_tree(
"(A,B,(C,D));",
metadata=metadata,
tracks=tracks,
format="pdf",
output="example.pdf",
command=["/path/to/treeviz-renderer"],
width=1400,
height=700,
auto_crop=True,
crop_padding=24,
metrics="example.metrics.json",
)
Without command, render_tree runs bun run treeviz render, which works
only inside a TreeViz source checkout; cwd sets the directory it runs in.
The session path, -o, --format, and the other options are appended to the
command. output and metrics are resolved against the current working
directory, not cwd, and the returned path is absolute. With no output, a
temporary file is created and returned. The temporary session file is removed
afterwards, also when the renderer fails. If the command cannot be started,
render_tree raises FileNotFoundError.
auto_crop=True trims the export to the visible content. crop_padding is in
pixels. metrics writes a JSON file with the content box, crop box,
whitespace margins, fill ratios, and warnings such as
excess-vertical-whitespace.
If no renderer command is available, use view_session(...),
view_tree(...), or session_url(...) and open the session in the hosted
browser app.
Public API¶
| Function | Purpose |
|---|---|
build_session(tree, metadata=None, tracks=None, view=None, name=None, row_key_column=None, *, legends=None, attribute_labels=None, connections=None, node_metadata=None, node_row_key_column=None, node_marks=None, saved_views=None, binding_flags=None) |
Build one session dictionary, or a list of sessions when tree is a list. |
validate_session(session, schema_path=None) |
Validate a session against the packaged JSON schema. |
save_session(session, path) |
Write a .treeviz.json session and return the output path. |
load_session(path, *, validate=True, schema_path=None) |
Read a saved session; validation is enabled by default. |
view_tree(tree, metadata=None, tracks=None, view=None, open_browser=True, app_url=..., name=None, row_key_column=None, *, ...) |
Build a session and return a notebook/browser view object. Takes the same keyword-only arguments as build_session. |
view_session(session, open_browser=True, app_url=...) |
Return a notebook/browser view for an existing session. |
session_url(session, app_url=...) |
Return the hosted TreeViz URL for a session. A session larger than 256 KB encoded returns the base app URL without the session. |
leaf_names(tree_or_session) |
Return terminal leaf labels. |
tree_stats(tree_or_session) |
Return topology and branch-length summary statistics. |
binding_diagnostics(session) |
Return metadata binding diagnostics. |
render_tree(tree, metadata=None, tracks=None, view=None, format="svg", output=None, command=None, width=None, height=None, auto_crop=None, crop_padding=None, metrics=None, cwd=None, *, ...) |
Render SVG, PNG, or PDF through an external renderer command and return the absolute output path. Takes the same keyword-only arguments as build_session. |
TreeVizSession(session, app_url=...) |
Notebook-friendly view object with .url, .fragment, and ._repr_html_(). |
treeviz.__version__ |
The installed treeviz-phylo version. |
Runnable example script¶
A clone of this repository includes a script that imports the package, builds 30- and 100-leaf examples, validates metadata binding, and writes sessions:
git clone https://github.com/fmschulz/treeviz
cd treeviz
python examples/plot_treeviz_examples.py --out treeviz-example-output
It writes lineage_30.treeviz.json (rectangular, metadata tracks, binary leaf
symbols, support markers, numeric branch coloring), clade_100.treeviz.json
(circular, the same features), a _bare twin of each without metadata, and
summary.json with leaf counts, binding diagnostics, and hosted URLs. Open the
.treeviz.json files in the browser, or pass
--render --renderer-command /path/to/treeviz-renderer if a renderer is
installed; see Exports.