Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 625bdc7

Browse files
committed
Add a helper to copy a colormap and set its extreme colors.
See changelog. See also the number of explicit copies in the examples, which suggest that the old setter-based API is a bit of a footgun (as forgetting to copy seems easy).
1 parent db06a34 commit 625bdc7

File tree

7 files changed

+48
-33
lines changed

7 files changed

+48
-33
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
`Colormap.set_extremes` and `Colormap.with_extremes`
2+
````````````````````````````````````````````````````
3+
4+
Because the `Colormap.set_bad`, `Colormap.set_under` and `Colormap.set_over`
5+
methods modify the colormap in place, the user must be careful to first make a
6+
copy of the colormap if setting the extreme colors e.g. for a builtin colormap.
7+
8+
The new ``Colormap.with_extremes(bad=..., under=..., over=...)`` can be used to
9+
first copy the colormap and set the extreme colors on that copy.
10+
11+
The new `Colormap.set_extremes` method is provided for API symmetry with
12+
`Colormap.with_extremes`, but note that it suffers from the same issue as the
13+
earlier individual setters.

examples/images_contours_and_fields/image_masked.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
The second subplot illustrates the use of BoundaryNorm to
99
get a filled contour effect.
1010
"""
11-
from copy import copy
1211

1312
import numpy as np
1413
import matplotlib.pyplot as plt
@@ -25,11 +24,7 @@
2524
Z = (Z1 - Z2) * 2
2625

2726
# Set up a colormap:
28-
# use copy so that we do not mutate the global colormap instance
29-
palette = copy(plt.cm.gray)
30-
palette.set_over('r', 1.0)
31-
palette.set_under('g', 1.0)
32-
palette.set_bad('b', 1.0)
27+
palette = plt.cm.gray.with_extremes(over='r', under='g', bad='b')
3328
# Alternatively, we could use
3429
# palette.set_bad(alpha = 0.0)
3530
# to make the bad region transparent. This is the default.

examples/images_contours_and_fields/quadmesh_demo.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99
This demo illustrates a bug in quadmesh with masked data.
1010
"""
1111

12-
import copy
13-
1412
from matplotlib import cm, pyplot as plt
1513
import numpy as np
1614

@@ -30,10 +28,8 @@
3028
axs[0].pcolormesh(Qx, Qz, Z, shading='gouraud')
3129
axs[0].set_title('Without masked values')
3230

33-
# You can control the color of the masked region. We copy the default colormap
34-
# before modifying it.
35-
cmap = copy.copy(cm.get_cmap(plt.rcParams['image.cmap']))
36-
cmap.set_bad('y', 1.0)
31+
# You can control the color of the masked region.
32+
cmap = cm.get_cmap(plt.rcParams['image.cmap']).with_extremes(bad='y')
3733
axs[1].pcolormesh(Qx, Qz, Zm, shading='gouraud', cmap=cmap)
3834
axs[1].set_title('With masked values')
3935

examples/specialty_plots/leftventricle_bulleye.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,9 +176,8 @@ def bullseye_plot(ax, data, seg_bold=None, cmap=None, norm=None):
176176
# The second example illustrates the use of a ListedColormap, a
177177
# BoundaryNorm, and extended ends to show the "over" and "under"
178178
# value colors.
179-
cmap3 = mpl.colors.ListedColormap(['r', 'g', 'b', 'c'])
180-
cmap3.set_over('0.35')
181-
cmap3.set_under('0.75')
179+
cmap3 = (mpl.colors.ListedColormap(['r', 'g', 'b', 'c'])
180+
.with_extremes(over='0.35', under='0.75'))
182181

183182
# If a ListedColormap is used, the length of the bounds array must be
184183
# one greater than the length of the color list. The bounds must be

