Skip to content

Commit 1474a90

Browse files
flying-sheepUbuntu
authored and
Ubuntu
committed
Allow all valid legend_loc options (scverse#3163)
1 parent 30350ec commit 1474a90

File tree

85 files changed

+65
-47
lines changed
  • docs/release-notes
  • src/scanpy/plotting
  • tests
    • _images/embedding-missing-values
      • test_missing_values_categorical[pca-na_color.black_tup-na_in_legend.False-legend.on_bottom-groups.3]
      • test_missing_values_categorical[pca-na_color.black_tup-na_in_legend.False-legend.on_bottom-groups.all]
      • test_missing_values_categorical[pca-na_color.black_tup-na_in_legend.True-legend.on_bottom-groups.3]
      • test_missing_values_categorical[pca-na_color.black_tup-na_in_legend.True-legend.on_bottom-groups.all]
      • test_missing_values_categorical[pca-na_color.default-na_in_legend.False-legend.on_bottom-groups.3]
      • test_missing_values_categorical[pca-na_color.default-na_in_legend.False-legend.on_bottom-groups.all]
      • test_missing_values_categorical[pca-na_color.default-na_in_legend.True-legend.on_bottom-groups.3]
      • test_missing_values_categorical[pca-na_color.default-na_in_legend.True-legend.on_bottom-groups.all]
      • test_missing_values_categorical[spatial-na_color.black_tup-na_in_legend.False-legend.on_bottom-groups.3]
      • test_missing_values_categorical[spatial-na_color.black_tup-na_in_legend.False-legend.on_bottom-groups.all]
      • test_missing_values_categorical[spatial-na_color.black_tup-na_in_legend.True-legend.on_bottom-groups.3]
      • test_missing_values_categorical[spatial-na_color.black_tup-na_in_legend.True-legend.on_bottom-groups.all]
      • test_missing_values_categorical[spatial-na_color.default-na_in_legend.False-legend.on_bottom-groups.3]
      • test_missing_values_categorical[spatial-na_color.default-na_in_legend.False-legend.on_bottom-groups.all]
      • test_missing_values_categorical[spatial-na_color.default-na_in_legend.True-legend.on_bottom-groups.3]
      • test_missing_values_categorical[spatial-na_color.default-na_in_legend.True-legend.on_bottom-groups.all]
      • test_missing_values_continuous[pca-na_color.black_tup-legend.on_data-vbounds.default]
      • test_missing_values_continuous[pca-na_color.black_tup-legend.on_data-vbounds.norm]
      • test_missing_values_continuous[pca-na_color.black_tup-legend.on_data-vbounds.numbers]
      • test_missing_values_continuous[pca-na_color.black_tup-legend.on_data-vbounds.percentile]
      • test_missing_values_continuous[pca-na_color.black_tup-legend.on_data-vbounds.vcenter]
      • test_missing_values_continuous[pca-na_color.black_tup-legend.on_right-vbounds.default]
      • test_missing_values_continuous[pca-na_color.black_tup-legend.on_right-vbounds.norm]
      • test_missing_values_continuous[pca-na_color.black_tup-legend.on_right-vbounds.numbers]
      • test_missing_values_continuous[pca-na_color.black_tup-legend.on_right-vbounds.percentile]
      • test_missing_values_continuous[pca-na_color.black_tup-legend.on_right-vbounds.vcenter]
      • test_missing_values_continuous[pca-na_color.black_tup-vbounds.default]
      • test_missing_values_continuous[pca-na_color.black_tup-vbounds.norm]
      • test_missing_values_continuous[pca-na_color.black_tup-vbounds.numbers]
      • test_missing_values_continuous[pca-na_color.black_tup-vbounds.percentile]
      • test_missing_values_continuous[pca-na_color.black_tup-vbounds.vcenter]
      • test_missing_values_continuous[pca-na_color.default-legend.on_data-vbounds.default]
      • test_missing_values_continuous[pca-na_color.default-legend.on_data-vbounds.norm]
      • test_missing_values_continuous[pca-na_color.default-legend.on_data-vbounds.numbers]
      • test_missing_values_continuous[pca-na_color.default-legend.on_data-vbounds.percentile]
      • test_missing_values_continuous[pca-na_color.default-legend.on_data-vbounds.vcenter]
      • test_missing_values_continuous[pca-na_color.default-legend.on_right-vbounds.default]
      • test_missing_values_continuous[pca-na_color.default-legend.on_right-vbounds.norm]
      • test_missing_values_continuous[pca-na_color.default-legend.on_right-vbounds.numbers]
      • test_missing_values_continuous[pca-na_color.default-legend.on_right-vbounds.percentile]
      • test_missing_values_continuous[pca-na_color.default-legend.on_right-vbounds.vcenter]
      • test_missing_values_continuous[pca-na_color.default-vbounds.default]
      • test_missing_values_continuous[pca-na_color.default-vbounds.norm]
      • test_missing_values_continuous[pca-na_color.default-vbounds.numbers]
      • test_missing_values_continuous[pca-na_color.default-vbounds.percentile]
      • test_missing_values_continuous[pca-na_color.default-vbounds.vcenter]
      • test_missing_values_continuous[spatial-na_color.black_tup-legend.on_data-vbounds.default]
      • test_missing_values_continuous[spatial-na_color.black_tup-legend.on_data-vbounds.norm]
      • test_missing_values_continuous[spatial-na_color.black_tup-legend.on_data-vbounds.numbers]
      • test_missing_values_continuous[spatial-na_color.black_tup-legend.on_data-vbounds.percentile]
      • test_missing_values_continuous[spatial-na_color.black_tup-legend.on_data-vbounds.vcenter]
      • test_missing_values_continuous[spatial-na_color.black_tup-legend.on_right-vbounds.default]
      • test_missing_values_continuous[spatial-na_color.black_tup-legend.on_right-vbounds.norm]
      • test_missing_values_continuous[spatial-na_color.black_tup-legend.on_right-vbounds.numbers]
      • test_missing_values_continuous[spatial-na_color.black_tup-legend.on_right-vbounds.percentile]
      • test_missing_values_continuous[spatial-na_color.black_tup-legend.on_right-vbounds.vcenter]
      • test_missing_values_continuous[spatial-na_color.black_tup-vbounds.default]
      • test_missing_values_continuous[spatial-na_color.black_tup-vbounds.norm]
      • test_missing_values_continuous[spatial-na_color.black_tup-vbounds.numbers]
      • test_missing_values_continuous[spatial-na_color.black_tup-vbounds.percentile]
      • test_missing_values_continuous[spatial-na_color.black_tup-vbounds.vcenter]
      • test_missing_values_continuous[spatial-na_color.default-legend.on_data-vbounds.default]
      • test_missing_values_continuous[spatial-na_color.default-legend.on_data-vbounds.norm]
      • test_missing_values_continuous[spatial-na_color.default-legend.on_data-vbounds.numbers]
      • test_missing_values_continuous[spatial-na_color.default-legend.on_data-vbounds.percentile]
      • test_missing_values_continuous[spatial-na_color.default-legend.on_data-vbounds.vcenter]
      • test_missing_values_continuous[spatial-na_color.default-legend.on_right-vbounds.default]
      • test_missing_values_continuous[spatial-na_color.default-legend.on_right-vbounds.norm]
      • test_missing_values_continuous[spatial-na_color.default-legend.on_right-vbounds.numbers]
      • test_missing_values_continuous[spatial-na_color.default-legend.on_right-vbounds.percentile]
      • test_missing_values_continuous[spatial-na_color.default-legend.on_right-vbounds.vcenter]
      • test_missing_values_continuous[spatial-na_color.default-vbounds.default]
      • test_missing_values_continuous[spatial-na_color.default-vbounds.norm]
      • test_missing_values_continuous[spatial-na_color.default-vbounds.numbers]
      • test_missing_values_continuous[spatial-na_color.default-vbounds.percentile]
      • test_missing_values_continuous[spatial-na_color.default-vbounds.vcenter]

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

85 files changed

+65
-47
lines changed

docs/release-notes/1.10.3.md

+1

docs/release-notes/1.9.7.md

+1

docs/release-notes/1.9.8.md

+1

src/scanpy/plotting/_anndata.py

+7-23
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import collections.abc as cabc
66
from collections import OrderedDict
77
from itertools import product
8-
from typing import TYPE_CHECKING
8+
from typing import TYPE_CHECKING, get_args
99

1010
import matplotlib as mpl
1111
import numpy as np
@@ -47,30 +47,14 @@
4747
from seaborn import FacetGrid
4848
from seaborn.matrix import ClusterGrid
4949

50-
from ._utils import ColorLike, _FontSize, _FontWeight
50+
from ._utils import ColorLike, _FontSize, _FontWeight, _LegendLoc
5151

5252
# TODO: is that all?
5353
_Basis = Literal["pca", "tsne", "umap", "diffmap", "draw_graph_fr"]
5454
_VarNames = Union[str, Sequence[str]]
5555

5656

57-
VALID_LEGENDLOCS = {
58-
"none",
59-
"right margin",
60-
"on data",
61-
"on data export",
62-
"best",
63-
"upper right",
64-
"upper left",
65-
"lower left",
66-
"lower right",
67-
"right",
68-
"center left",
69-
"center right",
70-
"lower center",
71-
"upper center",
72-
"center",
73-
}
57+
VALID_LEGENDLOCS = frozenset(get_args(_utils._LegendLoc))
7458

7559

7660
@old_positionals(
@@ -105,7 +89,7 @@ def scatter(
10589
groups: str | Iterable[str] | None = None,
10690
components: str | Collection[str] | None = None,
10791
projection: Literal["2d", "3d"] = "2d",
108-
legend_loc: str = "right margin",
92+
legend_loc: _LegendLoc | None = "right margin",
10993
legend_fontsize: int | float | _FontSize | None = None,
11094
legend_fontweight: int | _FontWeight | None = None,
11195
legend_fontoutline: float | None = None,
@@ -202,7 +186,7 @@ def _scatter_obs(
202186
groups=None,
203187
components=None,
204188
projection: Literal["2d", "3d"] = "2d",
205-
legend_loc="right margin",
189+
legend_loc: _LegendLoc | None = "right margin",
206190
legend_fontsize=None,
207191
legend_fontweight=None,
208192
legend_fontoutline=None,
@@ -211,7 +195,7 @@ def _scatter_obs(
211195
frameon=None,
212196
right_margin=None,
213197
left_margin=None,
214-
size=None,
198+
size: int | float | None = None,
215199
marker=".",
216200
title=None,
217201
show=None,
@@ -379,7 +363,7 @@ def _scatter_obs(
379363
key.replace("_", " ") if not is_color_like(key) else "" for key in keys
380364
]
381365

382-
axs = scatter_base(
366+
axs: list[Axes] = scatter_base(
383367
Y,
384368
title=title,
385369
alpha=alpha,

src/scanpy/plotting/_docs.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,8 @@
8080
projection
8181
Projection of plot (default: `'2d'`).
8282
legend_loc
83-
Location of legend, either `'on data'`, `'right margin'` or a valid keyword
84-
for the `loc` parameter of :class:`~matplotlib.legend.Legend`.
83+
Location of legend, either `'on data'`, `'right margin'`, `None`,
84+
or a valid keyword for the `loc` parameter of :class:`~matplotlib.legend.Legend`.
8585
legend_fontsize
8686
Numeric size in pt or string describing the size.
8787
See :meth:`~matplotlib.text.Text.set_fontsize`.

src/scanpy/plotting/_tools/paga.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
from matplotlib.axes import Axes
3232
from matplotlib.colors import Colormap
3333

34-
from .._utils import _FontSize, _FontWeight, _IGraphLayout
34+
from .._utils import _FontSize, _FontWeight, _IGraphLayout, _LegendLoc
3535

3636

3737
@old_positionals(
@@ -67,7 +67,7 @@ def paga_compare(
6767
groups=None,
6868
components=None,
6969
projection: Literal["2d", "3d"] = "2d",
70-
legend_loc="on data",
70+
legend_loc: _LegendLoc | None = "on data",
7171
legend_fontsize: int | float | _FontSize | None = None,
7272
legend_fontweight: int | _FontWeight = "bold",
7373
legend_fontoutline=None,

src/scanpy/plotting/_tools/scatterplots.py

+18-14
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
_FontSize, # noqa: TCH001
5353
_FontWeight, # noqa: TCH001
5454
_IGraphLayout, # noqa: TCH001
55+
_LegendLoc, # noqa: TCH001
5556
check_colornorm,
5657
check_projection,
5758
circles,
@@ -97,7 +98,7 @@ def embedding(
9798
frameon: bool | None = None,
9899
legend_fontsize: int | float | _FontSize | None = None,
99100
legend_fontweight: int | _FontWeight = "bold",
100-
legend_loc: str = "right margin",
101+
legend_loc: _LegendLoc | None = "right margin",
101102
legend_fontoutline: int | None = None,
102103
colorbar_loc: str | None = "right",
103104
vmax: VBound | Sequence[VBound] | None = None,
@@ -1091,11 +1092,11 @@ def _components_to_dimensions(
10911092

10921093

10931094
def _add_categorical_legend(
1094-
ax,
1095+
ax: Axes,
10951096
color_source_vector,
10961097
*,
10971098
palette: dict,
1098-
legend_loc: str,
1099+
legend_loc: _LegendLoc | None,
10991100
legend_fontweight,
11001101
legend_fontsize,
11011102
legend_fontoutline,
@@ -1124,17 +1125,7 @@ def _add_categorical_legend(
11241125
box = ax.get_position()
11251126
ax.set_position([box.x0, box.y0, box.width * 0.91, box.height])
11261127

1127-
if legend_loc == "right margin":
1128-
for label in cats:
1129-
ax.scatter([], [], c=palette[label], label=label)
1130-
ax.legend(
1131-
frameon=False,
1132-
loc="center left",
1133-
bbox_to_anchor=(1, 0.5),
1134-
ncol=(1 if len(cats) <= 14 else 2 if len(cats) <= 30 else 3),
1135-
fontsize=legend_fontsize,
1136-
)
1137-
elif legend_loc == "on data":
1128+
if legend_loc == "on data":
11381129
# identify centroids to put labels
11391130

11401131
all_pos = (
@@ -1158,6 +1149,19 @@ def _add_categorical_legend(
11581149
fontsize=legend_fontsize,
11591150
path_effects=legend_fontoutline,
11601151
)
1152+
elif legend_loc not in {None, "none"}:
1153+
for label in cats:
1154+
ax.scatter([], [], c=palette[label], label=label)
1155+
if legend_loc == "right margin":
1156+
ax.legend(
1157+
frameon=False,
1158+
loc="center left",
1159+
bbox_to_anchor=(1, 0.5),
1160+
ncol=(1 if len(cats) <= 14 else 2 if len(cats) <= 30 else 3),
1161+
fontsize=legend_fontsize,
1162+
)
1163+
else:
1164+
ax.legend(loc=legend_loc, fontsize=legend_fontsize)
11611165

11621166

11631167
def _get_basis(adata: AnnData, basis: str) -> np.ndarray:

src/scanpy/plotting/_utils.py

+17
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,23 @@
4242
_FontSize = Literal[
4343
"xx-small", "x-small", "small", "medium", "large", "x-large", "xx-large"
4444
]
45+
_LegendLoc = Literal[
46+
"none",
47+
"right margin",
48+
"on data",
49+
"on data export",
50+
"best",
51+
"upper right",
52+
"upper left",
53+
"lower left",
54+
"lower right",
55+
"right",
56+
"center left",
57+
"center right",
58+
"lower center",
59+
"upper center",
60+
"center",
61+
]
4562
ColorLike = Union[str, tuple[float, ...]]
4663

4764

tests/test_embedding_plots.py

+16-6
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from functools import partial
44
from pathlib import Path
5+
from typing import TYPE_CHECKING
56

67
import matplotlib as mpl
78
import matplotlib.pyplot as plt
@@ -15,6 +16,10 @@
1516
import scanpy as sc
1617
from testing.scanpy._helpers.data import pbmc3k_processed
1718

19+
if TYPE_CHECKING:
20+
from scanpy.plotting._utils import _LegendLoc
21+
22+
1823
HERE: Path = Path(__file__).parent
1924
ROOT = HERE / "_images"
2025

@@ -111,10 +116,10 @@ def plotfunc(request):
111116

112117

113118
@pytest.fixture(
114-
params=["on data", "right margin", None],
115-
ids=["legend.on_data", "legend.on_right", "legend.off"],
119+
params=["on data", "right margin", "lower center", None],
120+
ids=["legend.on_data", "legend.on_right", "legend.on_bottom", "legend.off"],
116121
)
117-
def legend_loc(request):
122+
def legend_loc(request) -> _LegendLoc | None:
118123
return request.param
119124

120125

@@ -155,7 +160,7 @@ def vbounds(request):
155160

156161
def test_missing_values_categorical(
157162
*,
158-
fixture_request,
163+
fixture_request: pytest.FixtureRequest,
159164
image_comparer,
160165
adata,
161166
plotfunc,
@@ -182,7 +187,13 @@ def test_missing_values_categorical(
182187

183188

184189
def test_missing_values_continuous(
185-
*, fixture_request, image_comparer, adata, plotfunc, na_color, legend_loc, vbounds
190+
*,
191+
fixture_request: pytest.FixtureRequest,
192+
image_comparer,
193+
adata,
194+
plotfunc,
195+
na_color,
196+
vbounds,
186197
):
187198
save_and_compare_images = partial(image_comparer, MISSING_VALUES_ROOT, tol=15)
188199

@@ -191,7 +202,6 @@ def test_missing_values_continuous(
191202
# Passing through a dict so it's easier to use default values
192203
kwargs = {}
193204
kwargs.update(vbounds)
194-
kwargs["legend_loc"] = legend_loc
195205
if na_color is not None:
196206
kwargs["na_color"] = na_color
197207

0 commit comments

Comments
 (0)