@@ -2355,12 +2355,10 @@ def __init__(self, levels, norm=None, unique=None, step=None, clip=False):
2355
2355
of the normalizer are set to the minimum and maximum values in `levels`.
2356
2356
unique : {'neither', 'both', 'min', 'max'}, optional
2357
2357
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.
2364
2362
step : float, optional
2365
2363
The intensity of the transition to out-of-bounds colors as a fraction
2366
2364
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):
2376
2374
----
2377
2375
This normalizer also makes sure that levels always span the full range
2378
2376
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.
2385
2380
2386
2381
See also
2387
2382
--------
2388
2383
proplot.constructor.Norm
2384
+ proplot.colors.SegmentedNorm
2385
+ proplot.ticker.DiscreteLocator
2389
2386
"""
2390
2387
# Parse input arguments
2391
2388
# 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):
2435
2432
mids [0 ] += step * (mids [1 ] - mids [2 ])
2436
2433
if unique in ('max' , 'both' ):
2437
2434
mids [- 1 ] += step * (mids [- 2 ] - mids [- 3 ])
2435
+ mmin , mmax = np .min (mids ), np .max (mids )
2438
2436
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 )
2440
2438
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 )
2447
2442
2448
- # Attributes
2443
+ # Instance attributes
2449
2444
# NOTE: If clip is True, we clip values to the centers of the end bins
2450
2445
# rather than vmin/vmax to prevent out-of-bounds colors from getting an
2451
2446
# in-bounds bin color due to landing on a bin edge.
2452
2447
# NOTE: With unique='min' the minimimum in-bounds and out-of-bounds
2453
2448
# colors are the same so clip=True will have no effect. Same goes
2454
2449
# for unique='max' with maximum colors.
2450
+ eps = 1e-10
2455
2451
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
2458
2454
self ._descending = descending
2459
2455
self ._bmin = np .min (mids )
2460
2456
self ._bmax = np .max (mids )
@@ -2470,7 +2466,7 @@ def __init__(self, levels, norm=None, unique=None, step=None, clip=False):
2470
2466
# up with unpredictable fill value, weird "out-of-bounds" colors
2471
2467
self ._norm_clip = None
2472
2468
if isinstance (norm , mcolors .LogNorm ):
2473
- self ._norm_clip = (5e -249 , None )
2469
+ self ._norm_clip = (1e -249 , None )
2474
2470
2475
2471
def __call__ (self , value , clip = None ):
2476
2472
"""
@@ -2504,41 +2500,56 @@ def __call__(self, value, clip=None):
2504
2500
2505
2501
def inverse (self , value ): # noqa: U100
2506
2502
"""
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.
2508
2509
"""
2509
2510
raise ValueError ('DiscreteNorm is not invertible.' )
2510
2511
2511
2512
@property
2512
2513
def descending (self ):
2513
2514
"""
2514
- Whether the normalizer levels are descending.
2515
+ Boolean indicating whether the levels are descending.
2515
2516
"""
2516
2517
return self ._descending
2517
2518
2518
2519
2519
2520
class SegmentedNorm (mcolors .Normalize ):
2520
2521
"""
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.
2523
2524
"""
2524
2525
def __init__ (self , levels , vmin = None , vmax = None , clip = False ):
2525
2526
"""
2526
2527
Parameters
2527
2528
----------
2528
2529
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`.
2533
2538
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
2536
2546
2537
2547
Note
2538
2548
----
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`).
2542
2553
2543
2554
Example
2544
2555
-------
@@ -2554,18 +2565,19 @@ def __init__(self, levels, vmin=None, vmax=None, clip=False):
2554
2565
>>> ax.contourf(data, levels=levels)
2555
2566
"""
2556
2567
# 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.
2558
2570
levels , _ = _sanitize_levels (levels )
2559
2571
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 )
2561
2574
super ().__init__ (vmin = vmin , vmax = vmax , clip = clip )
2562
2575
self ._x = self .boundaries = levels # 'boundaries' are used in PlotAxes
2563
2576
self ._y = dest
2564
2577
2565
2578
def __call__ (self , value , clip = None ):
2566
2579
"""
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`.
2569
2581
2570
2582
Parameters
2571
2583
----------
@@ -2586,7 +2598,7 @@ def __call__(self, value, clip=None):
2586
2598
2587
2599
def inverse (self , value ):
2588
2600
"""
2589
- Inverse operation of `~SegmentedNorm.__call__`.
2601
+ Inverse of `~SegmentedNorm.__call__`.
2590
2602
2591
2603
Parameters
2592
2604
----------
@@ -2603,7 +2615,7 @@ def inverse(self, value):
2603
2615
class DivergingNorm (mcolors .Normalize ):
2604
2616
"""
2605
2617
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``.
2607
2619
"""
2608
2620
def __str__ (self ):
2609
2621
return type (self ).__name__ + f'(center={ self .vcenter !r} )'
@@ -2616,15 +2628,16 @@ def __init__(
2616
2628
----------
2617
2629
vcenter : float, default: 0
2618
2630
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.
2621
2635
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.
2628
2641
clip : bool, optional
2629
2642
Whether to clip values falling outside of `vmin` and `vmax`.
2630
2643
@@ -2637,17 +2650,15 @@ def __init__(
2637
2650
# NOTE: This is a stale PR that plans to implement the same features.
2638
2651
# https://github.com/matplotlib/matplotlib/pull/15333#issuecomment-537545430
2639
2652
# 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
2641
2654
# confusing users: single class, default "fair" scaling that can be turned off.
2642
2655
super ().__init__ (vmin , vmax , clip )
2643
- self .vmin = vmin
2644
- self .vmax = vmax
2645
2656
self .vcenter = vcenter
2646
2657
self .fair = fair
2647
2658
2648
2659
def __call__ (self , value , clip = None ):
2649
2660
"""
2650
- Normalize data values to 0-1.
2661
+ Normalize the data values to 0-1.
2651
2662
2652
2663
Parameters
2653
2664
----------
0 commit comments