Skip to content

Label the platelet cluster as platelets, not DC - #98

Merged
shanikawm merged 2 commits into
mainfrom
fix/figure-cell-type-labels
Aug 7, 2026
Merged

Label the platelet cluster as platelets, not DC#98
shanikawm merged 2 commits into
mainfrom
fix/figure-cell-type-labels

Conversation

@shanikawm

@shanikawm shanikawm commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Found by running the opt-in tutorial smoke suite before cutting 1.1.0.
test_cell_type_map_matches_the_markers failed with:

AssertionError: FCER1A is highest in 'CD14+ Mono', not in 'DC'

The defect

tutorials/generate_plots.py carried Seurat's nine new.cluster.ids while
this pipeline resolves eight — DC merges into CD14+ Mono at resolution 0.5,
the split #97 documented. rename_idents is positional, so every label from
position 7 on slid by one:

  • the 14-cell platelet cluster (PPBP 5.85, FCER1A 0.00) was captioned
    "DC" in the annotated UMAP, the tutorial's headline figure
  • "Platelet" was never applied to anything

Per-cluster means, from the pipeline this PR fixes:

marker expected B CD14+ Mono CD8 T "DC" FCGR3A+ Mono Mem CD4 T NK Naive CD4 T
FCER1A DC 0.004 0.164 0.018 0.000 0.008 0.009 0.035 0.028
PPBP Platelet 0.029 0.089 0.035 5.851 0.103 0.028 0.000 0.019

The cluster wearing the DC label is platelets, and the real DC cells are inside
CD14+ Mono, which is what "absorbed at this resolution" means.

The cluster sizes make it exact. Both tools number clusters by descending size:

  • R: 711, 478, 471, 344, 270, 164, 154, 32, 14
  • truecell: 692, 515, 458, 344, 301, 159, 155, 14

R's cluster 7 is the 32-cell DC and its 8 the 14-cell platelets. truecell's 7 is
those same 14 platelets — and was wearing R's label for position 7.

This shipped in 1.0.0. Verified by running the same test against the
v1.0.0 tag in a worktree, where it fails identically — not a regression from
the recent review work.

The part worth reading twice

The rest of the repository already had it right. pbmc3k_tutorial.md prints the
corrected eight-entry map directly beside the figure drawn from the stale
one, and tutorials/README.md states that all 32 DC cells land in CD14+ Mono.
One file lagged, and it was the file that draws the pictures.

The guard test existed and was correct. It never ran: test_tutorial_smoke.py
is opt-in and excluded from CI by design, so nothing failed for a whole release
cycle.

Commit 1 — the fix

  • CELL_TYPE_MAP reduced to the eight clusters the pipeline actually produces,
    with a comment tying it to Step 12 of pbmc3k_tutorial.md — the two are the
    same map written twice.
  • run_pipeline split so run_pipeline_unlabelled exposes the object while the
    idents are still numeric. After the rename the question cannot be asked: a map
    of the wrong length looks exactly like a map of the right one.
  • New guard — the map's keys must equal the cluster ids produced. The old
    assertion reported this defect as "FCER1A is highest in CD14+ Mono", which
    describes the clustering rather than the labelling and reads like a false
    alarm about a merge. The new one names the actual fault.
  • The marker guard now iterates the map rather than the panel, so asking where
    FCER1A peaks stays a question about labelling. DC keeps its panel entry: it is
    the canonical marker list, not a description of what was resolved.

Figures. Seven regenerated. Dropping DC from the label set also shifts the
alphabetical palette assignment, so the two cluster-coloured panels move
alongside the five that carry text. 10_marker_heatmap.png correctly does
not — it restores cluster numbers before drawing. Confirmed these diffs are
all mine: regenerating on untouched main in a worktree produces byte-identical
figures, so nothing here is pre-existing drift.

Commit 2 — the prose that counted the same nine

Three claims outlived the 9 → 8 change:

Where Claim
pbmc3k_tutorial.md caption "the biological result — 9 identical cell types — is the same"
tutorials/README.md "annotate 9 cell types"
ROADMAP.md the earlier label transposition is "guarded by test_cell_type_map_matches_the_markers"

