Skip to content

fix(labels): align instance_id with colour vector for partial tables (#775) - #776

Open
timtreis wants to merge 2 commits into
mainfrom
fix/issue-775
Open

timtreis wants to merge 2 commits into
mainfrom
fix/issue-775

Conversation

@timtreis

@timtreis timtreis commented Sep 4, 2026

Copy link
Copy Markdown
Member

Problem

render_labels(color=...) raises IndexError when the annotating table has rows for instances that are not present in the labels element — e.g. objects lost when a segmentation is regenerated, or a table read from an upstream pipeline.

IndexError: boolean index did not match indexed array along axis 0;
size of axis is 2 but size of corresponding boolean axis is 3

Fixes #775.

Root cause

instance_id was derived from all table rows (np.unique(table.obs[instance_key])), but the colour vector from get_values is restricted to the element's present instances. The two diverged, so the rasterize mask (length = number of table rows) indexed the shorter colour vector → IndexError at render.py_color.py ColorSpec.filter.

This was only ever half-fixed: #603/#661 added _check_instance_ids_overlap, but that raises only when the ID sets are fully disjoint. Shapes/points already reindex colour to the element index and are robust to phantom rows; labels was left behind.

Fix

Restrict instance_id to the canonical instances actually present in the element (get_element_instances) intersected with the table — the same basis get_values uses for the colour vector, independent of the rendered scale:

instance_id = np.intersect1d(
    np.unique(table.obs[instance_key].values),
    np.asarray(get_element_instances(sdata_filt[element])),
)

Rasterize / multiscale display drops are still reconciled against both vectors by the existing mask further down, so alignment holds in all cases.

Behaviour

  • Changed: previously-crashing cases (phantom table rows; present-but-untabled labels under color) now render as missing instead of raising.
  • Unchanged: well-formed input — including rasterize/multiscale that drops small labels — is byte-for-byte the same (intersect equals the old np.unique(table.obs) there).
  • No public API change; one extra get_element_instances scan on the coloured-labels path.

Tests

tests/pl/test_render_labels.py: parametrized phantom-row + present-not-in-table × continuous/categorical, plus a rasterize-survival case. All fail on main (IndexError) and pass with the fix. Full non-visual labels suite: 35 passed.

https://claude.ai/code/session_01U9J5GSXR4XGjPMaQQR5iSj

…775)

render_labels(color=...) raised IndexError when the annotating table had
rows for instances absent from the labels (e.g. objects lost when a
segmentation is regenerated, or a table from an upstream pipeline).

instance_id was derived from all table rows, but the colour vector from
get_values is restricted to the element's present instances, so the two
diverged and the rasterize mask indexed the shorter colour vector.

Restrict instance_id to the canonical instances actually present in the
element (get_element_instances) intersected with the table, matching the
colour vector's basis independent of rasterize/multiscale display drops.

Claude-Session: https://claude.ai/code/session_01U9J5GSXR4XGjPMaQQR5iSj
@codecov-commenter

codecov-commenter commented Sep 4, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 66.66667% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 80.06%. Comparing base (956eb0c) to head (e5ac105).

Files with missing lines Patch % Lines
src/spatialdata_plot/pl/render.py 66.66% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #776      +/-   ##
==========================================
- Coverage   80.09%   80.06%   -0.03%     
==========================================
  Files          18       18              
  Lines        4863     4866       +3     
  Branches     1080     1081       +1     
==========================================
+ Hits         3895     3896       +1     
- Misses        603      604       +1     
- Partials      365      366       +1     
Files with missing lines Coverage Δ
src/spatialdata_plot/pl/render.py 88.83% <66.66%> (-0.17%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

…_points

Address review on #776: the extra get_element_instances scan is only needed
when a per-instance colour/outline vector (or as_points) must stay aligned;
the uniform color=None path keeps the cheap np.unique(table.obs). Hoist the
gate into a shared `needs_aligned_instances` flag reused by the rasterize
reconciliation, and drop the stale issue ref / line number from the comment.

Claude-Session: https://claude.ai/code/session_01U9J5GSXR4XGjPMaQQR5iSj

This branch has not been deployed

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

Labels

None yet

Projects

None yet

2 participants