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

Skip to content

Commit 49902e9

Browse files
committed
Improve the tests, docstrings, argument checking
The substantive difference is that a ValueError is now raised if BoundaryNorm is called with ncolors < the total number of bins, including any extensions.
1 parent 3b6ac62 commit 49902e9

File tree

2 files changed

+59
-52
lines changed

2 files changed

+59
-52
lines changed

lib/matplotlib/colors.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,7 @@ def __call__(self, X, alpha=None, bytes=False):
530530
"""
531531
Parameters
532532
----------
533-
X : float, ndarray
533+
X : float or int, ndarray or scalar
534534
The data value(s) to convert to RGBA.
535535
For floats, X should be in the interval ``[0.0, 1.0]`` to
536536
return the RGBA values ``X*100`` percent along the Colormap line.
@@ -1410,7 +1410,7 @@ class BoundaryNorm(Normalize):
14101410
interpolation, but using integers seems simpler, and reduces the number of
14111411
conversions back and forth between integer and floating point.
14121412
"""
1413-
def __init__(self, boundaries, ncolors, clip=False, extend='neither'):
1413+
def __init__(self, boundaries, ncolors, clip=False, *, extend='neither'):
14141414
"""
14151415
Parameters
14161416
----------
@@ -1436,14 +1436,18 @@ def __init__(self, boundaries, ncolors, clip=False, extend='neither'):
14361436
`~matplotlib.colorbar.Colorbar` will be drawn with
14371437
the triangle extension on the left or lower end.
14381438
1439+
Returns
1440+
-------
1441+
int16 scalar or array
1442+
14391443
Notes
14401444
-----
14411445
*boundaries* defines the edges of bins, and data falling within a bin
14421446
is mapped to the color with the same index.
14431447
1444-
If the number of bins, including any extensions, doesn't equal
1445-
*ncolors*, the color is chosen by linear interpolation of the
1446-
bin number onto color numbers.
1448+
If the number of bins, including any extensions, is less than
1449+
*ncolors*, the color index is chosen by linear interpolation, mapping
1450+
the ``[0, nbins - 1]`` range onto the ``[0, ncolors - 1]`` range.
14471451
"""
14481452
if clip and extend != 'neither':
14491453
raise ValueError("'clip=True' is not compatible with 'extend'")
@@ -1462,6 +1466,11 @@ def __init__(self, boundaries, ncolors, clip=False, extend='neither'):
14621466
self._offset = 1
14631467
if extend in ('max', 'both'):
14641468
self._N += 1
1469+
if self._N > self.Ncmap:
1470+
raise ValueError(f"There are {self._N} color bins including "
1471+
f"extensions, but ncolors = {ncolors}; "
1472+
"ncolors must equal or exceed the number of "
1473+
"bins")
14651474

14661475
def __call__(self, value, clip=None):
14671476
if clip is None:

lib/matplotlib/tests/test_colors.py

Lines changed: 45 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -265,85 +265,83 @@ def test_BoundaryNorm():
265265
vals = np.ma.masked_invalid([np.Inf])
266266
assert np.all(bn(vals).mask)
267267

268-
# Testing extend keyword
269-
bounds = [1, 2, 3]
270-
cmap = cm.get_cmap('jet')
268+
# Incompatible extend and clip
269+
with pytest.raises(ValueError, match="not compatible"):
270+
mcolors.BoundaryNorm(np.arange(4), 5, extend='both', clip=True)
271271

272-
refnorm = mcolors.BoundaryNorm([0] + bounds + [4], cmap.N)
272+
# Too small ncolors argument
273+
with pytest.raises(ValueError, match="ncolors must equal or exceed"):
274+
mcolors.BoundaryNorm(np.arange(4), 2)
275+
276+
with pytest.raises(ValueError, match="ncolors must equal or exceed"):
277+
mcolors.BoundaryNorm(np.arange(4), 3, extend='min')
278+
279+
with pytest.raises(ValueError, match="ncolors must equal or exceed"):
280+
mcolors.BoundaryNorm(np.arange(4), 4, extend='both')
281+
282+
# Testing extend keyword, with interpolation (large cmap)
283+
bounds = [1, 2, 3]
284+
cmap = cm.get_cmap('viridis')
273285
mynorm = mcolors.BoundaryNorm(bounds, cmap.N, extend='both')
274-
x = np.random.random(100) + 1.5
275-
np.testing.assert_array_equal(refnorm(x), mynorm(x))
286+
refnorm = mcolors.BoundaryNorm([0] + bounds + [4], cmap.N)
287+
x = np.random.randn(100) * 10 + 2
288+
ref = refnorm(x)
289+
ref[ref == 0] = -1
290+
ref[ref == cmap.N - 1] = cmap.N
291+
assert_array_equal(mynorm(x), ref)
276292

277-
# Min and max
293+
# Without interpolation
278294
cmref = mcolors.ListedColormap(['blue', 'red'])
279295
cmref.set_over('black')
280296
cmref.set_under('white')
281-
282297
cmshould = mcolors.ListedColormap(['white', 'blue', 'red', 'black'])
283-
cmshould.set_over(cmshould(cmshould.N))
284-
cmshould.set_under(cmshould(0))
285298

