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

Skip to content

Commit 5d9245d

Browse files
committed
Merge pull request #4824 from fmaussion/maintenance-colors
Two bugs in colors.BoundaryNorm
2 parents 760e8b9 + be12dc7 commit 5d9245d

File tree

2 files changed

+87
-15
lines changed

2 files changed

+87
-15
lines changed

lib/matplotlib/colors.py

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,9 +1252,12 @@ def __init__(self, boundaries, ncolors, clip=False):
12521252
as i varies from 0 to len(boundaries)-2,
12531253
j goes from 0 to ncolors-1.
12541254
1255-
Out-of-range values are mapped to -1 if low and ncolors
1256-
if high; these are converted to valid indices by
1255+
Out-of-range values are mapped
1256+
to -1 if low and ncolors if high; these are converted
1257+
to valid indices by
12571258
:meth:`Colormap.__call__` .
1259+
If clip == True, out-of-range values
1260+
are mapped to 0 if low and ncolors-1 if high.
12581261
"""
12591262
self.clip = clip
12601263
self.vmin = boundaries[0]
@@ -1267,25 +1270,29 @@ def __init__(self, boundaries, ncolors, clip=False):
12671270
else:
12681271
self._interp = True
12691272

1270-
def __call__(self, x, clip=None):
1273+
def __call__(self, value, clip=None):
12711274
if clip is None:
12721275
clip = self.clip
1273-
x = ma.asarray(x)
1274-
mask = ma.getmaskarray(x)
1275-
xx = x.filled(self.vmax + 1)
1276+
1277+
xx, is_scalar = self.process_value(value)
1278+
mask = ma.getmaskarray(xx)
1279+
xx = np.atleast_1d(xx.filled(self.vmax + 1))
12761280
if clip:
1277-
np.clip(xx, self.vmin, self.vmax)
1278-
iret = np.zeros(x.shape, dtype=np.int16)
1281+
np.clip(xx, self.vmin, self.vmax, out=xx)
1282+
max_col = self.Ncmap - 1
1283+
else:
1284+
max_col = self.Ncmap
1285+
iret = np.zeros(xx.shape, dtype=np.int16)
12791286
for i, b in enumerate(self.boundaries):
12801287
iret[xx >= b] = i
12811288
if self._interp:
12821289
scalefac = float(self.Ncmap - 1) / (self.N - 2)
12831290
iret = (iret * scalefac).astype(np.int16)
12841291
iret[xx < self.vmin] = -1
1285-
iret[xx >= self.vmax] = self.Ncmap
1292+
iret[xx >= self.vmax] = max_col
12861293
ret = ma.array(iret, mask=mask)
1287-
if ret.shape == () and not mask:
1288-
ret = int(ret) # assume python scalar
1294+
if is_scalar:
1295+
ret = int(ret[0]) # assume python scalar
12891296
return ret
12901297

12911298
def inverse(self, value):

lib/matplotlib/tests/test_colors.py

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import itertools
66
from distutils.version import LooseVersion as V
77

8-
from nose.tools import assert_raises, assert_equal
8+
from nose.tools import assert_raises, assert_equal, assert_true
99

1010
import numpy as np
1111
from numpy.testing.utils import assert_array_equal, assert_array_almost_equal
@@ -39,15 +39,80 @@ def test_BoundaryNorm():
3939
Github issue #1258: interpolation was failing with numpy
4040
1.7 pre-release.
4141
"""
42-
# TODO: expand this into a more general test of BoundaryNorm.
42+
4343
boundaries = [0, 1.1, 2.2]
44-
vals = [-1, 0, 2, 2.2, 4]
45-
expected = [-1, 0, 2, 3, 3]
44+
vals = [-1, 0, 1, 2, 2.2, 4]
45+
46+
# Without interpolation
47+
expected = [-1, 0, 0, 1, 2, 2]
48+
ncolors = len(boundaries) - 1
49+
bn = mcolors.BoundaryNorm(boundaries, ncolors)
50+
assert_array_equal(bn(vals), expected)
51+
4652
# ncolors != len(boundaries) - 1 triggers interpolation
53+
expected = [-1, 0, 0, 2, 3, 3]
4754
ncolors = len(boundaries)
4855
bn = mcolors.BoundaryNorm(boundaries, ncolors)
4956
assert_array_equal(bn(vals), expected)
5057

58+
# more boundaries for a third color
59+
boundaries = [0, 1, 2, 3]
60+
vals = [-1, 0.1, 1.1, 2.2, 4]
61+
ncolors = 5
62+
expected = [-1, 0, 2, 4, 5]
63+
bn = mcolors.BoundaryNorm(boundaries, ncolors)
64+
assert_array_equal(bn(vals), expected)
65+
66+
# a scalar as input should not trigger an error and should return a scalar
67+
boundaries = [0, 1, 2]
68+
vals = [-1, 0.1, 1.1, 2.2]
69+
bn = mcolors.BoundaryNorm(boundaries, 2)
70+
expected = [-1, 0, 1, 2]
71+
for v, ex in zip(vals, expected):
72+
ret = bn(v)
73+
assert_true(isinstance(ret, six.integer_types))
74+
assert_array_equal(ret, ex)
75+
assert_array_equal(bn([v]), ex)
76+
77+
# same with interp
78+
bn = mcolors.BoundaryNorm(boundaries, 3)
79+
expected = [-1, 0, 2, 3]
80+
for v, ex in zip(vals, expected):
81+
ret = bn(v)
82+
assert_true(isinstance(ret, six.integer_types))
83+
assert_array_equal(ret, ex)
84+
assert_array_equal(bn([v]), ex)
85+
86+
# Clipping
87+
bn = mcolors.BoundaryNorm(boundaries, 3, clip=True)
88+
expected = [0, 0, 2, 2]
89+
for v, ex in zip(vals, expected):
90+
ret = bn(v)
91+
assert_true(isinstance(ret, six.integer_types))
92+
assert_array_equal(ret, ex)
93+
assert_array_equal(bn([v]), ex)
94+
95+
# Masked arrays
96+
boundaries = [0, 1.1, 2.2]
97+
vals = np.ma.masked_invalid([-1., np.NaN, 0, 1.4, 9])
98+
99+
# Without interpolation
100+
ncolors = len(boundaries) - 1
101+
bn = mcolors.BoundaryNorm(boundaries, ncolors)
102+
expected = np.ma.masked_array([-1, -99, 0, 1, 2], mask=[0, 1, 0, 0, 0])
103+
assert_array_equal(bn(vals), expected)
104+
105+
# With interpolation
106+
bn = mcolors.BoundaryNorm(boundaries, len(boundaries))
107+
expected = np.ma.masked_array([-1, -99, 0, 2, 3], mask=[0, 1, 0, 0, 0])
108+
assert_array_equal(bn(vals), expected)
109+
110+
# Non-trivial masked arrays
111+
vals = np.ma.masked_invalid([np.Inf, np.NaN])
112+
assert_true(np.all(bn(vals).mask))
113+
vals = np.ma.masked_invalid([np.Inf])
114+
assert_true(np.all(bn(vals).mask))
115+
51116

52117
def test_LogNorm():
53118
"""

0 commit comments

Comments
 (0)