Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/matplotlib/cm.py: 29%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
2Builtin colormaps, colormap handling utilities, and the `ScalarMappable` mixin.
4.. seealso::
6 :doc:`/gallery/color/colormap_reference` for a list of builtin colormaps.
8 :ref:`colormap-manipulation` for examples of how to make
9 colormaps.
11 :ref:`colormaps` an in-depth discussion of choosing
12 colormaps.
14 :ref:`colormapnorms` for more details about data normalization.
15"""
17from collections.abc import Mapping
18import functools
20import numpy as np
21from numpy import ma
23import matplotlib as mpl
24from matplotlib import _api, colors, cbook, scale
25from matplotlib._cm import datad
26from matplotlib._cm_listed import cmaps as cmaps_listed
29_LUTSIZE = mpl.rcParams['image.lut']
32def _gen_cmap_registry():
33 """
34 Generate a dict mapping standard colormap names to standard colormaps, as
35 well as the reversed colormaps.
36 """
37 cmap_d = {**cmaps_listed}
38 for name, spec in datad.items():
39 cmap_d[name] = ( # Precache the cmaps at a fixed lutsize..
40 colors.LinearSegmentedColormap(name, spec, _LUTSIZE)
41 if 'red' in spec else
42 colors.ListedColormap(spec['listed'], name)
43 if 'listed' in spec else
44 colors.LinearSegmentedColormap.from_list(name, spec, _LUTSIZE))
46 # Register colormap aliases for gray and grey.
47 cmap_d['grey'] = cmap_d['gray']
48 cmap_d['gist_grey'] = cmap_d['gist_gray']
49 cmap_d['gist_yerg'] = cmap_d['gist_yarg']
50 cmap_d['Grays'] = cmap_d['Greys']
52 # Generate reversed cmaps.
53 for cmap in list(cmap_d.values()):
54 rmap = cmap.reversed()
55 cmap_d[rmap.name] = rmap
56 return cmap_d
59class ColormapRegistry(Mapping):
60 r"""
61 Container for colormaps that are known to Matplotlib by name.
63 The universal registry instance is `matplotlib.colormaps`. There should be
64 no need for users to instantiate `.ColormapRegistry` themselves.
66 Read access uses a dict-like interface mapping names to `.Colormap`\s::
68 import matplotlib as mpl
69 cmap = mpl.colormaps['viridis']
71 Returned `.Colormap`\s are copies, so that their modification does not
72 change the global definition of the colormap.
74 Additional colormaps can be added via `.ColormapRegistry.register`::
76 mpl.colormaps.register(my_colormap)
78 To get a list of all registered colormaps, you can do::
80 from matplotlib import colormaps
81 list(colormaps)
82 """
83 def __init__(self, cmaps):
84 self._cmaps = cmaps
85 self._builtin_cmaps = tuple(cmaps)
87 def __getitem__(self, item):
88 try:
89 return self._cmaps[item].copy()
90 except KeyError:
91 raise KeyError(f"{item!r} is not a known colormap name") from None
93 def __iter__(self):
94 return iter(self._cmaps)
96 def __len__(self):
97 return len(self._cmaps)
99 def __str__(self):
100 return ('ColormapRegistry; available colormaps:\n' +
101 ', '.join(f"'{name}'" for name in self))
103 def __call__(self):
104 """
105 Return a list of the registered colormap names.
107 This exists only for backward-compatibility in `.pyplot` which had a
108 ``plt.colormaps()`` method. The recommended way to get this list is
109 now ``list(colormaps)``.
110 """
111 return list(self)
113 def register(self, cmap, *, name=None, force=False):
114 """
115 Register a new colormap.
117 The colormap name can then be used as a string argument to any ``cmap``
118 parameter in Matplotlib. It is also available in ``pyplot.get_cmap``.
120 The colormap registry stores a copy of the given colormap, so that
121 future changes to the original colormap instance do not affect the
122 registered colormap. Think of this as the registry taking a snapshot
123 of the colormap at registration.
125 Parameters
126 ----------
127 cmap : matplotlib.colors.Colormap
128 The colormap to register.
130 name : str, optional
131 The name for the colormap. If not given, ``cmap.name`` is used.
133 force : bool, default: False
134 If False, a ValueError is raised if trying to overwrite an already
135 registered name. True supports overwriting registered colormaps
136 other than the builtin colormaps.
137 """
138 _api.check_isinstance(colors.Colormap, cmap=cmap)
140 name = name or cmap.name
141 if name in self:
142 if not force:
143 # don't allow registering an already existing cmap
144 # unless explicitly asked to
145 raise ValueError(
146 f'A colormap named "{name}" is already registered.')
147 elif name in self._builtin_cmaps:
148 # We don't allow overriding a builtin.
149 raise ValueError("Re-registering the builtin cmap "
150 f"{name!r} is not allowed.")
152 # Warn that we are updating an already existing colormap
153 _api.warn_external(f"Overwriting the cmap {name!r} "
154 "that was already in the registry.")
156 self._cmaps[name] = cmap.copy()
157 # Someone may set the extremes of a builtin colormap and want to register it
158 # with a different name for future lookups. The object would still have the
159 # builtin name, so we should update it to the registered name
160 if self._cmaps[name].name != name:
161 self._cmaps[name].name = name
163 def unregister(self, name):
164 """
165 Remove a colormap from the registry.
167 You cannot remove built-in colormaps.
169 If the named colormap is not registered, returns with no error, raises
170 if you try to de-register a default colormap.
172 .. warning::
174 Colormap names are currently a shared namespace that may be used
175 by multiple packages. Use `unregister` only if you know you
176 have registered that name before. In particular, do not
177 unregister just in case to clean the name before registering a
178 new colormap.
180 Parameters
181 ----------
182 name : str
183 The name of the colormap to be removed.
185 Raises
186 ------
187 ValueError
188 If you try to remove a default built-in colormap.
189 """
190 if name in self._builtin_cmaps:
191 raise ValueError(f"cannot unregister {name!r} which is a builtin "
192 "colormap.")
193 self._cmaps.pop(name, None)
195 def get_cmap(self, cmap):
196 """
197 Return a color map specified through *cmap*.
199 Parameters
200 ----------
201 cmap : str or `~matplotlib.colors.Colormap` or None
203 - if a `.Colormap`, return it
204 - if a string, look it up in ``mpl.colormaps``
205 - if None, return the Colormap defined in :rc:`image.cmap`
207 Returns
208 -------
209 Colormap
210 """
211 # get the default color map
212 if cmap is None:
213 return self[mpl.rcParams["image.cmap"]]
215 # if the user passed in a Colormap, simply return it
216 if isinstance(cmap, colors.Colormap):
217 return cmap
218 if isinstance(cmap, str):
219 _api.check_in_list(sorted(_colormaps), cmap=cmap)
220 # otherwise, it must be a string so look it up
221 return self[cmap]
222 raise TypeError(
223 'get_cmap expects None or an instance of a str or Colormap . ' +
224 f'you passed {cmap!r} of type {type(cmap)}'
225 )
228# public access to the colormaps should be via `matplotlib.colormaps`. For now,
229# we still create the registry here, but that should stay an implementation
230# detail.
231_colormaps = ColormapRegistry(_gen_cmap_registry())
232globals().update(_colormaps)
235# This is an exact copy of pyplot.get_cmap(). It was removed in 3.9, but apparently
236# caused more user trouble than expected. Re-added for 3.9.1 and extended the
237# deprecation period for two additional minor releases.
238@_api.deprecated(
239 '3.7',
240 removal='3.11',
241 alternative="``matplotlib.colormaps[name]`` or ``matplotlib.colormaps.get_cmap()``"
242 " or ``pyplot.get_cmap()``"
243 )
244def get_cmap(name=None, lut=None):
245 """
246 Get a colormap instance, defaulting to rc values if *name* is None.
248 Parameters
249 ----------
250 name : `~matplotlib.colors.Colormap` or str or None, default: None
251 If a `.Colormap` instance, it will be returned. Otherwise, the name of
252 a colormap known to Matplotlib, which will be resampled by *lut*. The
253 default, None, means :rc:`image.cmap`.
254 lut : int or None, default: None
255 If *name* is not already a Colormap instance and *lut* is not None, the
256 colormap will be resampled to have *lut* entries in the lookup table.
258 Returns
259 -------
260 Colormap
261 """
262 if name is None:
263 name = mpl.rcParams['image.cmap']
264 if isinstance(name, colors.Colormap):
265 return name
266 _api.check_in_list(sorted(_colormaps), name=name)
267 if lut is None:
268 return _colormaps[name]
269 else:
270 return _colormaps[name].resampled(lut)
273def _auto_norm_from_scale(scale_cls):
274 """
275 Automatically generate a norm class from *scale_cls*.
277 This differs from `.colors.make_norm_from_scale` in the following points:
279 - This function is not a class decorator, but directly returns a norm class
280 (as if decorating `.Normalize`).
281 - The scale is automatically constructed with ``nonpositive="mask"``, if it
282 supports such a parameter, to work around the difference in defaults
283 between standard scales (which use "clip") and norms (which use "mask").
285 Note that ``make_norm_from_scale`` caches the generated norm classes
286 (not the instances) and reuses them for later calls. For example,
287 ``type(_auto_norm_from_scale("log")) == LogNorm``.
288 """
289 # Actually try to construct an instance, to verify whether
290 # ``nonpositive="mask"`` is supported.
291 try:
292 norm = colors.make_norm_from_scale(
293 functools.partial(scale_cls, nonpositive="mask"))(
294 colors.Normalize)()
295 except TypeError:
296 norm = colors.make_norm_from_scale(scale_cls)(
297 colors.Normalize)()
298 return type(norm)
301class ScalarMappable:
302 """
303 A mixin class to map scalar data to RGBA.
305 The ScalarMappable applies data normalization before returning RGBA colors
306 from the given colormap.
307 """
309 def __init__(self, norm=None, cmap=None):
310 """
311 Parameters
312 ----------
313 norm : `.Normalize` (or subclass thereof) or str or None
314 The normalizing object which scales data, typically into the
315 interval ``[0, 1]``.
316 If a `str`, a `.Normalize` subclass is dynamically generated based
317 on the scale with the corresponding name.
318 If *None*, *norm* defaults to a *colors.Normalize* object which
319 initializes its scaling based on the first data processed.
320 cmap : str or `~matplotlib.colors.Colormap`
321 The colormap used to map normalized data values to RGBA colors.
322 """
323 self._A = None
324 self._norm = None # So that the setter knows we're initializing.
325 self.set_norm(norm) # The Normalize instance of this ScalarMappable.
326 self.cmap = None # So that the setter knows we're initializing.
327 self.set_cmap(cmap) # The Colormap instance of this ScalarMappable.
328 #: The last colorbar associated with this ScalarMappable. May be None.
329 self.colorbar = None
330 self.callbacks = cbook.CallbackRegistry(signals=["changed"])
332 def _scale_norm(self, norm, vmin, vmax):
333 """
334 Helper for initial scaling.
336 Used by public functions that create a ScalarMappable and support
337 parameters *vmin*, *vmax* and *norm*. This makes sure that a *norm*
338 will take precedence over *vmin*, *vmax*.
340 Note that this method does not set the norm.
341 """
342 if vmin is not None or vmax is not None:
343 self.set_clim(vmin, vmax)
344 if isinstance(norm, colors.Normalize):
345 raise ValueError(
346 "Passing a Normalize instance simultaneously with "
347 "vmin/vmax is not supported. Please pass vmin/vmax "
348 "directly to the norm when creating it.")
350 # always resolve the autoscaling so we have concrete limits
351 # rather than deferring to draw time.
352 self.autoscale_None()
354 def to_rgba(self, x, alpha=None, bytes=False, norm=True):
355 """
356 Return a normalized RGBA array corresponding to *x*.
358 In the normal case, *x* is a 1D or 2D sequence of scalars, and
359 the corresponding `~numpy.ndarray` of RGBA values will be returned,
360 based on the norm and colormap set for this ScalarMappable.
362 There is one special case, for handling images that are already
363 RGB or RGBA, such as might have been read from an image file.
364 If *x* is an `~numpy.ndarray` with 3 dimensions,
365 and the last dimension is either 3 or 4, then it will be
366 treated as an RGB or RGBA array, and no mapping will be done.
367 The array can be `~numpy.uint8`, or it can be floats with
368 values in the 0-1 range; otherwise a ValueError will be raised.
369 Any NaNs or masked elements will be set to 0 alpha.
370 If the last dimension is 3, the *alpha* kwarg (defaulting to 1)
371 will be used to fill in the transparency. If the last dimension
372 is 4, the *alpha* kwarg is ignored; it does not
373 replace the preexisting alpha. A ValueError will be raised
374 if the third dimension is other than 3 or 4.
376 In either case, if *bytes* is *False* (default), the RGBA
377 array will be floats in the 0-1 range; if it is *True*,
378 the returned RGBA array will be `~numpy.uint8` in the 0 to 255 range.
380 If norm is False, no normalization of the input data is
381 performed, and it is assumed to be in the range (0-1).
383 """
384 # First check for special case, image input:
385 try:
386 if x.ndim == 3:
387 if x.shape[2] == 3:
388 if alpha is None:
389 alpha = 1
390 if x.dtype == np.uint8:
391 alpha = np.uint8(alpha * 255)
392 m, n = x.shape[:2]
393 xx = np.empty(shape=(m, n, 4), dtype=x.dtype)
394 xx[:, :, :3] = x
395 xx[:, :, 3] = alpha
396 elif x.shape[2] == 4:
397 xx = x
398 else:
399 raise ValueError("Third dimension must be 3 or 4")
400 if xx.dtype.kind == 'f':
401 # If any of R, G, B, or A is nan, set to 0
402 if np.any(nans := np.isnan(x)):
403 if x.shape[2] == 4:
404 xx = xx.copy()
405 xx[np.any(nans, axis=2), :] = 0
407 if norm and (xx.max() > 1 or xx.min() < 0):
408 raise ValueError("Floating point image RGB values "
409 "must be in the 0..1 range.")
410 if bytes:
411 xx = (xx * 255).astype(np.uint8)
412 elif xx.dtype == np.uint8:
413 if not bytes:
414 xx = xx.astype(np.float32) / 255
415 else:
416 raise ValueError("Image RGB array must be uint8 or "
417 "floating point; found %s" % xx.dtype)
418 # Account for any masked entries in the original array
419 # If any of R, G, B, or A are masked for an entry, we set alpha to 0
420 if np.ma.is_masked(x):
421 xx[np.any(np.ma.getmaskarray(x), axis=2), 3] = 0
422 return xx
423 except AttributeError:
424 # e.g., x is not an ndarray; so try mapping it
425 pass
427 # This is the normal case, mapping a scalar array:
428 x = ma.asarray(x)
429 if norm:
430 x = self.norm(x)
431 rgba = self.cmap(x, alpha=alpha, bytes=bytes)
432 return rgba
434 def set_array(self, A):
435 """
436 Set the value array from array-like *A*.
438 Parameters
439 ----------
440 A : array-like or None
441 The values that are mapped to colors.
443 The base class `.ScalarMappable` does not make any assumptions on
444 the dimensionality and shape of the value array *A*.
445 """
446 if A is None:
447 self._A = None
448 return
450 A = cbook.safe_masked_invalid(A, copy=True)
451 if not np.can_cast(A.dtype, float, "same_kind"):
452 raise TypeError(f"Image data of dtype {A.dtype} cannot be "
453 "converted to float")
455 self._A = A
456 if not self.norm.scaled():
457 self.norm.autoscale_None(A)
459 def get_array(self):
460 """
461 Return the array of values, that are mapped to colors.
463 The base class `.ScalarMappable` does not make any assumptions on
464 the dimensionality and shape of the array.
465 """
466 return self._A
468 def get_cmap(self):
469 """Return the `.Colormap` instance."""
470 return self.cmap
472 def get_clim(self):
473 """
474 Return the values (min, max) that are mapped to the colormap limits.
475 """
476 return self.norm.vmin, self.norm.vmax
478 def set_clim(self, vmin=None, vmax=None):
479 """
480 Set the norm limits for image scaling.
482 Parameters
483 ----------
484 vmin, vmax : float
485 The limits.
487 The limits may also be passed as a tuple (*vmin*, *vmax*) as a
488 single positional argument.
490 .. ACCEPTS: (vmin: float, vmax: float)
491 """
492 # If the norm's limits are updated self.changed() will be called
493 # through the callbacks attached to the norm
494 if vmax is None:
495 try:
496 vmin, vmax = vmin
497 except (TypeError, ValueError):
498 pass
499 if vmin is not None:
500 self.norm.vmin = colors._sanitize_extrema(vmin)
501 if vmax is not None:
502 self.norm.vmax = colors._sanitize_extrema(vmax)
504 def get_alpha(self):
505 """
506 Returns
507 -------
508 float
509 Always returns 1.
510 """
511 # This method is intended to be overridden by Artist sub-classes
512 return 1.
514 def set_cmap(self, cmap):
515 """
516 Set the colormap for luminance data.
518 Parameters
519 ----------
520 cmap : `.Colormap` or str or None
521 """
522 in_init = self.cmap is None
524 self.cmap = _ensure_cmap(cmap)
525 if not in_init:
526 self.changed() # Things are not set up properly yet.
528 @property
529 def norm(self):
530 return self._norm
532 @norm.setter
533 def norm(self, norm):
534 _api.check_isinstance((colors.Normalize, str, None), norm=norm)
535 if norm is None:
536 norm = colors.Normalize()
537 elif isinstance(norm, str):
538 try:
539 scale_cls = scale._scale_mapping[norm]
540 except KeyError:
541 raise ValueError(
542 "Invalid norm str name; the following values are "
543 f"supported: {', '.join(scale._scale_mapping)}"
544 ) from None
545 norm = _auto_norm_from_scale(scale_cls)()
547 if norm is self.norm:
548 # We aren't updating anything
549 return
551 in_init = self.norm is None
552 # Remove the current callback and connect to the new one
553 if not in_init:
554 self.norm.callbacks.disconnect(self._id_norm)
555 self._norm = norm
556 self._id_norm = self.norm.callbacks.connect('changed',
557 self.changed)
558 if not in_init:
559 self.changed()
561 def set_norm(self, norm):
562 """
563 Set the normalization instance.
565 Parameters
566 ----------
567 norm : `.Normalize` or str or None
569 Notes
570 -----
571 If there are any colorbars using the mappable for this norm, setting
572 the norm of the mappable will reset the norm, locator, and formatters
573 on the colorbar to default.
574 """
575 self.norm = norm
577 def autoscale(self):
578 """
579 Autoscale the scalar limits on the norm instance using the
580 current array
581 """
582 if self._A is None:
583 raise TypeError('You must first set_array for mappable')
584 # If the norm's limits are updated self.changed() will be called
585 # through the callbacks attached to the norm
586 self.norm.autoscale(self._A)
588 def autoscale_None(self):
589 """
590 Autoscale the scalar limits on the norm instance using the
591 current array, changing only limits that are None
592 """
593 if self._A is None:
594 raise TypeError('You must first set_array for mappable')
595 # If the norm's limits are updated self.changed() will be called
596 # through the callbacks attached to the norm
597 self.norm.autoscale_None(self._A)
599 def changed(self):
600 """
601 Call this whenever the mappable is changed to notify all the
602 callbackSM listeners to the 'changed' signal.
603 """
604 self.callbacks.process('changed', self)
605 self.stale = True
608# The docstrings here must be generic enough to apply to all relevant methods.
609mpl._docstring.interpd.update(
610 cmap_doc="""\
611cmap : str or `~matplotlib.colors.Colormap`, default: :rc:`image.cmap`
612 The Colormap instance or registered colormap name used to map scalar data
613 to colors.""",
614 norm_doc="""\
615norm : str or `~matplotlib.colors.Normalize`, optional
616 The normalization method used to scale scalar data to the [0, 1] range
617 before mapping to colors using *cmap*. By default, a linear scaling is
618 used, mapping the lowest value to 0 and the highest to 1.
620 If given, this can be one of the following:
622 - An instance of `.Normalize` or one of its subclasses
623 (see :ref:`colormapnorms`).
624 - A scale name, i.e. one of "linear", "log", "symlog", "logit", etc. For a
625 list of available scales, call `matplotlib.scale.get_scale_names()`.
626 In that case, a suitable `.Normalize` subclass is dynamically generated
627 and instantiated.""",
628 vmin_vmax_doc="""\
629vmin, vmax : float, optional
630 When using scalar data and no explicit *norm*, *vmin* and *vmax* define
631 the data range that the colormap covers. By default, the colormap covers
632 the complete value range of the supplied data. It is an error to use
633 *vmin*/*vmax* when a *norm* instance is given (but using a `str` *norm*
634 name together with *vmin*/*vmax* is acceptable).""",
635)
638def _ensure_cmap(cmap):
639 """
640 Ensure that we have a `.Colormap` object.
642 For internal use to preserve type stability of errors.
644 Parameters
645 ----------
646 cmap : None, str, Colormap
648 - if a `Colormap`, return it
649 - if a string, look it up in mpl.colormaps
650 - if None, look up the default color map in mpl.colormaps
652 Returns
653 -------
654 Colormap
656 """
657 if isinstance(cmap, colors.Colormap):
658 return cmap
659 cmap_name = cmap if cmap is not None else mpl.rcParams["image.cmap"]
660 # use check_in_list to ensure type stability of the exception raised by
661 # the internal usage of this (ValueError vs KeyError)
662 if cmap_name not in _colormaps:
663 _api.check_in_list(sorted(_colormaps), cmap=cmap_name)
664 return mpl.colormaps[cmap_name]