Skip to content

fix: anchor sc.pl.scatter colorbar to user-supplied ax#4137

Open
gkneighb wants to merge 2 commits into
scverse:mainfrom
gkneighb:fix-3963-scatter-colorbar-ax
Open

fix: anchor sc.pl.scatter colorbar to user-supplied ax#4137
gkneighb wants to merge 2 commits into
scverse:mainfrom
gkneighb:fix-3963-scatter-colorbar-ax

Conversation

@gkneighb
Copy link
Copy Markdown

Summary

Fixes #3963.

When sc.pl.scatter is called with a user-supplied ax, scatter_base placed the colorbar by fig.add_axes(rectangle) with the rectangle coordinates computed from panel_pos -- positions scanpy uses for its own internal panel layout. Those positions have no relationship to the caller's figure, so the colorbar lands somewhere in the figure interior, typically overlapping a sibling axes.

sc.pl.embedding already handles this correctly by routing through plt.colorbar(sct, ax=ax, ...). This PR detects the user-supplied-ax case in scatter_base and uses the same ax= form. The original rectangle-based path is kept for the scanpy-managed-figure case, so multi-panel scatter outputs that scanpy lays out itself are visually unchanged.

Before / after

The OP's repro pattern (left panel is sc.pl.scatter, right panel is anything else):

Before: colorbar floats between the two panels, partly over the right panel's y-axis labels.
After: colorbar sits directly to the right of the left ax, like every other matplotlib colorbar attached via ax=.

Locally on pbmc3k_processed with fig, axs = plt.subplots(1, 2, figsize=(10, 4)):

  • user ax: x0=0.125 x1=0.446
  • colorbar: x0=0.449 x1=0.477 (just past the user ax right edge)

Test plan

Adds tests/test_plotting.py::test_scatter_colorbar_uses_user_ax -- structural assertion that the colorbar's x position sits just past the user's ax right edge, rather than overlapping a sibling panel. Uses a tiny synthetic AnnData to keep the test self-contained.

Unable to run the full pytest tests/test_plotting.py locally because every test in that file currently fails for me with Cannot shard v2 format data. Please set anndata.settings.zarr_write_format to 3 from the anndata_settings autouse fixture (tests/conftest.py:162) interacting with anndata 0.12.10 on Apple Silicon. The added test passes when run standalone with explicit ad.settings.zarr_write_format = 3; ad.settings.auto_shard_zarr_v3 = False, and the underlying fix is verified against the OP's two-panel repro. CI on a clean runner should be the authoritative check.

Notes

  • Doc string left as-is; no doc changes needed.
  • Release notes not yet added (will follow up once I know the PR number, unless the maintainer would prefer me to add <PR>.fix.md in a follow-up commit here).

AI-assisted by Claude Code.

gkneighb added 2 commits May 20, 2026 23:12
When sc.pl.scatter is called with a user-supplied ax, scatter_base
created the colorbar with fig.add_axes(rectangle) using rectangle
coordinates derived from panel_pos -- positions computed against
scanpy's own panel layout, which has no relationship to the caller's
figure. Result: the colorbar lands somewhere in the figure interior,
typically overlapping a sibling user-axes.

Detect the user-supplied-ax case at the top of scatter_base and route
that branch through plt.colorbar(sct, ax=ax, ...), mirroring how
sc.pl.embedding already handles its colorbar. The original
rectangle-based path is preserved for the scanpy-managed-figure case so
the visual layout of multi-panel scatter outputs stays unchanged.

Adds tests/test_plotting.py::test_scatter_colorbar_uses_user_ax as a
structural regression test that asserts the colorbar sits just to the
right of the user ax, not floating across the figure.

AI-assisted by Claude Code.

Closes scverse#3963
@codecov
Copy link
Copy Markdown

codecov Bot commented May 21, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 79.61%. Comparing base (4ba31e4) to head (3f9fcad).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #4137   +/-   ##
=======================================
  Coverage   79.61%   79.61%           
=======================================
  Files         120      120           
  Lines       12786    12789    +3     
=======================================
+ Hits        10179    10182    +3     
  Misses       2607     2607           
Flag Coverage Δ
hatch-test.low-vers 78.86% <100.00%> (+<0.01%) ⬆️
hatch-test.pre 79.45% <100.00%> (-0.02%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/scanpy/plotting/_utils.py 78.99% <100.00%> (+0.13%) ⬆️

... and 1 file with indirect coverage changes

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.

Mislocation of colorbar legend by sc.pl.scatter

1 participant