tree-annotated-plot 0.3.0__tar.gz → 0.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/CHANGELOG.md +63 -1
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/CLAUDE.md +14 -2
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/PKG-INFO +3 -3
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/docs/examples.md +71 -0
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/examples/flu-seqneut-2025to2026_titer_charts.py +11 -0
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/pyproject.toml +9 -2
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/scripts/generate_docs_assets.py +43 -0
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/src/tree_annotated_plot/_config.py +53 -6
- tree_annotated_plot-0.5.0/src/tree_annotated_plot/_label_style.py +349 -0
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/src/tree_annotated_plot/_plot.py +117 -77
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/src/tree_annotated_plot/cli.py +62 -12
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/tests/test_appearance_tuning.py +27 -0
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/tests/test_chart_loader.py +108 -0
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/tests/test_cli.py +1 -0
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/tests/test_label_connection.py +48 -3
- tree_annotated_plot-0.5.0/tests/test_strain_label_styles.py +565 -0
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/tests/test_tree_location.py +52 -0
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/.github/workflows/docs.yml +0 -0
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/.github/workflows/release.yml +0 -0
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/.github/workflows/tests.yml +0 -0
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/.gitignore +0 -0
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/LICENSE +0 -0
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/README.md +0 -0
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/docs/cli.md +0 -0
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/docs/index.md +0 -0
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/docs/python-api.md +0 -0
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/examples/fetch_auspice_data.py +0 -0
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/examples/synthetic_example.py +0 -0
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/mkdocs.yml +0 -0
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/scripts/build_docs.sh +0 -0
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/scripts/check.sh +0 -0
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/src/tree_annotated_plot/__init__.py +0 -0
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/src/tree_annotated_plot/_color.py +0 -0
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/src/tree_annotated_plot/_tree.py +0 -0
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/tests/test_branch_length.py +0 -0
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/tests/test_color_tree.py +0 -0
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/tests/test_examples_run.py +0 -0
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/tests/test_introspection.py +0 -0
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/tests/test_line_order.py +0 -0
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/tests/test_plot.py +0 -0
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/tests/test_plot_config.py +0 -0
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/tests/test_real_data.py +0 -0
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/tests/test_reconciliation.py +0 -0
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/tests/test_scale_bar.py +0 -0
- {tree_annotated_plot-0.3.0 → tree_annotated_plot-0.5.0}/tests/test_style.py +0 -0
|
@@ -5,7 +5,69 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
-
## [
|
|
8
|
+
## [0.5.0] - 2026-10-30
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `strain_label_styles` (default `None`): style a subset of the strain labels
|
|
13
|
+
rendered in the tree panel when `connect_leader_to_label` is on, e.g. to draw
|
|
14
|
+
the vaccine strains' labels in bold red. A list of rules, each naming some
|
|
15
|
+
`strains` and a `style` of Vega-Lite text properties (`fill`, `opacity`,
|
|
16
|
+
`fontWeight`, `fontStyle`). Where rules overlap, later rules win property by
|
|
17
|
+
property. An optional `when` condition applies a rule only while it holds:
|
|
18
|
+
- the name of a parameter declared in the chart, such as a checkbox;
|
|
19
|
+
- any Vega expression, e.g. `"emphasis == 'cell'"` for a radio button;
|
|
20
|
+
- in Python, an `alt.Parameter`, which is declared on the returned chart if
|
|
21
|
+
the chart doesn't already declare it.
|
|
22
|
+
|
|
23
|
+
Each styled property becomes a per-label expression on the existing label
|
|
24
|
+
layers, so no layers are added and the returned chart keeps its shape. A
|
|
25
|
+
strain that isn't a tree tip raises; strains dropped by `prune_tree_to_chart`
|
|
26
|
+
are skipped. CLI form: `--strain-label-styles` with a JSON array, where
|
|
27
|
+
`when` names a parameter saved in the chart JSON.
|
|
28
|
+
([#12](https://github.com/jbloomlab/tree-annotated-plot/issues/12))
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
|
|
32
|
+
- A chart with selection parameters on a compound chart (layer, facet, or
|
|
33
|
+
concat) failed to render when passed to `plot()` as a JSON path, HTML path
|
|
34
|
+
or spec dict, which is every CLI use. Vega-Lite reported
|
|
35
|
+
`Duplicate signal name: "<selection>_tuple"`. This affected the flu examples'
|
|
36
|
+
CLI reproductions. The chart loader removed each selection's `views` before
|
|
37
|
+
loading, and altair then emitted empty `views`, which makes Vega-Lite define
|
|
38
|
+
the selection in every view. The loader now leaves the spec's `views` intact.
|
|
39
|
+
Live altair charts were never affected.
|
|
40
|
+
- The `plot()` docstring's Returns section described the panel order wrongly
|
|
41
|
+
for a strain on `x`. The default `tree_location="bottom"` puts the chart
|
|
42
|
+
first and the tree second. The section now gives the order for each
|
|
43
|
+
`tree_location`.
|
|
44
|
+
|
|
45
|
+
## [0.4.0] - 2026-08-20
|
|
46
|
+
|
|
47
|
+
### Added
|
|
48
|
+
|
|
49
|
+
- `spacing` (default `0`): pixels of blank space between the tree panel and
|
|
50
|
+
the chart panel. `0` keeps the two butted together, as before. Distinct from
|
|
51
|
+
`shift_tree_loc`, which moves the tree within its own panel by resizing the
|
|
52
|
+
label strip. CLI form: `--spacing`.
|
|
53
|
+
|
|
54
|
+
### Changed
|
|
55
|
+
|
|
56
|
+
- Pin `ruff` to `>=0.16,<0.17` and state the lint rules explicitly with
|
|
57
|
+
`[tool.ruff.lint] select`. Ruff's *default* rule set widened in 0.16, which
|
|
58
|
+
turned CI red on unchanged code; the selection is now a project decision
|
|
59
|
+
rather than whatever the installed ruff defaults to. Development-only, no
|
|
60
|
+
effect on the package.
|
|
61
|
+
|
|
62
|
+
### Fixed
|
|
63
|
+
|
|
64
|
+
- With `connect_leader_to_label` on, strain labels were anchored exactly on the
|
|
65
|
+
tree panel's chart-facing edge, so the text ran up against the chart's frame
|
|
66
|
+
and the frame line became hard to read. The pad that the label strip already
|
|
67
|
+
reserved is now applied between the text and the chart, and the leader lines
|
|
68
|
+
stop where the text begins rather than continuing to the panel edge. The gap
|
|
69
|
+
is `max(3, strain_label_font_size * 0.2)` px, and the tree panel grows by that
|
|
70
|
+
much, so a hand-tuned `shift_tree_loc` may want the same adjustment.
|
|
9
71
|
|
|
10
72
|
## [0.3.0] - 2026-05-19
|
|
11
73
|
|
|
@@ -7,7 +7,9 @@
|
|
|
7
7
|
section as you make them — new parameters, behavior changes, removed
|
|
8
8
|
features, bug fixes — in [Keep a Changelog](https://keepachangelog.com/)
|
|
9
9
|
format. At release time, `## [Unreleased]` is renamed to
|
|
10
|
-
`## [X.Y.Z] - YYYY-MM-DD
|
|
10
|
+
`## [X.Y.Z] - YYYY-MM-DD`, with no empty `## [Unreleased]` left above it.
|
|
11
|
+
When the next change lands and there's no `## [Unreleased]` section, add
|
|
12
|
+
one at the top.
|
|
11
13
|
- **Single source of truth — `pyproject.toml`**: canonical for dependencies,
|
|
12
14
|
supported Python version, build config, tool settings. Don't restate any of
|
|
13
15
|
these in prose; refer to `pyproject.toml`.
|
|
@@ -52,6 +54,13 @@
|
|
|
52
54
|
on `x` or `y`, overrides its sort to match the tree's tip order, and
|
|
53
55
|
hconcat's or vconcat's a tree panel on the matching side. Tree dictates
|
|
54
56
|
tip ordering; plot follows.
|
|
57
|
+
- **Loaded specs go to `from_dict` unmodified.** Don't strip or rewrite
|
|
58
|
+
altair's annotations when loading a dict / JSON / HTML chart. Selection
|
|
59
|
+
params' `views` in particular are valid at the top level; removing them made
|
|
60
|
+
every compound chart with a selection fail to render ("Duplicate signal
|
|
61
|
+
name"). See `_load_chart`. Likewise, declare extra params by appending to
|
|
62
|
+
the combined chart's top-level `params`, never with `add_params`, which
|
|
63
|
+
recurses into sub-charts and raises on spec-loaded ones.
|
|
55
64
|
- **Two surfaces, one config**: `tree_annotated_plot.plot()` and the `tree-annotated-plot`
|
|
56
65
|
CLI both accept the same parameters via `PlotConfig`. They converge on
|
|
57
66
|
`_build(tree, chart, config)` so they can never disagree.
|
|
@@ -78,7 +87,10 @@
|
|
|
78
87
|
- **Eyeball examples after any rendering change.** There are no
|
|
79
88
|
image-snapshot regression tests (vl-convert/font/platform variance
|
|
80
89
|
makes them flaky), so visual regressions only get caught by
|
|
81
|
-
looking.
|
|
90
|
+
looking. Tests may still render with `vl_convert.vegalite_to_svg`
|
|
91
|
+
to check that a spec renders at all, or to assert discrete SVG
|
|
92
|
+
attributes such as a label's `font-weight` / `fill`; those are
|
|
93
|
+
deterministic. After any change to `_plot.py`, `_tree.py`, the example
|
|
82
94
|
scripts, or anything they touch: run `scripts/build_docs.sh`, open
|
|
83
95
|
`site/examples.html`, and click through each example — both the
|
|
84
96
|
embedded SVG and the interactive HTML link. Confirm tip-row
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: tree-annotated-plot
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Annotate the axis of an Altair / Vega-Lite plot with a phylogenetic tree.
|
|
5
5
|
Project-URL: Documentation, https://jbloomlab.github.io/tree-annotated-plot/
|
|
6
6
|
Project-URL: Homepage, https://github.com/jbloomlab/tree-annotated-plot
|
|
@@ -43,7 +43,7 @@ Requires-Dist: vl-convert-python>=1.9
|
|
|
43
43
|
Provides-Extra: dev
|
|
44
44
|
Requires-Dist: black>=24; extra == 'dev'
|
|
45
45
|
Requires-Dist: pytest>=8; extra == 'dev'
|
|
46
|
-
Requires-Dist: ruff
|
|
46
|
+
Requires-Dist: ruff<0.17,>=0.16; extra == 'dev'
|
|
47
47
|
Provides-Extra: docs
|
|
48
48
|
Requires-Dist: mkdocs-click>=0.8; extra == 'docs'
|
|
49
49
|
Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
|
|
@@ -140,6 +140,77 @@ CLI flags: `--connect-leader-to-label --strain-label-font-size 9
|
|
|
140
140
|
--shift-tree-loc 60`. In Python:
|
|
141
141
|
`connect_leader_to_label=True, strain_label_font_size=9, shift_tree_loc=60`.
|
|
142
142
|
|
|
143
|
+
### Optional: emphasize some strains' labels
|
|
144
|
+
|
|
145
|
+
With label connection on, you can also style a subset of the labels, for
|
|
146
|
+
example so the vaccine strains are easy to pick out. Pass
|
|
147
|
+
`strain_label_styles`, a list of rules. Each rule names some `strains`
|
|
148
|
+
(tree tip identifiers, i.e. `tree_strain_field` values) and a `style` built
|
|
149
|
+
from Vega-Lite text properties: `fill`, `opacity`, `fontWeight` and
|
|
150
|
+
`fontStyle`. A rule can also take a `when` condition, usually the name of a
|
|
151
|
+
checkbox parameter, so the style applies only while the box is ticked.
|
|
152
|
+
Where rules overlap, later rules win.
|
|
153
|
+
|
|
154
|
+
The example below draws the labels of the H3N2 recent vaccine strains in
|
|
155
|
+
bold red, with a checkbox that turns the emphasis off and on:
|
|
156
|
+
|
|
157
|
+

|
|
158
|
+
|
|
159
|
+
[Open the interactive chart in a new tab →](charts/h3n2_combined_label_styles.html){target="_blank"}
|
|
160
|
+
|
|
161
|
+
From the CLI, a `when` parameter has to be declared in the saved chart, so
|
|
162
|
+
add it before saving:
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
chart = chart.add_params(
|
|
166
|
+
alt.param(
|
|
167
|
+
name="vaccine_strains",
|
|
168
|
+
value=True,
|
|
169
|
+
bind=alt.binding_checkbox(name="highlight vaccine strains "),
|
|
170
|
+
)
|
|
171
|
+
)
|
|
172
|
+
chart.save("titers.json")
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
then pass the rules as a JSON array, alongside the label-connection flags
|
|
176
|
+
above:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
--connect-leader-to-label --strain-label-styles \
|
|
180
|
+
'[{"strains": ["J.2.4", "J.2:S145N"],
|
|
181
|
+
"style": {"fill": "#d62728", "fontWeight": "bold"},
|
|
182
|
+
"when": "vaccine_strains"}]'
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
In Python, pass the parameter object itself. If your chart doesn't already
|
|
186
|
+
declare it, it is declared on the returned chart for you:
|
|
187
|
+
|
|
188
|
+
```python
|
|
189
|
+
vaccine_strains = alt.param(
|
|
190
|
+
name="vaccine_strains",
|
|
191
|
+
value=True,
|
|
192
|
+
bind=alt.binding_checkbox(name="highlight vaccine strains "),
|
|
193
|
+
)
|
|
194
|
+
tree_annotated_plot.plot(
|
|
195
|
+
...,
|
|
196
|
+
connect_leader_to_label=True,
|
|
197
|
+
strain_label_styles=[
|
|
198
|
+
{
|
|
199
|
+
"strains": ["J.2.4", "J.2:S145N"],
|
|
200
|
+
"style": {"fill": "#d62728", "fontWeight": "bold"},
|
|
201
|
+
"when": vaccine_strains,
|
|
202
|
+
}
|
|
203
|
+
],
|
|
204
|
+
)
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Leave out `when` for a style that is always on. `when` can be any Vega
|
|
208
|
+
expression, so other inputs work too. For example, with a radio button
|
|
209
|
+
`alt.param(name="emphasis", value="cell", bind=alt.binding_radio(options=["cell", "egg"]))`
|
|
210
|
+
declared on the chart, one rule with `"when": "emphasis == 'cell'"` and
|
|
211
|
+
another with `"when": "emphasis == 'egg'"` switch the emphasis between two
|
|
212
|
+
groups of strains.
|
|
213
|
+
|
|
143
214
|
### Color the tree
|
|
144
215
|
|
|
145
216
|
Pass `color_tree_by` to color the tree's branches and tip circles by
|
|
@@ -131,6 +131,17 @@ def strain_plot_order(viruses_subset: pd.DataFrame) -> list[str]:
|
|
|
131
131
|
return sorted_df["virus"].tolist()
|
|
132
132
|
|
|
133
133
|
|
|
134
|
+
def vaccine_axis_labels(viruses: pd.DataFrame, subtype: str) -> list[str]:
|
|
135
|
+
"""Axis labels of the `RECENT_VACCINE_STRAINS` in `subtype`, in table order.
|
|
136
|
+
|
|
137
|
+
These are the strain-axis values the chart (and so the tree's tips) use for
|
|
138
|
+
the vaccine strains, e.g. for `strain_label_styles`."""
|
|
139
|
+
is_vaccine = (viruses["subtype"] == subtype) & viruses["virus"].isin(
|
|
140
|
+
RECENT_VACCINE_STRAINS
|
|
141
|
+
)
|
|
142
|
+
return viruses.loc[is_vaccine, "axis_label"].tolist()
|
|
143
|
+
|
|
144
|
+
|
|
134
145
|
def make_chart(
|
|
135
146
|
subtype: str,
|
|
136
147
|
chart_type: str,
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tree-annotated-plot"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.5.0"
|
|
8
8
|
description = "Annotate the axis of an Altair / Vega-Lite plot with a phylogenetic tree."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -36,7 +36,7 @@ Issues = "https://github.com/jbloomlab/tree-annotated-plot/issues"
|
|
|
36
36
|
[project.optional-dependencies]
|
|
37
37
|
dev = [
|
|
38
38
|
"pytest>=8",
|
|
39
|
-
"ruff>=0.
|
|
39
|
+
"ruff>=0.16,<0.17",
|
|
40
40
|
"black>=24",
|
|
41
41
|
]
|
|
42
42
|
docs = [
|
|
@@ -58,3 +58,10 @@ target-version = ["py311"]
|
|
|
58
58
|
|
|
59
59
|
[tool.ruff]
|
|
60
60
|
target-version = "py311"
|
|
61
|
+
|
|
62
|
+
[tool.ruff.lint]
|
|
63
|
+
# Ruff's default rule set is not stable across releases -- 0.16 broadened it,
|
|
64
|
+
# which turned CI red on unchanged code. State the rules we lint against so a
|
|
65
|
+
# ruff upgrade changes them only when we say so. These are ruff's pre-0.16
|
|
66
|
+
# defaults, which is what this project has been checked against all along.
|
|
67
|
+
select = ["E4", "E7", "E9", "F"]
|
|
@@ -25,6 +25,8 @@ import sys
|
|
|
25
25
|
from pathlib import Path
|
|
26
26
|
from types import ModuleType
|
|
27
27
|
|
|
28
|
+
import altair as alt
|
|
29
|
+
|
|
28
30
|
import tree_annotated_plot
|
|
29
31
|
|
|
30
32
|
REPO = Path(__file__).resolve().parent.parent
|
|
@@ -163,6 +165,47 @@ def _render_kikawa() -> None:
|
|
|
163
165
|
)
|
|
164
166
|
_save_pair(out, "h3n2_combined_label_connect")
|
|
165
167
|
|
|
168
|
+
# The same label-connect H3N2 chart, with the recent vaccine strains'
|
|
169
|
+
# labels drawn in bold red while a checkbox is ticked: `strain_label_styles`
|
|
170
|
+
# with a `when` param. The checkbox starts ticked so the static SVG shows
|
|
171
|
+
# the emphasis.
|
|
172
|
+
h3n2_chart_styles = builder.make_chart(
|
|
173
|
+
subtype="H3N2",
|
|
174
|
+
chart_type="iqr",
|
|
175
|
+
titers=titers,
|
|
176
|
+
viruses=viruses,
|
|
177
|
+
metadata=metadata,
|
|
178
|
+
all_cohorts=all_cohorts,
|
|
179
|
+
)
|
|
180
|
+
vaccine_strains = alt.param(
|
|
181
|
+
name="vaccine_strains",
|
|
182
|
+
value=True,
|
|
183
|
+
bind=alt.binding_checkbox(name="highlight vaccine strains "),
|
|
184
|
+
)
|
|
185
|
+
out = tree_annotated_plot.plot(
|
|
186
|
+
DATA_DIR / "flu-seqneut-2025to2026_H3N2.json",
|
|
187
|
+
h3n2_chart_styles,
|
|
188
|
+
chart_strain_field="axis_label",
|
|
189
|
+
tree_strain_field="derived_haplotype",
|
|
190
|
+
branch_length="div",
|
|
191
|
+
tree_size=140,
|
|
192
|
+
scale_bar=True,
|
|
193
|
+
branch_length_units="substitutions",
|
|
194
|
+
connect_leader_to_label=True,
|
|
195
|
+
strain_label_font_size=9,
|
|
196
|
+
shift_tree_loc=60,
|
|
197
|
+
color_tree_by="subclade",
|
|
198
|
+
tree_color_legend_format={"orient": "left"},
|
|
199
|
+
strain_label_styles=[
|
|
200
|
+
{
|
|
201
|
+
"strains": builder.vaccine_axis_labels(viruses, "H3N2"),
|
|
202
|
+
"style": {"fill": "#d62728", "fontWeight": "bold"},
|
|
203
|
+
"when": vaccine_strains,
|
|
204
|
+
}
|
|
205
|
+
],
|
|
206
|
+
)
|
|
207
|
+
_save_pair(out, "h3n2_combined_label_styles")
|
|
208
|
+
|
|
166
209
|
# H3N2 once more, colored by genotype at HA1 site 158: same chart and
|
|
167
210
|
# default layout as `h3n2_combined`, with `color_tree_by` switched to
|
|
168
211
|
# the genotype form. Site 158 has two mutations (N158K, N158D) in the
|
|
@@ -68,6 +68,15 @@ class PlotConfig:
|
|
|
68
68
|
"(x-encoded).",
|
|
69
69
|
] = None
|
|
70
70
|
|
|
71
|
+
spacing: Annotated[
|
|
72
|
+
int,
|
|
73
|
+
"Pixels of blank space between the tree panel and the chart panel. "
|
|
74
|
+
"Default 0, which butts the two together. Distinct from "
|
|
75
|
+
"`shift_tree_loc`, which moves the tree within its own panel by "
|
|
76
|
+
"resizing the label strip; `spacing` sets the gap between the panels "
|
|
77
|
+
"and applies whether or not `connect_leader_to_label` is on.",
|
|
78
|
+
] = 0
|
|
79
|
+
|
|
71
80
|
tree_line_width: Annotated[
|
|
72
81
|
float,
|
|
73
82
|
"Stroke width (px) for the tree's branch lines. Default 2.",
|
|
@@ -152,11 +161,34 @@ class PlotConfig:
|
|
|
152
161
|
"when `connect_leader_to_label` is on.",
|
|
153
162
|
] = "normal"
|
|
154
163
|
|
|
164
|
+
strain_label_styles: Annotated[
|
|
165
|
+
list[dict[str, Any]] | None,
|
|
166
|
+
"Style a subset of the strain labels rendered in the tree panel; "
|
|
167
|
+
"requires `connect_leader_to_label`. A list of rules, each a dict "
|
|
168
|
+
'with keys "strains" (a list of tip identifiers, i.e. '
|
|
169
|
+
'`tree_strain_field` values), "style" and, optionally, "when". '
|
|
170
|
+
'"style" holds Vega-Lite text-mark properties: "fill" (a color), '
|
|
171
|
+
'"opacity" (0 to 1), "fontWeight" ("normal", "bold", or 100-900) '
|
|
172
|
+
'and "fontStyle" ("normal" or "italic"). "when" is a Vega '
|
|
173
|
+
"expression that turns the rule on while it is truthy, usually just "
|
|
174
|
+
"the name of a parameter declared in the chart (e.g. one bound to a "
|
|
175
|
+
"checkbox), but any expression works (e.g. \"emphasis == 'cell'\" "
|
|
176
|
+
'for a radio button). Without "when" the style always applies. '
|
|
177
|
+
"Where rules overlap, later rules win property by property; "
|
|
178
|
+
"properties no rule sets keep `strain_label_font_weight` and the "
|
|
179
|
+
"default black, upright text. A strain that isn't a tree tip is an "
|
|
180
|
+
"error; strains dropped by `prune_tree_to_chart` are skipped. CLI "
|
|
181
|
+
"form: a JSON array (quote the whole argument), e.g. "
|
|
182
|
+
'\'[{"strains": ["K"], "style": {"fill": "#d62728", '
|
|
183
|
+
'"fontWeight": "bold"}, "when": "vaccine_strains"}]\'.',
|
|
184
|
+
] = None
|
|
185
|
+
|
|
155
186
|
shift_tree_loc: Annotated[
|
|
156
187
|
int,
|
|
157
188
|
"Pixels by which to shift the tree toward (positive) or away from "
|
|
158
|
-
"(negative) the chart
|
|
159
|
-
"connect_leader_to_label is off
|
|
189
|
+
"(negative) the chart, by resizing the label strip inside the tree "
|
|
190
|
+
"panel. Default 0. Has no effect when connect_leader_to_label is off; "
|
|
191
|
+
"to set the gap between the tree and chart panels use `spacing`.",
|
|
160
192
|
] = 0
|
|
161
193
|
|
|
162
194
|
color_tree_by: Annotated[
|
|
@@ -215,10 +247,25 @@ class PlotConfig:
|
|
|
215
247
|
|
|
216
248
|
|
|
217
249
|
# Sidecar for Python-docstring-only prose, keyed by PlotConfig field name.
|
|
218
|
-
#
|
|
219
|
-
#
|
|
220
|
-
#
|
|
221
|
-
PARAM_DOC_EXTRAS: dict[str, str] = {
|
|
250
|
+
# Add an entry when a field's docstring entry needs more than the CLI
|
|
251
|
+
# `--help` text covers. The extras are appended to the field's description in
|
|
252
|
+
# `tree_annotated_plot.plot.__doc__`, never in CLI --help.
|
|
253
|
+
PARAM_DOC_EXTRAS: dict[str, str] = {
|
|
254
|
+
"strain_label_styles": (
|
|
255
|
+
'In Python, "when" may also be an `altair.Parameter`, e.g. '
|
|
256
|
+
'`alt.param(name="vaccine_strains", value=True, '
|
|
257
|
+
"bind=alt.binding_checkbox())`, which is declared on the returned "
|
|
258
|
+
"chart if your chart doesn't already declare it; or an altair "
|
|
259
|
+
'parameter expression such as `emphasis == "cell"`. Only a bare '
|
|
260
|
+
"`altair.Parameter` is declared for you: parameters that an "
|
|
261
|
+
"expression refers to must be declared on the chart, and an "
|
|
262
|
+
"expression other than a bare name isn't checked until the chart "
|
|
263
|
+
"renders. A bare name or parameter must be a variable parameter: "
|
|
264
|
+
"selection parameters are rejected, since the label data doesn't "
|
|
265
|
+
"carry the fields a selection tests. Any iterable of strings (e.g. "
|
|
266
|
+
'a pandas Series) works for "strains".'
|
|
267
|
+
),
|
|
268
|
+
}
|
|
222
269
|
|
|
223
270
|
|
|
224
271
|
# Descriptions for the three data-input parameters. These can't live on
|