Skip to content

Commit e189a9b

Browse files
committed
Internal cleanup and add see also blocks
This prevents DivergingNorm from triggering update blocks (vmin and vmax are public attributes back to matplotlib 3.0) and adds see also to classes except the Index/Degree/Longitude/Latitude stub classes.
1 parent 4f5cd83 commit e189a9b

File tree

4 files changed

+170
-92
lines changed

4 files changed

+170
-92
lines changed

proplot/colors.py

+65-54
Original file line numberDiff line numberDiff line change
@@ -2355,12 +2355,10 @@ def __init__(self, levels, norm=None, unique=None, step=None, clip=False):
23552355
of the normalizer are set to the minimum and maximum values in `levels`.
23562356
unique : {'neither', 'both', 'min', 'max'}, optional
23572357
Which out-of-bounds regions should be assigned unique colormap colors.
2358-
Possible values are equivalent to the `extend` values. The normalizer
2359-
needs this information so it can ensure the colorbar always spans the
2360-
full range of colormap colors. Internally, proplot sets this automatically
2361-
depending on whether the colormap is cyclic and whether "extreme" colors
2362-
were designated separately using `~matplotlib.colors.Colormap.set_under`
2363-
and/or `~matplotlib.colors.Colormap.set_over`.
2358+
Possible values are equivalent to the `extend` values. Internally, proplot
2359+
sets this depending on the user-input `extend`, whether the colormap is
2360+
cyclic, and whether `~matplotlib.colors.Colormap.set_under`
2361+
or `~matplotlib.colors.Colormap.set_over` were called for the colormap.
23642362
step : float, optional
23652363
The intensity of the transition to out-of-bounds colors as a fraction
23662364
of the adjacent step between in-bounds colors. Internally, proplot sets
@@ -2376,16 +2374,15 @@ def __init__(self, levels, norm=None, unique=None, step=None, clip=False):
23762374
----
23772375
This normalizer also makes sure that levels always span the full range
23782376
of colors in the colormap, whether `extend` is set to ``'min'``, ``'max'``,
2379-
``'neither'``, or ``'both'``. By default, when `extend` is not ``'both'``,
2380-
matplotlib cuts off the most intense colors (reserved for "out of bounds"
2381-
data), even though they are not being used. Note that this means
2382-
using a diverging colormap with ``extend='max'`` or ``extend='min'``
2383-
will shift the central color by default. But that is very strange
2384-
usage anyway... so please just don't do that :)
2377+
``'neither'``, or ``'both'``. In matplotlib, when `extend` is not ``'both'``,
2378+
the most intense colors are cut off (reserved for "out of bounds" data),
2379+
even though they are not being used.
23852380
23862381
See also
23872382
--------
23882383
proplot.constructor.Norm
2384+
proplot.colors.SegmentedNorm
2385+
proplot.ticker.DiscreteLocator
23892386
"""
23902387
# Parse input arguments
23912388
# NOTE: This must be a subclass BoundaryNorm, so ColorbarBase will
@@ -2435,26 +2432,25 @@ def __init__(self, levels, norm=None, unique=None, step=None, clip=False):
24352432
mids[0] += step * (mids[1] - mids[2])
24362433
if unique in ('max', 'both'):
24372434
mids[-1] += step * (mids[-2] - mids[-3])
2435+
mmin, mmax = np.min(mids), np.max(mids)
24382436
if vcenter is None:
2439-
mids = _interpolate_scalar(mids, np.min(mids), np.max(mids), vmin, vmax)
2437+
mids = _interpolate_scalar(mids, mmin, mmax, vmin, vmax)
24402438
else:
2441-
mids[mids < vcenter] = _interpolate_scalar(
2442-
mids[mids < vcenter], np.min(mids), vcenter, vmin, vcenter
2443-
)
2444-
mids[mids >= vcenter] = _interpolate_scalar(
2445-
mids[mids >= vcenter], vcenter, np.max(mids), vcenter, vmax
2446-
)
2439+
mask1, mask2 = mids < vcenter, mids >= vcenter
2440+
mids[mask1] = _interpolate_scalar(mids[mask1], mmin, vcenter, vmin, vcenter)
2441+
mids[mask2] = _interpolate_scalar(mids[mask2], vcenter, mmax, vcenter, vmax)
24472442

2448-
# Attributes
2443+
# Instance attributes
24492444
# NOTE: If clip is True, we clip values to the centers of the end bins
24502445
# rather than vmin/vmax to prevent out-of-bounds colors from getting an
24512446
# in-bounds bin color due to landing on a bin edge.
24522447
# NOTE: With unique='min' the minimimum in-bounds and out-of-bounds
24532448
# colors are the same so clip=True will have no effect. Same goes
24542449
# for unique='max' with maximum colors.
2450+
eps = 1e-10
24552451
dest = norm(mids)
2456-
dest[0] -= 1e-10 # dest guaranteed to be numpy.float64
2457-
dest[-1] += 1e-10
2452+
dest[0] -= eps # dest guaranteed to be numpy.float64
2453+
dest[-1] += eps
24582454
self._descending = descending
24592455
self._bmin = np.min(mids)
24602456
self._bmax = np.max(mids)
@@ -2470,7 +2466,7 @@ def __init__(self, levels, norm=None, unique=None, step=None, clip=False):
24702466
# up with unpredictable fill value, weird "out-of-bounds" colors
24712467
self._norm_clip = None
24722468
if isinstance(norm, mcolors.LogNorm):
2473-
self._norm_clip = (5e-249, None)
2469+
self._norm_clip = (1e-249, None)
24742470

24752471
def __call__(self, value, clip=None):
24762472
"""
@@ -2504,41 +2500,56 @@ def __call__(self, value, clip=None):
25042500

