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

Skip to content

Commit 17aeeea

Browse files
committed
Set norms using scale names.
1 parent e13f0bd commit 17aeeea

File tree

2 files changed

+61
-9
lines changed

2 files changed

+61
-9
lines changed

lib/matplotlib/cm.py

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@
1616
"""
1717

1818
from collections.abc import MutableMapping
19+
import functools
1920

2021
import numpy as np
2122
from numpy import ma
2223

2324
import matplotlib as mpl
24-
from matplotlib import _api, colors, cbook
25+
from matplotlib import _api, colors, cbook, scale
2526
from matplotlib._cm import datad
2627
from matplotlib._cm_listed import cmaps as cmaps_listed
2728

@@ -235,6 +236,36 @@ def unregister_cmap(name):
235236
return _cmap_registry.pop(name)
236237

237238

239+
@functools.lru_cache(None)
240+
def _auto_norm_from_scale(scale_cls):
241+
"""
242+
Automatically generate a norm class from *scale_cls*.
243+
244+
This differs from `.colors.make_norm_from_scale` in the following points:
245+
246+
- This function is not a class decorator, but directly returns a norm class
247+
(as if decorating `.Normalize`).
248+
- The scale is automatically constructed with ``nonpositive="mask"``, if it
249+
supports such a parameter, to work around the difference in defaults
250+
between standard scales (which use "clip") and norms (which use "mask").
251+
- The returned norm class is memoized and reused for later calls. (This
252+
would be desirable in `.colors.make_norm_from_scale` as well, but
253+
because that function sometimes gets called with `functools.partial`
254+
instances which always compare unequal, a simple ``@lru_cache`` would be
255+
insufficient.)
256+
"""
257+
try:
258+
norm = colors.make_norm_from_scale(
259+
functools.partial(scale_cls, nonpositive="mask"))(
260+
colors.Normalize)()
261+
except TypeError:
262+
norm = colors.make_norm_from_scale(scale_cls)(
263+
colors.Normalize)()
264+
norm_cls = type(norm)
265+
norm_cls.__name__ = f"{scale_cls.__name__}Norm"
266+
return norm_cls
267+
268+
238269
class ScalarMappable:
239270
"""
240271
A mixin class to map scalar data to RGBA.
@@ -245,12 +276,13 @@ class ScalarMappable:
245276

246277
def __init__(self, norm=None, cmap=None):
247278
"""
248-
249279
Parameters
250280
----------
251-
norm : `matplotlib.colors.Normalize` (or subclass thereof)
281+
norm : `.Normalize` (or subclass thereof) or str or None
252282
The normalizing object which scales data, typically into the
253283
interval ``[0, 1]``.
284+
If a `str`, a `.Normalize` subclass is dynamically generated based
285+
on the scale with the corresponding name.
254286
If *None*, *norm* defaults to a *colors.Normalize* object which
255287
initializes its scaling based on the first data processed.
256288
cmap : str or `~matplotlib.colors.Colormap`
@@ -277,11 +309,11 @@ def _scale_norm(self, norm, vmin, vmax):
277309
"""
278310
if vmin is not None or vmax is not None:
279311
self.set_clim(vmin, vmax)
280-
if norm is not None:
312+
if isinstance(norm, colors.Normalize):
281313
raise ValueError(
282-
"Passing parameters norm and vmin/vmax simultaneously is "
283-
"not supported. Please pass vmin/vmax directly to the "
284-
"norm when creating it.")
314+
"Passing a Normalize instance simultaneously with "
315+
"vmin/vmax is not supported. Please pass vmin/vmax "
316+
"directly to the norm when creating it.")
285317

286318
# always resolve the autoscaling so we have concrete limits
287319
# rather than deferring to draw time.
@@ -454,18 +486,22 @@ def set_norm(self, norm):
454486
455487
Parameters
456488
----------
457-
norm : `.Normalize` or None
489+
norm : `.Normalize` or str or None
458490
459491
Notes
460492
-----
461493
If there are any colorbars using the mappable for this norm, setting
462494
the norm of the mappable will reset the norm, locator, and formatters
463495
on the colorbar to default.
464496
"""
465-
_api.check_isinstance((colors.Normalize, None), norm=norm)
497+
_api.check_isinstance((colors.Normalize, str, None), norm=norm)
466498
in_init = self.norm is None
467499
if norm is None:
468500
norm = colors.Normalize()
501+
elif isinstance(norm, str):
502+
# case-insensitive, consistently with scale_factory.
503+
scale_cls = scale._scale_mapping[norm.lower()]
504+
norm = _auto_norm_from_scale(scale_cls)()
469505
self.norm = norm
470506
if not in_init:
471507
self.changed() # Things are not set up properly yet.

lib/matplotlib/tests/test_image.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1328,3 +1328,19 @@ def test_nonuniform_and_pcolor():
13281328
ax.set_axis_off()
13291329
# NonUniformImage "leaks" out of extents, not PColorImage.
13301330
ax.set(xlim=(0, 10))
1331+
1332+
1333+
@check_figures_equal(extensions=["png"])
1334+
def test_str_norms(fig_test, fig_ref):
1335+
t = np.random.rand(10, 10) * .8 + .1 # between 0 and 1
1336+
axs = fig_test.subplots(1, 4)
1337+
axs[0].imshow(t, norm="log")
1338+
axs[1].imshow(t, norm="log", vmin=.2)
1339+
axs[2].imshow(t, norm="symlog")
1340+
axs[3].imshow(t, norm="symlog", vmin=.3, vmax=.7)
1341+
axs = fig_ref.subplots(1, 4)
1342+
axs[0].imshow(t, norm=colors.LogNorm())
1343+
axs[1].imshow(t, norm=colors.LogNorm(vmin=.2))
1344+
# same linthresh as SymmetricalLogScale's default.
1345+
axs[2].imshow(t, norm=colors.SymLogNorm(linthresh=2))
1346+
axs[3].imshow(t, norm=colors.SymLogNorm(linthresh=2, vmin=.3, vmax=.7))

0 commit comments

Comments
 (0)