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

Skip to content

Commit c16c3c2

Browse files
committed
Added the from_levels_and_colors function.
1 parent bcf4df6 commit c16c3c2

File tree

7 files changed

+188
-11
lines changed

7 files changed

+188
-11
lines changed

doc/api/api_changes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ Changes in 1.3.x
9999
Deep copying a `Path` always creates an editable (i.e. non-readonly)
100100
`Path`.
101101

102+
* matplotlib.colors.normalize and matplotlib.colors.no_norm have been
103+
deprecated in favour of matplotlib.colors.Normalize and
104+
matplotlib.colors.NoNorm respectively.
105+
102106
* The `font.*` rcParams now affect only text objects created after the
103107
rcParam has been set, and will not retroactively affect already
104108
existing text objects. This brings their behavior in line with most

doc/users/whats_new.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,14 @@ rcParam has been set, and will not retroactively affect already
6868
existing text objects. This brings their behavior in line with most
6969
other rcParams.
7070

71+
Easier creation of colormap and normalizer for levels with colors
72+
-----------------------------------------------------------------
73+
Phil Elson added the :func:`matplotlib.colors.from_levels_and_colors`
74+
function to easily create a colormap and normalizer for representation
75+
of discrete colors for plot types such as
76+
:func:`matplotlib.pyplot.pcolormesh`, with a similar interface to that of
77+
contourf.
78+
7179
Catch opening too many figures using pyplot
7280
-------------------------------------------
7381
Figures created through `pyplot.figure` are retained until they are

lib/matplotlib/cbook.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ def deprecate(func, message=message, name=name, alternative=alternative,
189189
name = func.__name__
190190

191191
message = _generate_deprecation_message(
192-
since, message, name, alternative, pending, 'function')
192+
since, message, name, alternative, pending, obj_type)
193193

194194
@functools.wraps(func)
195195
def deprecated_func(*args, **kwargs):

lib/matplotlib/colorbar.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -853,8 +853,8 @@ def __init__(self, ax, mappable, **kw):
853853
mappable.autoscale_None()
854854

855855
self.mappable = mappable
856-
kw['cmap'] = mappable.cmap
857-
kw['norm'] = mappable.norm
856+
kw['cmap'] = cmap = mappable.cmap
857+
kw['norm'] = norm = mappable.norm
858858

859859
if isinstance(mappable, contour.ContourSet):
860860
CS = mappable
@@ -869,6 +869,9 @@ def __init__(self, ax, mappable, **kw):
869869
if not CS.filled:
870870
self.add_lines(CS)
871871
else:
872+
if getattr(cmap, 'colorbar_extend', False) is not False:
873+
kw.setdefault('extend', cmap.colorbar_extend)
874+
872875
if isinstance(mappable, martist.Artist):
873876
kw['alpha'] = mappable.get_alpha()
874877

lib/matplotlib/colors.py

Lines changed: 81 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,12 @@ def __init__(self, name, N=256):
508508
self._i_bad = N + 2
509509
self._isinit = False
510510

