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

Skip to content

Commit c12dafd

Browse files
authored
Merge pull request matplotlib#17830 from dstansby/single-boundary
Fix BoundaryNorm for multiple colors and one region
2 parents 2707208 + 39b5dfc commit c12dafd

File tree

2 files changed

+36
-13
lines changed

2 files changed

+36
-13
lines changed

lib/matplotlib/colors.py

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1431,9 +1431,9 @@ def __init__(self, boundaries, ncolors, clip=False, *, extend='neither'):
14311431
Parameters
14321432
----------
14331433
boundaries : array-like
1434-
Monotonically increasing sequence of boundaries
1434+
Monotonically increasing sequence of at least 2 boundaries.
14351435
ncolors : int
1436-
Number of colors in the colormap to be used
1436+
Number of colors in the colormap to be used.
14371437
clip : bool, optional
14381438
If clip is ``True``, out of range values are mapped to 0 if they
14391439
are below ``boundaries[0]`` or mapped to ``ncolors - 1`` if they
@@ -1472,38 +1472,56 @@ def __init__(self, boundaries, ncolors, clip=False, *, extend='neither'):
14721472
self.vmax = boundaries[-1]
14731473
self.boundaries = np.asarray(boundaries)
14741474
self.N = len(self.boundaries)
1475+
if self.N < 2:
1476+
raise ValueError("You must provide at least 2 boundaries "
1477+
f"(1 region) but you passed in {boundaries!r}")
14751478
self.Ncmap = ncolors
14761479
self.extend = extend
14771480

1478-
self._N = self.N - 1 # number of colors needed
1481+
self._n_regions = self.N - 1 # number of colors needed
14791482
self._offset = 0
14801483
if extend in ('min', 'both'):
1481-
self._N += 1
1484+
self._n_regions += 1
14821485
self._offset = 1
14831486
if extend in ('max', 'both'):
1484-
self._N += 1
1485-
if self._N > self.Ncmap:
1486-
raise ValueError(f"There are {self._N} color bins including "
1487-
f"extensions, but ncolors = {ncolors}; "
1488-
"ncolors must equal or exceed the number of "
1489-
"bins")
1487+
self._n_regions += 1
1488+
if self._n_regions > self.Ncmap:
1489+
raise ValueError(f"There are {self._n_regions} color bins "
1490+
"including extensions, but ncolors = "
1491+
f"{ncolors}; ncolors must equal or exceed the "
1492+
"number of bins")
14901493

14911494
def __call__(self, value, clip=None):
14921495
if clip is None:
14931496
clip = self.clip
14941497

14951498
xx, is_scalar = self.process_value(value)
14961499
mask = np.ma.getmaskarray(xx)
1500+
# Fill masked values a value above the upper boundary
14971501
xx = np.atleast_1d(xx.filled(self.vmax + 1))
14981502
if clip:
14991503
np.clip(xx, self.vmin, self.vmax, out=xx)
15001504
max_col = self.Ncmap - 1
15011505
else:
15021506
max_col = self.Ncmap
1507+
# this gives us the bins in the lookup table in the range
1508+
# [0, _n_regions - 1] (the offset is baked in in the init)
15031509
iret = np.digitize(xx, self.boundaries) - 1 + self._offset
1504-
if self.Ncmap > self._N:
1505-
scalefac = (self.Ncmap - 1) / (self._N - 1)
1506-
iret = (iret * scalefac).astype(np.int16)
1510+
# if we have more colors than regions, stretch the region
1511+
# index computed above to full range of the color bins. This
1512+
# will make use of the full range (but skip some of the colors
1513+
# in the middle) such that the first region is mapped to the
1514+
# first color and the last region is mapped to the last color.
1515+
if self.Ncmap > self._n_regions:
1516+
if self._n_regions == 1:
1517+
# special case the 1 region case, pick the middle color
1518+
iret[iret == 0] = (self.Ncmap - 1) // 2
1519+
else:
1520+
# otherwise linearly remap the values from the region index
1521+
# to the color index spaces
1522+
iret = (self.Ncmap - 1) / (self._n_regions - 1) * iret
1523+
# cast to 16bit integers in all cases
1524+
iret = iret.astype(np.int16)
15071525
iret[xx < self.vmin] = -1
15081526
iret[xx >= self.vmax] = max_col
15091527
ret = np.ma.array(iret, mask=mask)

lib/matplotlib/tests/test_colors.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,11 @@ def test_BoundaryNorm():
207207
bn = mcolors.BoundaryNorm(boundaries, ncolors)
208208
assert_array_equal(bn(vals), expected)
209209

210+
# with a single region and interpolation
211+
expected = [-1, 1, 1, 1, 3, 3]
212+
bn = mcolors.BoundaryNorm([0, 2.2], ncolors)
213+
assert_array_equal(bn(vals), expected)
214+
210215
# more boundaries for a third color
211216
boundaries = [0, 1, 2, 3]
212217
vals = [-1, 0.1, 1.1, 2.2, 4]

0 commit comments

Comments
 (0)