🌐 AI搜索 & 代理 主页
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions doc/api/next_api_changes/deprecations/30531-TH.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
In-place modifications of colormaps
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Colormaps are planned to become immutable in the long term.

As a first step, in-place modifications of colormaps are now pending-deprecated.
This affects the following methods of `.Colormap`:

- `.Colormap.set_bad` - use ``cmap.with_extremes(bad=...)`` instead
- `.Colormap.set_under` - use ``cmap.with_extremes(under=...)`` instead
- `.Colormap.set_over` - use ``cmap.with_extremes(over=...)`` instead
- `.Colormap.set_extremes` - use ``cmap.with_extremes(...)`` instead

Use the respective `.Colormap.with_extremes` and appropriate keyword arguments
instead which returns a copy of the colormap (available since matplotlib 3.4).
Alternatively, if you create the colormap yourself, you can also pass the
respective arguments to the constructor (available since matplotlib 3.11).
36 changes: 28 additions & 8 deletions lib/matplotlib/colors.py
Original file line number Diff line number Diff line change
Expand Up @@ -871,6 +871,10 @@ def get_bad(self):
self._ensure_inited()
return np.array(self._lut[self._i_bad])

@_api.deprecated(
"3.11",
pending=True,
alternative="cmap.with_extremes(bad=...) or Colormap(bad=...)")
def set_bad(self, color='k', alpha=None):
"""Set the color for masked values."""
self._set_extremes(bad=(color, alpha))
Expand All @@ -880,6 +884,10 @@ def get_under(self):
self._ensure_inited()
return np.array(self._lut[self._i_under])

@_api.deprecated(
"3.11",
pending=True,
alternative="cmap.with_extremes(under=...) or Colormap(under=...)")
def set_under(self, color='k', alpha=None):
"""Set the color for low out-of-range values."""
self._set_extremes(under=(color, alpha))
Expand All @@ -889,10 +897,19 @@ def get_over(self):
self._ensure_inited()
return np.array(self._lut[self._i_over])

@_api.deprecated(
"3.11",
pending=True,
alternative="cmap.with_extremes(over=...) or Colormap(over=...)")
def set_over(self, color='k', alpha=None):
"""Set the color for high out-of-range values."""
self._set_extremes(over=(color, alpha))

@_api.deprecated(
"3.11",
pending=True,
alternative="cmap.with_extremes(bad=..., under=..., over=...) or "
"Colormap(bad=..., under=..., over=...)")
def set_extremes(self, *, bad=None, under=None, over=None):
"""
Set the colors for masked (*bad*) values and, when ``norm.clip =
Expand Down Expand Up @@ -1614,14 +1631,16 @@ def with_extremes(self, *, bad=None, under=None, over=None):
f" i.e. be of length {len(new_cm)}.")
else:
for c, b in zip(new_cm, under):
c.set_under(b)
# in-place change is ok, since we've just created c as a copy
c._set_extremes(under=b)
if over is not None:
if not np.iterable(over) or len(over) != len(new_cm):
raise ValueError("*over* must contain a color for each scalar colormap"
f" i.e. be of length {len(new_cm)}.")
else:
for c, b in zip(new_cm, over):
c.set_over(b)
# in-place change is ok, since we've just created c as a copy
c._set_extremes(over=b)
return new_cm

@property
Expand Down Expand Up @@ -2070,26 +2089,27 @@ def __getitem__(self, item):
"""Creates and returns a colorbar along the selected axis"""
if not self._isinit:
self._init()
extremes = (
dict(bad=self._rgba_bad, over=self._rgba_outside, under=self._rgba_outside)
if self.shape in ['ignore', 'circleignore']
else dict(bad=self._rgba_bad)
)
if item == 0:
origin_1_as_int = int(self._origin[1]*self.M)
if origin_1_as_int > self.M-1:
origin_1_as_int = self.M-1
one_d_lut = self._lut[:, origin_1_as_int]
new_cmap = ListedColormap(one_d_lut, name=f'{self.name}_0')
new_cmap = ListedColormap(one_d_lut, name=f'{self.name}_0', **extremes)

elif item == 1:
origin_0_as_int = int(self._origin[0]*self.N)
if origin_0_as_int > self.N-1:
origin_0_as_int = self.N-1
one_d_lut = self._lut[origin_0_as_int, :]
new_cmap = ListedColormap(one_d_lut, name=f'{self.name}_1')
new_cmap = ListedColormap(one_d_lut, name=f'{self.name}_1', **extremes)
else:
raise KeyError(f"only 0 or 1 are"
f" valid keys for BivarColormap, not {item!r}")
new_cmap._rgba_bad = self._rgba_bad
if self.shape in ['ignore', 'circleignore']:
new_cmap.set_over(self._rgba_outside)
new_cmap.set_under(self._rgba_outside)
return new_cmap

def _repr_png_(self):
Expand Down
3 changes: 1 addition & 2 deletions lib/matplotlib/tests/test_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2966,8 +2966,7 @@ def test_scatter_edgecolor_RGB(self):
@check_figures_equal()
def test_scatter_invalid_color(self, fig_test, fig_ref):
ax = fig_test.subplots()
cmap = mpl.colormaps["viridis"].resampled(16)
cmap.set_bad("k", 1)
cmap = mpl.colormaps["viridis"].resampled(16).with_extremes(bad="black")
# Set a nonuniform size to prevent the last call to `scatter` (plotting
# the invalid points separately in fig_ref) from using the marker
# stamping fast path, which would result in slightly offset markers.
Expand Down
12 changes: 8 additions & 4 deletions lib/matplotlib/tests/test_colors.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@ def test_colormap_copy():
with np.errstate(invalid='ignore'):
ret1 = copied_cmap([-1, 0, .5, 1, np.nan, np.inf])
cmap2 = copy.copy(copied_cmap)
cmap2.set_bad('g')
with pytest.warns(PendingDeprecationWarning):
cmap2.set_bad('g')
with np.errstate(invalid='ignore'):
ret2 = copied_cmap([-1, 0, .5, 1, np.nan, np.inf])
assert_array_equal(ret1, ret2)
Expand All @@ -121,7 +122,8 @@ def test_colormap_copy():
with np.errstate(invalid='ignore'):
ret1 = copied_cmap([-1, 0, .5, 1, np.nan, np.inf])
cmap2 = copy.copy(copied_cmap)
cmap2.set_bad('g')
with pytest.warns(PendingDeprecationWarning):
cmap2.set_bad('g')
with np.errstate(invalid='ignore'):
ret2 = copied_cmap([-1, 0, .5, 1, np.nan, np.inf])
assert_array_equal(ret1, ret2)
Expand All @@ -135,7 +137,8 @@ def test_colormap_equals():
# But the same data should be equal
assert cm_copy == cmap
# Change the copy
cm_copy.set_bad('y')
with pytest.warns(PendingDeprecationWarning):
cm_copy.set_bad('y')
assert cm_copy != cmap
# Make sure we can compare different sizes without failure
cm_copy._lut = cm_copy._lut[:10, :]
Expand Down Expand Up @@ -1535,7 +1538,8 @@ def test_get_under_over_bad():
def test_non_mutable_get_values(kind):
cmap = copy.copy(mpl.colormaps['viridis'])
init_value = getattr(cmap, f'get_{kind}')()
getattr(cmap, f'set_{kind}')('k')
with pytest.warns(PendingDeprecationWarning):
getattr(cmap, f'set_{kind}')('k')
black_value = getattr(cmap, f'get_{kind}')()
assert np.all(black_value == [0, 0, 0, 1])
assert not np.all(init_value == black_value)
Expand Down
Loading