511+
#: When this colormap exists on a scalar mappable and colorbar_extend
512+
#: is not False, colorbar creation will pick up ``colorbar_extend`` as
513+
#: the default value for the ``extend`` keyword in the
514+
#: :class:`matplotlib.colorbar.Colorbar` constructor.
515+
self.colorbar_extend = False
516+
511517
def __call__(self, X, alpha=None, bytes=False):
512518
"""
513519
Parameters
@@ -832,7 +838,7 @@ def _init(self):
832838
class Normalize(object):
833839
"""
834840
A class which, when called, can normalize data into
835-
the ``[0, 1]`` interval.
841+
the ``[0.0, 1.0]`` interval.
836842
837843
"""
838844
def __init__(self, vmin=None, vmax=None, clip=False):
@@ -1212,8 +1218,12 @@ def inverse(self, value):
12121218
return value
12131219

12141220
# compatibility with earlier class names that violated convention:
1215-
normalize = Normalize
1216-
no_norm = NoNorm
1221+
normalize = cbook.deprecated('1.3', alternative='Normalize',
1222+
name='normalize',
1223+
obj_type='class alias')(Normalize)
1224+
no_norm = cbook.deprecated('1.3', alternative='NoNorm',
1225+
name='no_norm',
1226+
obj_type='class alias')(NoNorm)
12171227

12181228

12191229
def rgb_to_hsv(arr):
@@ -1405,3 +1415,71 @@ def shade_rgb(self, rgb, elevation, fraction=1.):
14051415
hsv[:, :, 1:] = np.where(hsv[:, :, 1:] > 1., 1, hsv[:, :, 1:])
14061416
# convert modified hsv back to rgb.
14071417
return hsv_to_rgb(hsv)
1418+
1419+
1420+
def from_levels_and_colors(levels, colors, extend='neither'):
1421+
"""
1422+
A helper routine to generate a cmap and a norm instance which
1423+
behave similar to contourf's levels and colors arguments.
1424+
1425+
Parameters
1426+
----------
1427+
levels : sequence of numbers
1428+
The quantization levels used to construct the :class:`BoundaryNorm`.
1429+
Values ``v`` are quantizized to level ``i`` if
1430+
``lev[i] <= v < lev[i+1]``.
1431+
colors : sequence of colors
1432+
The fill color to use for each level. If `extend` is "neither" there
1433+
must be ``n_level - 1`` colors. For an `extend` of "min" or "max" add
1434+
one extra color, and for an `extend` of "both" add two colors.
1435+
extend : {'neither', 'min', 'max', 'both'}, optional
1436+
The behaviour when a value falls out of range of the given levels.
1437+
See :func:`~matplotlib.pyplot.contourf` for details.
1438+
1439+
Returns
1440+
-------
1441+
(cmap, norm) : tuple containing a :class:`Colormap` and a \
1442+
:class:`Normalize` instance
1443+
"""
1444+
colors_i0 = 0
1445+
colors_i1 = None
1446+
1447+
if extend == 'both':
1448+
colors_i0 = 1
1449+
colors_i1 = -1
1450+
extra_colors = 2
1451+
elif extend == 'min':
1452+
colors_i0 = 1
1453+
extra_colors = 1
1454+
elif extend == 'max':
1455+
colors_i1 = -1
1456+
extra_colors = 1
1457+
elif extend == 'neither':
1458+
extra_colors = 0
1459+
else:
1460+
raise ValueError('Unexpected value for extend: {0!r}'.format(extend))
1461+
1462+
n_data_colors = len(levels) - 1
1463+
n_expected_colors = n_data_colors + extra_colors
1464+
if len(colors) != n_expected_colors:
1465+
raise ValueError('With extend == {0!r} and n_levels == {1!r} expected'
1466+
' n_colors == {2!r}. Got {3!r}.'
1467+
''.format(extend, len(levels), n_expected_colors,
1468+
len(colors)))
1469+
1470+
cmap = ListedColormap(colors[colors_i0:colors_i1], N=n_data_colors)
1471+
1472+
if extend in ['min', 'both']:
1473+
cmap.set_under(colors[0])
1474+
else:
1475+
cmap.set_under('none')
1476+
1477+
if extend in ['max', 'both']:
1478+
cmap.set_over(colors[-1])
1479+
else:
1480+
cmap.set_over('none')
1481+
1482+
cmap.colorbar_extend = extend
1483+
1484+
norm = BoundaryNorm(levels, ncolors=n_data_colors)
1485+
return cmap, norm

lib/matplotlib/tests/test_colors.py

Lines changed: 89 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
"""
2-
Tests for the colors module.
3-
"""
4-
51
from __future__ import print_function
2+
from nose.tools import assert_raises
63
import numpy as np
74
from numpy.testing.utils import assert_array_equal, assert_array_almost_equal
5+
6+
87
import matplotlib.colors as mcolors
98
import matplotlib.cm as cm
9+
import matplotlib.pyplot as plt
10+
from matplotlib.testing.decorators import image_comparison
11+
1012

1113
def test_colormap_endian():
1214
"""
@@ -23,6 +25,7 @@ def test_colormap_endian():
2325
#print(anative.dtype.isnative, aforeign.dtype.isnative)
2426
assert_array_equal(cmap(anative), cmap(aforeign))
2527

28+
2629
def test_BoundaryNorm():
2730
"""
2831
Github issue #1258: interpolation was failing with numpy
@@ -36,7 +39,8 @@ def test_BoundaryNorm():
3639
ncolors = len(boundaries)
3740
bn = mcolors.BoundaryNorm(boundaries, ncolors)
3841
assert_array_equal(bn(vals), expected)
39-
42+
43+
4044
def test_LogNorm():
4145
"""
4246
LogNorm igornoed clip, now it has the same
@@ -46,6 +50,7 @@ def test_LogNorm():
4650
ln = mcolors.LogNorm(clip=True, vmax=5)
4751
assert_array_equal(ln([1, 6]), [0, 1.0])
4852

