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

211 statements  

1""" 

2Builtin colormaps, colormap handling utilities, and the `ScalarMappable` mixin. 

3 

4.. seealso:: 

5 

6 :doc:`/gallery/color/colormap_reference` for a list of builtin colormaps. 

7 

8 :ref:`colormap-manipulation` for examples of how to make 

9 colormaps. 

10 

11 :ref:`colormaps` an in-depth discussion of choosing 

12 colormaps. 

13 

14 :ref:`colormapnorms` for more details about data normalization. 

15""" 

16 

17from collections.abc import Mapping 

18import functools 

19 

20import numpy as np 

21from numpy import ma 

22 

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 

27 

28 

29_LUTSIZE = mpl.rcParams['image.lut'] 

30 

31 

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)) 

45 

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'] 

51 

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 

57 

58 

59class ColormapRegistry(Mapping): 

60 r""" 

61 Container for colormaps that are known to Matplotlib by name. 

62 

63 The universal registry instance is `matplotlib.colormaps`. There should be 

64 no need for users to instantiate `.ColormapRegistry` themselves. 

65 

66 Read access uses a dict-like interface mapping names to `.Colormap`\s:: 

67 

68 import matplotlib as mpl 

69 cmap = mpl.colormaps['viridis'] 

70 

71 Returned `.Colormap`\s are copies, so that their modification does not 

72 change the global definition of the colormap. 

73 

74 Additional colormaps can be added via `.ColormapRegistry.register`:: 

75 

76 mpl.colormaps.register(my_colormap) 

77 

78 To get a list of all registered colormaps, you can do:: 

79 

80 from matplotlib import colormaps 

81 list(colormaps) 

82 """ 

83 def __init__(self, cmaps): 

84 self._cmaps = cmaps 

85 self._builtin_cmaps = tuple(cmaps) 

86 

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 

92 

93 def __iter__(self): 

94 return iter(self._cmaps) 

95 

96 def __len__(self): 

97 return len(self._cmaps) 

98 

99 def __str__(self): 

100 return ('ColormapRegistry; available colormaps:\n' + 

101 ', '.join(f"'{name}'" for name in self)) 

102 

103 def __call__(self): 

104 """ 

105 Return a list of the registered colormap names. 

106 

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) 

112 

113 def register(self, cmap, *, name=None, force=False): 

114 """ 

115 Register a new colormap. 

116 

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``. 

119 

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. 

124 

125 Parameters 

126 ---------- 

127 cmap : matplotlib.colors.Colormap 

128 The colormap to register. 

129 

130 name : str, optional 

131 The name for the colormap. If not given, ``cmap.name`` is used. 

132 

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) 

139 

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.") 

151 

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.") 

155 

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 

162 

163 def unregister(self, name): 

164 """ 

165 Remove a colormap from the registry. 

166 

167 You cannot remove built-in colormaps. 

168 

169 If the named colormap is not registered, returns with no error, raises 

170 if you try to de-register a default colormap. 

171 

172 .. warning:: 

173 

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. 

179 

180 Parameters 

181 ---------- 

182 name : str 

183 The name of the colormap to be removed. 

184 

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) 

194 

195 def get_cmap(self, cmap): 

196 """ 

197 Return a color map specified through *cmap*. 

198 

199 Parameters 

200 ---------- 

201 cmap : str or `~matplotlib.colors.Colormap` or None 

202 

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` 

206 

207 Returns 

208 ------- 

209 Colormap 

210 """ 

211 # get the default color map 

212 if cmap is None: 

213 return self[mpl.rcParams["image.cmap"]] 

214 

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 ) 

226 

227 

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) 

233 

234 

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. 

247 

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. 

257 

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) 

271 

272 

273def _auto_norm_from_scale(scale_cls): 

274 """ 

275 Automatically generate a norm class from *scale_cls*. 

276 

277 This differs from `.colors.make_norm_from_scale` in the following points: 

278 

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"). 

284 

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) 

299 

300 

301class ScalarMappable: 

302 """ 

303 A mixin class to map scalar data to RGBA. 

304 

305 The ScalarMappable applies data normalization before returning RGBA colors 

306 from the given colormap. 

307 """ 

308 

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"]) 

331 

332 def _scale_norm(self, norm, vmin, vmax): 

333 """ 

334 Helper for initial scaling. 

335 

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*. 

339 

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.") 

349 

350 # always resolve the autoscaling so we have concrete limits 

351 # rather than deferring to draw time. 

352 self.autoscale_None() 

353 

354 def to_rgba(self, x, alpha=None, bytes=False, norm=True): 

355 """ 

356 Return a normalized RGBA array corresponding to *x*. 

357 

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. 

361 

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. 

375 

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. 

379 

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). 

382 

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 

406 

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 

426 

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 

433 

434 def set_array(self, A): 

435 """ 

436 Set the value array from array-like *A*. 

437 

438 Parameters 

439 ---------- 

440 A : array-like or None 

441 The values that are mapped to colors. 

442 

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 

449 

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") 

454 

455 self._A = A 

456 if not self.norm.scaled(): 

457 self.norm.autoscale_None(A) 

458 

459 def get_array(self): 

460 """ 

461 Return the array of values, that are mapped to colors. 

462 

463 The base class `.ScalarMappable` does not make any assumptions on 

464 the dimensionality and shape of the array. 

465 """ 

466 return self._A 

467 

468 def get_cmap(self): 

469 """Return the `.Colormap` instance.""" 

470 return self.cmap 

471 

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 

477 

478 def set_clim(self, vmin=None, vmax=None): 

479 """ 

480 Set the norm limits for image scaling. 

481 

482 Parameters 

483 ---------- 

484 vmin, vmax : float 

485 The limits. 

486 

487 The limits may also be passed as a tuple (*vmin*, *vmax*) as a 

488 single positional argument. 

489 

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) 

503 

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. 

513 

514 def set_cmap(self, cmap): 

515 """ 

516 Set the colormap for luminance data. 

517 

518 Parameters 

519 ---------- 

520 cmap : `.Colormap` or str or None 

521 """ 

522 in_init = self.cmap is None 

523 

524 self.cmap = _ensure_cmap(cmap) 

525 if not in_init: 

526 self.changed() # Things are not set up properly yet. 

527 

528 @property 

529 def norm(self): 

530 return self._norm 

531 

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)() 

546 

547 if norm is self.norm: 

548 # We aren't updating anything 

549 return 

550 

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() 

560 

561 def set_norm(self, norm): 

562 """ 

563 Set the normalization instance. 

564 

565 Parameters 

566 ---------- 

567 norm : `.Normalize` or str or None 

568 

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 

576 

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) 

587 

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) 

598 

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 

606 

607 

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. 

619 

620 If given, this can be one of the following: 

621 

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) 

636 

637 

638def _ensure_cmap(cmap): 

639 """ 

640 Ensure that we have a `.Colormap` object. 

641 

642 For internal use to preserve type stability of errors. 

643 

644 Parameters 

645 ---------- 

646 cmap : None, str, Colormap 

647 

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 

651 

652 Returns 

653 ------- 

654 Colormap 

655 

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]