The first sat directly beneath the mislabeled figure, contradicting Step 11 and
the fidelity table on the same page. The third is disproved by this PR existing:
the assertion was correct and never ran, so the entry now says that a correct
assertion nobody runs is not a guard, and that the standing gap is the gate
rather than the assertion.

That caption also explained the differing cluster numbering as Louvain being
"non-deterministic by cluster ID". It is not — both tools number by descending
size, as above, so the keys differ because the sizes do.

docs/fidelity.md and docs/quickstart.md needed nothing: #97 already put them
at eight-vs-nine and ARI 0.899. The root README makes no cluster-count claim.

Verification

Mutation-tested, and the two guards discriminate:

Mutant Shape guard Marker guard
Nine-entry map (the shipped defect) fails fails
B ↔ CD8 T transposed, right length passes fails
Last cluster renamed "DC", right length passes fails
  • Full suite 1179 passed, skips 25 → 26 (the new opt-in guard)
  • tests/test_docs.py 18 passed — the site still builds --strict
  • ruff unchanged at 7 pre-existing findings in files CI does not lint
  • Full TRUECELL_TUTORIAL_SMOKE=1 suite re-running against the fix; result to
    be posted below. The pre-fix run was 24 passed / 1 failed, that one failure
    being this defect.

Worth considering separately

The pbmc3k portion of the smoke suite needs only the 24 MB dataset and ran in
15 seconds. "Run it before cutting a release" is a gate that only works if
someone remembers; this is the second release cycle where a labelled figure went
out wrong. Caching that one dataset in CI would close it.

🤖 Generated with Claude Code

shanikawm and others added 2 commits August 8, 2026 01:01
tutorials/generate_plots.py carried Seurat's nine new.cluster.ids while this
pipeline resolves eight — DC merges into CD14+ Mono at resolution 0.5 — and
rename_idents is positional, so every label from position 7 on slid by one.
The 14-cell platelet cluster (PPBP 5.85, FCER1A 0.00) was captioned "DC" in
the annotated UMAP, the tutorial's headline figure, and "Platelet" was never
applied to anything. This shipped in 1.0.0.

The rest of the repository already had it right: pbmc3k_tutorial.md prints the
corrected eight-entry map beside the figure drawn from the stale one, and
tutorials/README.md states all 32 DC cells land in CD14+ Mono. The file that
draws the pictures was the one that lagged.

The guard test existed and was correct — it just never ran, because
test_tutorial_smoke.py is opt-in and excluded from CI. It now also asserts that
the map's keys are exactly the cluster ids produced, which names a length
mismatch directly rather than inferring it from a marker landing in the wrong
cluster; the old assertion reported this as "FCER1A is highest in CD14+ Mono",
which describes the clustering and reads like a false alarm.

Seven figures regenerated: dropping DC from the label set also shifts the
alphabetical palette assignment, so the cluster-coloured panels move too.
10_marker_heatmap.png correctly does not — it restores cluster numbers first.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three claims outlived the 9 -> 8 cluster change:

* the caption directly beneath the annotated UMAP said "the biological result —
  9 identical cell types — is the same", contradicting Step 11 and the fidelity
  table on the same page;
* tutorials/README.md said the tutorial annotates nine;
* ROADMAP.md recorded the earlier label transposition as guarded by
  test_cell_type_map_matches_the_markers, which this recurrence disproves — the
  assertion was correct and never ran.

The caption also explained the differing cluster numbering as Louvain being
"non-deterministic by cluster ID". Both tools number clusters by descending
size (R: 711, 478, 471, 344, 270, 164, 154, 32, 14 — truecell: 692, 515, 458,
344, 301, 159, 155, 14, verified from r_anchors.json and the pipeline), so the
keys differ because the sizes do. R's cluster 7 is the 32-cell DC and its 8 the
14-cell platelets; truecell's 7 is those same platelets.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@shanikawm

Copy link
Copy Markdown
Contributor Author

Smoke suite against the fix: 26 passed, 11m02s.

Against the pre-fix run of 24 passed / 1 failed. The count goes 25 → 26 because
this PR adds test_cell_type_map_covers_exactly_the_clusters_produced; all 16
tutorials still run end to end.

CI: test (3.12) and test (3.13) both pass, alongside the distributions and
site builds.

@shanikawm
shanikawm merged commit a01bfb1 into main Aug 7, 2026
6 checks passed
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.

1 participant