Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit c12dafd

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

File tree

Expand file treeCollapse file tree

2 files changed

+36
-13
lines changed
Filter options
Expand file treeCollapse file tree

2 files changed

+36
-13
lines changed

‎lib/matplotlib/colors.py

Copy file name to clipboardExpand all lines: lib/matplotlib/colors.py
+31-13Lines 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

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_colors.py
+5Lines 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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.