@@ -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
25192520class 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):
26032615class 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 ----------
0 commit comments