53+
4954
def test_Normalize():
5055
norm = mcolors.Normalize()
5156
vals = np.arange(-10, 10, 1, dtype=np.float)
@@ -74,6 +79,7 @@ def _inverse_tester(norm_instance, vals):
7479
"""
7580
assert_array_almost_equal(norm_instance.inverse(norm_instance(vals)), vals)
7681

82+
7783
def _scalar_tester(norm_instance, vals):
7884
"""
7985
Checks if scalars and arrays are handled the same way.
@@ -82,10 +88,88 @@ def _scalar_tester(norm_instance, vals):
8288
scalar_result = [norm_instance(float(v)) for v in vals]
8389
assert_array_almost_equal(scalar_result, norm_instance(vals))
8490

91+
8592
def _mask_tester(norm_instance, vals):
8693
"""
8794
Checks mask handling
8895
"""
8996
masked_array = np.ma.array(vals)
9097
masked_array[0] = np.ma.masked
9198
assert_array_equal(masked_array.mask, norm_instance(masked_array).mask)
99+
100+
101+
@image_comparison(baseline_images=['levels_and_colors'],
102+
extensions=['png'])
103+
def test_cmap_and_norm_from_levels_and_colors():
104+
data = np.linspace(-2, 4, 49).reshape(7, 7)
105+
levels = [-1, 2, 2.5, 3]
106+
colors = ['red', 'green', 'blue', 'yellow', 'black']
107+
extend = 'both'
108+
cmap, norm = mcolors.from_levels_and_colors(levels, colors, extend=extend)
109+
110+
ax = plt.axes()
111+
m = plt.pcolormesh(data, cmap=cmap, norm=norm)
112+
plt.colorbar(m)
113+
114+
# Hide the axes labels (but not the colorbar ones, as they are useful)
115+
for lab in ax.get_xticklabels() + ax.get_yticklabels():
116+
lab.set_visible(False)
117+
118+
119+
def test_cmap_and_norm_from_levels_and_colors2():
120+
levels = [-1, 2, 2.5, 3]
121+
colors = ['red', (0, 1, 0), 'blue', (0.5, 0.5, 0.5), (0.0, 0.0, 0.0, 1.0)]
122+
clr = mcolors.colorConverter.to_rgba_array(colors)
123+
bad = (0.1, 0.1, 0.1, 0.1)
124+
no_color = (0.0, 0.0, 0.0, 0.0)
125+
126+
# Define the test values which are of interest.
127+
# Note: levels are lev[i] <= v < lev[i+1]
128+
tests = [('both', None, {-2: clr[0],
129+
-1: clr[1],
130+
2: clr[2],
131+
2.25: clr[2],
132+
3: clr[4],
133+
3.5: clr[4],
134+
np.ma.array(1, mask=True): bad}),
135+
136+
('min', -1, {-2: clr[0],
137+
-1: clr[1],
138+
2: clr[2],
139+
2.25: clr[2],
140+
3: no_color,
141+
3.5: no_color,
142+
np.ma.array(1, mask=True): bad}),
143+
144+
('max', -1, {-2: no_color,
145+
-1: clr[0],
146+
2: clr[1],
147+
2.25: clr[1],
148+
3: clr[3],
149+
3.5: clr[3],
150+
np.ma.array(1, mask=True): bad}),
151+
152+
('neither', -2, {-2: no_color,
153+
-1: clr[0],
154+
2: clr[1],
155+
2.25: clr[1],
156+
3: no_color,
157+
3.5: no_color,
158+
np.ma.array(1, mask=True): bad}),
159+
]
160+
161+
for extend, i1, cases in tests:
162+
cmap, norm = mcolors.from_levels_and_colors(levels, colors[0:i1],
163+
extend=extend)
164+
cmap.set_bad(bad)
165+
for d_val, expected_color in sorted(cases.items()):
166+
assert_array_equal(expected_color, cmap(norm([d_val]))[0],
167+
'Wih extend={0!r} and data '
168+
'value={1!r}'.format(extend, d_val))
169+
170+
assert_raises(ValueError, mcolors.from_levels_and_colors, levels, colors)
171+
172+
173+
if __name__ == '__main__':
174+
import nose
175+
nose.runmodule(argv=['-s', '--with-doctest'], exit=False)

0 commit comments

Comments
 (0)