286299
refnorm = mcolors.BoundaryNorm(bounds, cmref.N)
287300
mynorm = mcolors.BoundaryNorm(bounds, cmshould.N, extend='both')
288-
np.testing.assert_array_equal(refnorm.vmin, mynorm.vmin)
289-
np.testing.assert_array_equal(refnorm.vmax, mynorm.vmax)
290-
x = [-1, 1.2, 2.3, 9.6]
291-
np.testing.assert_array_equal(cmshould([0, 1, 2, 3]), cmshould(mynorm(x)))
292-
x = np.random.randn(100) * 10 + 2
293-
np.testing.assert_array_equal(cmref(refnorm(x)), cmshould(mynorm(x)))
301+
assert mynorm.vmin == refnorm.vmin
302+
assert mynorm.vmax == refnorm.vmax
294303

295-
np.testing.assert_array_equal(-1, mynorm(-1))
296-
np.testing.assert_array_equal(1, mynorm(1.1))
297-
np.testing.assert_array_equal(4, mynorm(12))
304+
assert mynorm(bounds[0] - 0.1) == -1 # under
305+
assert mynorm(bounds[0] + 0.1) == 1 # first bin -> second color
306+
assert mynorm(bounds[-1] - 0.1) == cmshould.N - 2 # next-to-last color
307+
assert mynorm(bounds[-1] + 0.1) == cmshould.N # over
298308

299-
# Test raises
300-
with pytest.raises(ValueError):
301-
mcolors.BoundaryNorm(bounds, cmref.N, extend='both', clip=True)
309+
x = [-1, 1.2, 2.3, 9.6]
310+
assert_array_equal(cmshould(mynorm(x)), cmshould([0, 1, 2, 3]))
311+
x = np.random.randn(100) * 10 + 2
312+
assert_array_equal(cmshould(mynorm(x)), cmref(refnorm(x)))
302313

303314
# Just min
304315
cmref = mcolors.ListedColormap(['blue', 'red'])
305316
cmref.set_under('white')
306317
cmshould = mcolors.ListedColormap(['white', 'blue', 'red'])
307-
cmshould.set_under(cmshould(0))
308318

309-
np.testing.assert_array_equal(2, cmref.N)
310-
np.testing.assert_array_equal(3, cmshould.N)
319+
assert cmref.N == 2
320+
assert cmshould.N == 3
311321
refnorm = mcolors.BoundaryNorm(bounds, cmref.N)
312322
mynorm = mcolors.BoundaryNorm(bounds, cmshould.N, extend='min')
313-
np.testing.assert_array_equal(refnorm.vmin, mynorm.vmin)
314-
np.testing.assert_array_equal(refnorm.vmax, mynorm.vmax)
323+
assert mynorm.vmin == refnorm.vmin
324+
assert mynorm.vmax == refnorm.vmax
315325
x = [-1, 1.2, 2.3]
316-
np.testing.assert_array_equal(cmshould([0, 1, 2]), cmshould(mynorm(x)))
326+
assert_array_equal(cmshould(mynorm(x)), cmshould([0, 1, 2]))
317327
x = np.random.randn(100) * 10 + 2
318-
np.testing.assert_array_equal(cmref(refnorm(x)), cmshould(mynorm(x)))
328+
assert_array_equal(cmshould(mynorm(x)), cmref(refnorm(x)))
319329

320330
# Just max
321331
cmref = mcolors.ListedColormap(['blue', 'red'])
322332
cmref.set_over('black')
323333
cmshould = mcolors.ListedColormap(['blue', 'red', 'black'])
324-
cmshould.set_over(cmshould(2))
325334

326-
np.testing.assert_array_equal(2, cmref.N)
327-
np.testing.assert_array_equal(3, cmshould.N)
335+
assert cmref.N == 2
336+
assert cmshould.N == 3
328337
refnorm = mcolors.BoundaryNorm(bounds, cmref.N)
329338
mynorm = mcolors.BoundaryNorm(bounds, cmshould.N, extend='max')
330-
np.testing.assert_array_equal(refnorm.vmin, mynorm.vmin)
331-
np.testing.assert_array_equal(refnorm.vmax, mynorm.vmax)
339+
assert mynorm.vmin == refnorm.vmin
340+
assert mynorm.vmax == refnorm.vmax
332341
x = [1.2, 2.3, 4]
333-
np.testing.assert_array_equal(cmshould([0, 1, 2]), cmshould(mynorm(x)))
342+
assert_array_equal(cmshould(mynorm(x)), cmshould([0, 1, 2]))
334343
x = np.random.randn(100) * 10 + 2
335-
np.testing.assert_array_equal(cmref(refnorm(x)), cmshould(mynorm(x)))
336-
337-
# General case
338-
bounds = [1, 2, 3, 4]
339-
cmap = cm.get_cmap('jet')
340-
mynorm = mcolors.BoundaryNorm(bounds, cmap.N, extend='both')
341-
refnorm = mcolors.BoundaryNorm([-100] + bounds + [100], cmap.N)
342-
x = np.random.randn(100) * 10 - 5
343-
ref = refnorm(x)
344-
ref = np.where(ref == 0, -1, ref)
345-
ref = np.where(ref == cmap.N-1, cmap.N, ref)
346-
np.testing.assert_array_equal(ref, mynorm(x))
344+
assert_array_equal(cmshould(mynorm(x)), cmref(refnorm(x)))
347345

348346

349347
@pytest.mark.parametrize("vmin,vmax", [[-1, 2], [3, 1]])

0 commit comments

Comments
 (0)