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

fix(labels): align instance_id with colour vector for partial tables (#775)#776
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
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