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

Skip to content

Commit 2b0d588

Browse files
committed
Moved AsinhLocator into ticker.py and extracted AsinhScale nested transform classes
1 parent 1ec0b22 commit 2b0d588

File tree

3 files changed

+98
-82
lines changed

3 files changed

+98
-82
lines changed

lib/matplotlib/scale.py

Lines changed: 31 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from matplotlib.ticker import (
2424
NullFormatter, ScalarFormatter, LogFormatterSciNotation, LogitFormatter,
2525
Locator, NullLocator, LogLocator, AutoLocator, AutoMinorLocator,
26-
SymmetricalLogLocator, LogitLocator)
26+
SymmetricalLogLocator, AsinhLocator, LogitLocator)
2727
from matplotlib.transforms import Transform, IdentityTransform
2828

2929

@@ -458,6 +458,34 @@ def get_transform(self):
458458
return self._transform
459459

460460

461+
class AsinhTransform(Transform):
462+
input_dims = output_dims = 1
463+
464+
def __init__(self, linear_width):
465+
super().__init__()
466+
self.linear_width = linear_width
467+
468+
def transform_non_affine(self, a):
469+
return self.linear_width * np.arcsinh(a / self.linear_width)
470+
471+
def inverted(self):
472+
return InvertedAsinhTransform(self.linear_width)
473+
474+
475+
class InvertedAsinhTransform(Transform):
476+
input_dims = output_dims = 1
477+
478+
def __init__(self, linear_width):
479+
super().__init__()
480+
self.linear_width = linear_width
481+
482+
def transform_non_affine(self, a):
483+
return self.linear_width * np.sinh(a / self.linear_width)
484+
485+
def inverted(self):
486+
return AsinhTransform(self.linear_width)
487+
488+
461489
class AsinhScale(ScaleBase):
462490
"""
463491
A quasi-logarithmic scale based on the inverse hyperbolic sine (asinh)
@@ -494,89 +522,12 @@ def __init__(self, axis, *, linear_width=1.0, **kwargs):
494522
self.linear_width = linear_width
495523

496524
def get_transform(self):
497-
return self.AsinhTransform(self.linear_width)
525+
return AsinhTransform(self.linear_width)
498526

499527
def set_default_locators_and_formatters(self, axis):
500-
axis.set(major_locator=AsinhScale.AsinhLocator(self.linear_width),
528+
axis.set(major_locator=AsinhLocator(self.linear_width),
501529
major_formatter='{x:.3g}')
502530

503-
class AsinhTransform(Transform):
504-
input_dims = output_dims = 1
505-
506-
def __init__(self, linear_width):
507-
super().__init__()
508-
self.linear_width = linear_width
509-
510-
def transform_non_affine(self, a):
511-
return self.linear_width * np.arcsinh(a / self.linear_width)
512-
513-
def inverted(self):
514-
return AsinhScale.InvertedAsinhTransform(self.linear_width)
515-
516-
class InvertedAsinhTransform(Transform):
517-
input_dims = output_dims = 1
518-
519-
def __init__(self, linear_width):
520-
super().__init__()
521-
self.linear_width = linear_width
522-
523-
def transform_non_affine(self, a):
524-
return self.linear_width * np.sinh(a / self.linear_width)
525-
526-
def inverted(self):
527-
return AsinhScale.AsinhTransform(self.linear_width)
528-
529-
class AsinhLocator(Locator):
530-
"""
531-
An axis tick locator specialized for the arcsinh scale
532-
533-
This is very unlikely to have any use beyond the AsinhScale class.
534-
"""
535-
def __init__(self, linear_width, numticks=12):
536-
"""
537-
Parameters
538-
----------
539-
linear_width : float
540-
The scale parameter defining the extent
541-
of the quasi-linear region.
542-
numticks : int, default: 12
543-
The approximate number of major ticks that will fit
544-
along the entire axis
545-
"""
546-
super().__init__()
547-
self.linear_width = linear_width
548-
self.numticks = numticks
549-
550-
def __call__(self):
551-
dmin, dmax = self.axis.get_data_interval()
552-
return self.tick_values(dmin, dmax)
553-
554-
def tick_values(self, vmin, vmax):
555-
# Construct a set of "on-screen" locations
556-
# that are uniformly spaced:
557-
ymin, ymax = self.linear_width * np.arcsinh(np.array([vmin, vmax]) / self.linear_width)
558-
ys = np.linspace(ymin, ymax, self.numticks)
559-
if (ymin * ymax) < 0:
560-
# Ensure that the zero tick-mark is included,
561-
# if the axis stradles zero
562-
ys = np.hstack([ys, 0.0])
563-
564-
# Transform the "on-screen" grid to the data space:
565-
xs = self.linear_width * np.sinh(ys / self.linear_width)
566-
zero_xs = (xs == 0)
567-
568-
# Round the data-space values to be intuitive decimal numbers:
569-
decades = (
570-
np.where(xs >= 0, 1, -1) *
571-
np.power(10, np.where(zero_xs, 1.0,
572-
np.floor(np.log10(np.abs(xs)
573-
+ zero_xs*1e-6))))
574-
)
575-
qs = decades * np.round(xs / decades)
576-
577-
return np.array(sorted(set(qs)))
578-
579-
580531
class LogitTransform(Transform):
581532
input_dims = output_dims = 1
582533

lib/matplotlib/tests/test_scale.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import matplotlib.pyplot as plt
44
from matplotlib.scale import (
5+
AsinhTransform,
56
LogTransform, InvertedLogTransform,
67
SymmetricalLogTransform)
78
import matplotlib.scale as mscale
@@ -222,5 +223,16 @@ def test_scale_deepcopy():
222223

223224

224225
def test_asinh_transforms():
225-
# FIXME - more here soon
226-
pass
226+
a0 = 17.0
227+
a = np.linspace(-50, 50, 100)
228+
229+
forward = AsinhTransform(a0)
230+
inverse = forward.inverted()
231+
invinv = inverse.inverted()
232+
233+
a_forward = forward.transform_non_affine(a)
234+
a_inverted = inverse.transform_non_affine(a_forward)
235+
assert_allclose(a_inverted, a)
236+
237+
a_invinv = invinv.transform_non_affine(a)
238+
assert_allclose(a_invinv, a0 * numpy.asinh(a / a0))

lib/matplotlib/ticker.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
`SymmetricalLogLocator` Locator for use with with the symlog norm; works like
3737
`LogLocator` for the part outside of the threshold and
3838
adds 0 if inside the limits.
39+
`AsinhLocator` Locator for use with the asinh norm, attempting to
40+
space ticks approximately uniformly.
3941
`LogitLocator` Locator for logit scaling.
4042
`AutoMinorLocator` Locator for minor ticks when the axis is linear and the
4143
major ticks are uniformly spaced. Subdivides the major
@@ -2583,6 +2585,57 @@ def view_limits(self, vmin, vmax):
25832585
return result
25842586

25852587

2588+
class AsinhLocator(Locator):
2589+
"""
2590+
An axis tick locator specialized for the inverse-sinh scale
2591+
2592+
This is very unlikely to have any use beyond the AsinhScale class.
2593+
"""
2594+
def __init__(self, linear_width, numticks=12):
2595+
"""
2596+
Parameters
2597+
----------
2598+
linear_width : float
2599+
The scale parameter defining the extent
2600+
of the quasi-linear region.
2601+
numticks : int, default: 12
2602+
The approximate number of major ticks that will fit
2603+
along the entire axis
2604+
"""
2605+
super().__init__()
2606+
self.linear_width = linear_width
2607+
self.numticks = numticks
2608+
2609+
def __call__(self):
2610+
dmin, dmax = self.axis.get_data_interval()
2611+
return self.tick_values(dmin, dmax)
2612+
2613+
def tick_values(self, vmin, vmax):
2614+
# Construct a set of "on-screen" locations
2615+
# that are uniformly spaced:
2616+
ymin, ymax = self.linear_width * np.arcsinh(np.array([vmin, vmax]) / self.linear_width)
2617+
ys = np.linspace(ymin, ymax, self.numticks)
2618+
if (ymin * ymax) < 0:
2619+
# Ensure that the zero tick-mark is included,
2620+
# if the axis stradles zero
2621+
ys = np.hstack([ys, 0.0])
2622+
2623+
# Transform the "on-screen" grid to the data space:
2624+
xs = self.linear_width * np.sinh(ys / self.linear_width)
2625+
zero_xs = (xs == 0)
2626+
2627+
# Round the data-space values to be intuitive decimal numbers:
2628+
decades = (
2629+
np.where(xs >= 0, 1, -1) *
2630+
np.power(10, np.where(zero_xs, 1.0,
2631+
np.floor(np.log10(np.abs(xs)
2632+
+ zero_xs*1e-6))))
2633+
)
2634+
qs = decades * np.round(xs / decades)
2635+
2636+
return np.array(sorted(set(qs)))
2637+
2638+
25862639
class LogitLocator(MaxNLocator):
25872640
"""
25882641
Determine the tick locations for logit axes

0 commit comments

Comments
 (0)