lib/matplotlib/colors.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
"""
6161

6262
from collections.abc import Sized
63+
import copy
6364
import itertools
6465
import re
6566

@@ -606,8 +607,7 @@ def __copy__(self):
606607
return cmapobject
607608

608609
def set_bad(self, color='k', alpha=None):
609-
"""Set color to be used for masked values.
610-
"""
610+
"""Set color to be used for masked values."""
611611
self._rgba_bad = to_rgba(color, alpha)
612612
if self._isinit:
613613
self._set_extremes()
@@ -628,6 +628,28 @@ def set_over(self, color='k', alpha=None):
628628
if self._isinit:
629629
self._set_extremes()
630630

631+
def set_extremes(self, *, bad=None, under=None, over=None):
632+
"""
633+
Set the colors for masked (*bad*) values and, when ``norm.clip =
634+
False``, low (*under*) and high (*over*) out-of-range values.
635+
"""
636+
if bad is not None:
637+
self.set_bad(bad)
638+
if under is not None:
639+
self.set_under(under)
640+
if over is not None:
641+
self.set_over(over)
642+
643+
def with_extremes(self, *, bad=None, under=None, over=None):
644+
"""
645+
Return a copy of the colormap, for which the colors for masked (*bad*)
646+
values and, when ``norm.clip = False``, low (*under*) and high (*over*)
647+
out-of-range values, have been set accordingly.
648+
"""
649+
new_cm = copy.copy(self)
650+
new_cm.set_extremes(bad=bad, under=under, over=over)
651+
return new_cm
652+
631653
def _set_extremes(self):
632654
if self._rgba_under:
633655
self._lut[self._i_under] = self._rgba_under

lib/matplotlib/tests/test_image.py

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
from contextlib import ExitStack
2-
from copy import copy
32
import io
43
import os
54
import sys
@@ -769,10 +768,7 @@ def test_mask_image_over_under():
769768
(2 * np.pi * 0.5 * 1.5))
770769
Z = 10*(Z2 - Z1) # difference of Gaussians
771770

772-
palette = copy(plt.cm.gray)
773-
palette.set_over('r', 1.0)
774-
palette.set_under('g', 1.0)
775-
palette.set_bad('b', 1.0)
771+
palette = plt.cm.gray.with_extremes(over='r', under='g', bad='b')
776772
Zm = ma.masked_where(Z > 1.2, Z)
777773
fig, (ax1, ax2) = plt.subplots(1, 2)
778774
im = ax1.imshow(Zm, interpolation='bilinear',
@@ -830,10 +826,7 @@ def test_imshow_endianess():
830826
remove_text=True, style='mpl20')
831827
def test_imshow_masked_interpolation():
832828

833-
cm = copy(plt.get_cmap('viridis'))
834-
cm.set_over('r')
835-
cm.set_under('b')
836-
cm.set_bad('k')
829+
cm = plt.get_cmap('viridis').with_extremes(over='r', under='b', bad='k')
837830

838831
N = 20
839832
n = colors.Normalize(vmin=0, vmax=N*N-1)

tutorials/colors/colorbar_only.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,8 @@
6464
fig, ax = plt.subplots(figsize=(6, 1))
6565
fig.subplots_adjust(bottom=0.5)
6666

67-
cmap = mpl.colors.ListedColormap(['red', 'green', 'blue', 'cyan'])
68-
cmap.set_over('0.25')
69-
cmap.set_under('0.75')
67+
cmap = (mpl.colors.ListedColormap(['red', 'green', 'blue', 'cyan'])
68+
.with_extremes(over='0.25', under='0.75'))
7069

7170
bounds = [1, 2, 4, 7, 8]
7271
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)
@@ -91,10 +90,8 @@
9190
fig, ax = plt.subplots(figsize=(6, 1))
9291
fig.subplots_adjust(bottom=0.5)
9392

94-
cmap = mpl.colors.ListedColormap(['royalblue', 'cyan',
95-
'yellow', 'orange'])
96-
cmap.set_over('red')
97-
cmap.set_under('blue')
93+
cmap = (mpl.colors.ListedColormap(['royalblue', 'cyan', 'yellow', 'orange'])
94+
.with_extremes(over='red', under='blue'))
9895

9996
bounds = [-1.0, -0.5, 0.0, 0.5, 1.0]
10097
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)

0 commit comments

Comments
 (0)