Skip to content

Cumulative root-to-node S1 mutation count - #1223

Open
trvrb wants to merge 2 commits into
masterfrom
cumulative-s1
Open

Cumulative root-to-node S1 mutation count#1223
trvrb wants to merge 2 commits into
masterfrom
cumulative-s1

Conversation

@trvrb

@trvrb trvrb commented Aug 2, 2026

Copy link
Copy Markdown
Member

Via Claude

Motivation

The S1_mutations coloring counts spike S1 amino-acid changes (sites 14–685). Today it's computed with augur distance --compare-to root, which compares each node's reconstructed sequence directly against the root — i.e. the number of S1 sites that currently differ from the root.

Six-plus years in, S1 sites now mutate recurrently (like flu). Under a net-vs-root count, a second hit at an already-changed site adds nothing and a reversion subtracts, so the metric saturates and looks like S1 evolution is slowing — when the underlying substitution process isn't.

Why not just use augur distance?

augur distance can't express a path sum. Every --compare-to mode (root / ancestor / pairwise) makes a single node-pair comparison and never walks/sums along branches — root mode compares each node to the root sequence once. So no combination of its flags yields "total changes accumulated from root to node." Hence a small new script.

I'd propose that we add some sort of --cumulative option to augur distance as often want this sort of cumulative counting rather than direct comparison to reference.

What this does

New scripts/root_to_node_distance.py sums the per-branch S1 distance along every edge from the root to each node, so each substitution event accrues. It doesn't reimplement the distance logic; it reuses augur's own machinery per branch:

  • augur.distance.read_distance_map — load defaults/distance_maps/S1.json
  • augur.distance.get_distance_between_nodes(parent, child, map) — per-branch distance, identical semantics to the current metric
  • augur.reconstruct_sequences.load_alignments — the existing aligned.gene.S_withInternalNodes.fasta already holds reconstructed sequences for every node (internal + tip)

It walks the tree in iterative preorder (safe for deep trees) accumulating cumulative[child] = cumulative[parent] + branch_distance.

Gaps and ambiguities are excluded via a new "ignored_characters": ["-", "X", "N"] in S1.json, so artifactual gaps in submitted sequences can't inflate divergence.

S1_mutations uses augur distance --compare-to root, which counts S1 sites
(spike aa 14-685) that currently differ from the root. As S1 sites now mutate
recurrently, a second hit at an already-changed site adds nothing and a
reversion subtracts, so the metric saturates and looks like it is slowing down.

Add scripts/root_to_node_distance.py, which sums the per-branch S1 distance
along the path from the root to each node (reusing augur's read_distance_map,
get_distance_between_nodes and load_alignments), so every substitution event
accrues -- a molecular-clock-like tally rather than a net difference. Gaps and
ambiguities are excluded via a new ignored_characters key in S1.json so
artifactual gaps in submitted sequences cannot inflate divergence.

Wire this in as a new rule cumulative_distances producing the attribute
S1_cumulative_mutations, alongside the existing vs-root S1_mutations, with a new
"S1 mutations (cumulative)" coloring, so the two can be compared head-to-head
before S1_mutations itself is switched over.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@trvrb

trvrb commented Aug 2, 2026

Copy link
Copy Markdown
Member Author

We can see that saturation is indeed occurring by comparing new cumulative S1 mutations against old S1 mutations against reference.

Screenshot 2026-08-02 at 11 25 27 AM

An interesting side note: it looks like the successful saltational trunk is not reverting as much as side branches.

Now that the cumulative metric can be compared head-to-head against the old
net-vs-root count, promote it to be the canonical S1_mutations coloring.

Remove the vs-root `rule distances` (augur distance --compare-to root); the
existing `rule cumulative_distances` now emits the attribute S1_mutations
directly. Drop the temporary "S1 mutations (cumulative)" coloring and the stale
rules.distances node-data wiring in export.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@trvrb
trvrb marked this pull request as ready for review August 2, 2026 09:55
@trvrb
trvrb requested a review from huddlej August 2, 2026 09:56
@trvrb

trvrb commented Aug 2, 2026

Copy link
Copy Markdown
Member Author

@huddlej --- Tagging you here mostly as FYI. This form of counting is going to be relevant for longer timescale analyses, and may be relevant to 12y H3N2 for example. I do think this eventually deserves to live as something like a --cumulative option in augur distance.

@huddlej

huddlej commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Thanks, @trvrb! I added an issue to Augur describing this new feature.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants