Minimal call
import random
rng = random.Random(0)
data = [[rng.gauss(i % 3, 0.4) for _ in range(4)] for i in range(9)]
c = pt.chart()
c.add_dendrogram(data, labels=[f"s{i}" for i in range(9)], method="ward")
Docstring
Hierarchical-clustering tree renderer.
The artist is a *tree renderer*: it consumes a `cluster.SplitTree`
(N linkage matrices + their display order) and emits the SVG. The
clustering math lives in [`cluster.py`](../cluster.py).
Three input paths, all funnel into the same SplitTree → layout → draw
pipeline:
- `data=` (+ optional `clusters=` for two-level clustering) →
`linkage()` or `linkage_split()` builds the tree.
- `linkage_matrix=` → one-block tree wrap (user already has a raw scipy Z).
- `tree=` → use a pre-built SplitTree directly (lets the same cluster
result drive multiple charts without redoing scipy work).
`clusters=` is a parallel list of group labels (one per row of `data`,
aligned with `labels=`) — it drives the two-level cluster (per-block
linkage + per-centroid linkage). The visual gap whitespace between
blocks is a separate concern: declare `c.sectors({cluster: [members]},
axis=...)` on the panel for that — the dendrogram and any peer heatmap
on the same shared scale pick up the gaps automatically.
When clusters are in play, the dendrogram exposes its final leaf order
via `axis_order`, so a peer heatmap with the same grouping vector picks
up the two-level block order automatically — artist `axis_order` beats
`frame_defaults` order in `_resolution`'s precedence rule.
`palette=` (a `{group: color}` map) colors each block's branches by its
group, leaving the between-cluster trunk neutral — driven off the same
two-level block structure. Needs `clusters=` (or a `tree=` from
`linkage_split`).
**`labels=` indexes the ORIGINAL input order, NOT the display order.**
scipy's Z matrix uses original observation indices throughout; so
`labels[i]` must still refer to original observation `i` after the
tree reorders things. The renderer applies the scipy leaf permutation
internally — the user supplies labels aligned with `data=` (row-by-row)
and never has to reorder them. This is also what makes it safe for a
peer heatmap to share a category x/y axis by name: both artists see the
same labels (in original input order); the SplitTree's `axis_order`
hook then drives the *final* display order downstream.