25052501
def inverse(self, value): # noqa: U100
25062502
"""
2507-
Raise an error. Inversion after discretization is impossible.
2503+
Raise an error.
2504+
2505+
Raises
2506+
------
2507+
ValueError
2508+
Inversion after discretization is impossible.
25082509
"""
25092510
raise ValueError('DiscreteNorm is not invertible.')
25102511

25112512
@property
25122513
def descending(self):
25132514
"""
2514-
Whether the normalizer levels are descending.
2515+
Boolean indicating whether the levels are descending.
25152516
"""
25162517
return self._descending
25172518

25182519

25192520
class SegmentedNorm(mcolors.Normalize):
25202521
"""
2521-
Normalizer that scales data linearly with respect to the interpolated
2522-
index in an arbitrary monotonically increasing level sequence.
2522+
Normalizer that scales data linearly with respect to the
2523+
interpolated index in an arbitrary monotonic level sequence.
25232524
"""
25242525
def __init__(self, levels, vmin=None, vmax=None, clip=False):
25252526
"""
25262527
Parameters
25272528
----------
25282529
levels : sequence of float
2529-
The level boundaries. Must be monotonically increasing or decreasing.
2530-
vmin, vmax : None
2531-
Ignored but included for consistency with other normalizers. These are
2532-
set to the minimum and maximum of `levels`.
2530+
The level boundaries. Must be monotonically increasing
2531+
or decreasing.
2532+
vmin : float, optional
2533+
Ignored but included for consistency with other normalizers.
2534+
Set to the minimum of `levels`.
2535+
vmax : float, optional
2536+
Ignored but included for consistency with other normalizers.
2537+
Set to the minimum of `levels`.
25332538
clip : bool, optional
2534-
Whether to clip values falling outside of the minimum and
2535-
maximum levels.
2539+
Whether to clip values falling outside of the minimum
2540+
and maximum of `levels`.
2541+
2542+
See also
2543+
--------
2544+
proplot.constructor.Norm
2545+
proplot.colors.DiscreteNorm
25362546
25372547
Note
25382548
----
2539-
This normalizer adapts the algorithm used by
2540-
`~matplotlib.colors.LinearSegmentedColormap`
2541-
to select colors in-between indices in segment data tables.
2549+
The algorithm this normalizer uses to select normalized values
2550+
in-between level list indices is adapted from the algorithm
2551+
`~matplotlib.colors.LinearSegmentedColormap` uses to select channel
2552+
values in-between segment data points (hence the name `SegmentedNorm`).
25422553
25432554
Example
25442555
-------
@@ -2554,18 +2565,19 @@ def __init__(self, levels, vmin=None, vmax=None, clip=False):
25542565
>>> ax.contourf(data, levels=levels)
25552566
"""
25562567
# WARNING: Tried using descending levels by adding 1 - yq to __call__() and
2557-
# inverse() but then tick labels fail. Instead just silently reverse here.
2568+
# inverse() but then tick labels fail. Instead just silently reverse here and
2569+
# the corresponding DiscreteLocator should enforce the descending axis.
25582570
levels, _ = _sanitize_levels(levels)
25592571
dest = np.linspace(0, 1, len(levels))
2560-
vmin, vmax = np.min(levels), np.max(levels)
2572+
vmin = np.min(levels)
2573+
vmax = np.max(levels)
25612574
super().__init__(vmin=vmin, vmax=vmax, clip=clip)
25622575
self._x = self.boundaries = levels # 'boundaries' are used in PlotAxes
25632576
self._y = dest
25642577

25652578
def __call__(self, value, clip=None):
25662579
"""
2567-
Normalize the data values to 0-1. Inverse
2568-
of `~SegmentedNorm.inverse`.
2580+
Normalize the data values to 0-1. Inverse of `~SegmentedNorm.inverse`.
25692581
25702582
Parameters
25712583
----------
@@ -2586,7 +2598,7 @@ def __call__(self, value, clip=None):
25862598

25872599
def inverse(self, value):
25882600
"""
2589-
Inverse operation of `~SegmentedNorm.__call__`.
2601+
Inverse of `~SegmentedNorm.__call__`.
25902602
25912603
Parameters
25922604
----------
@@ -2603,7 +2615,7 @@ def inverse(self, value):
26032615
class DivergingNorm(mcolors.Normalize):
26042616
"""
26052617
Normalizer that ensures some central data value lies at the central
2606-
colormap color. The default central value is ``0``.
2618+
colormap color. The default central value is ``0``.
26072619
"""
26082620
def __str__(self):
26092621
return type(self).__name__ + f'(center={self.vcenter!r})'
@@ -2616,15 +2628,16 @@ def __init__(
26162628
----------
26172629
vcenter : float, default: 0
26182630
The data value corresponding to the central colormap position.
2619-
vmin, vmax : float, optional
2620-
The minimum and maximum data values.
2631+
vmin : float, optional
2632+
The minimum data value.
2633+
vmax : float, optional
2634+
The maximum data value.
26212635
fair : bool, optional
2622-
If ``True`` (default), the speeds of the color gradations on
2623-
either side of the center point are equal, but colormap colors may
2624-
be omitted. If ``False``, all colormap colors are included, but
2625-
the color gradations on one side may be faster than the other side.
2626-
``False`` should be used with great care, as it may result in
2627-
a misleading interpretation of your data.
2636+
If ``True`` (default), the speeds of the color gradations on either side
2637+
of the center point are equal, but colormap colors may be omitted. If
2638+
``False``, all colormap colors are included, but the color gradations on
2639+
one side may be faster than the other side. ``False`` should be used with
2640+
great care, as it may result in a misleading interpretation of your data.
26282641
clip : bool, optional
26292642
Whether to clip values falling outside of `vmin` and `vmax`.
26302643
@@ -2637,17 +2650,15 @@ def __init__(
26372650
# NOTE: This is a stale PR that plans to implement the same features.
26382651
# https://github.com/matplotlib/matplotlib/pull/15333#issuecomment-537545430
26392652
# Since proplot is starting without matplotlib's baggage we can just implement
2640-
# DivergingNorm like they would prefer if they didn't have to worry about
2653+
# a diverging norm like they would prefer if they didn't have to worry about
26412654
# confusing users: single class, default "fair" scaling that can be turned off.
26422655
super().__init__(vmin, vmax, clip)
2643-
self.vmin = vmin
2644-
self.vmax = vmax
26452656
self.vcenter = vcenter
26462657
self.fair = fair
26472658

26482659
def __call__(self, value, clip=None):
26492660
"""
2650-
Normalize data values to 0-1.
2661+
Normalize the data values to 0-1.
26512662
26522663
Parameters
26532664
----------

proplot/constructor.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@
164164

165165
# Scale registry and presets
166166
SCALES = mscale._scale_mapping
167-
SCALE_PRESETS = {
167+
SCALES_PRESETS = {
168168
'quadratic': ('power', 2,),
169169
'cubic': ('power', 3,),
170170
'quartic': ('power', 4,),
@@ -1246,19 +1246,19 @@ def Scale(scale, *args, **kwargs):
12461246
if not isinstance(scale, str):
12471247
raise ValueError(f'Invalid scale name {scale!r}. Must be string.')
12481248
scale = scale.lower()
1249-
if scale in SCALE_PRESETS:
1249+
if scale in SCALES_PRESETS:
12501250
if args or kwargs:
12511251
warnings._warn_proplot(
12521252
f'Scale {scale!r} is a scale *preset*. Ignoring positional '
12531253
'argument(s): {args} and keyword argument(s): {kwargs}. '
12541254
)
1255-
scale, *args = SCALE_PRESETS[scale]
1255+
scale, *args = SCALES_PRESETS[scale]
12561256
if scale in SCALES:
12571257
scale = SCALES[scale]
12581258
else:
12591259
raise ValueError(
12601260
f'Unknown scale or preset {scale!r}. Options are: '
1261-
+ ', '.join(map(repr, (*SCALES, *SCALE_PRESETS)))
1261+
+ ', '.join(map(repr, (*SCALES, *SCALES_PRESETS)))
12621262
+ '.'
12631263
)
12641264
return scale(*args, **kwargs)

0 commit comments

Comments
 (0)