|
23 | 23 | from matplotlib.ticker import (
|
24 | 24 | NullFormatter, ScalarFormatter, LogFormatterSciNotation, LogitFormatter,
|
25 | 25 | Locator, NullLocator, LogLocator, AutoLocator, AutoMinorLocator,
|
26 |
| - SymmetricalLogLocator, LogitLocator) |
| 26 | + SymmetricalLogLocator, AsinhLocator, LogitLocator) |
27 | 27 | from matplotlib.transforms import Transform, IdentityTransform
|
28 | 28 |
|
29 | 29 |
|
@@ -458,6 +458,34 @@ def get_transform(self):
|
458 | 458 | return self._transform
|
459 | 459 |
|
460 | 460 |
|
| 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 | + |
461 | 489 | class AsinhScale(ScaleBase):
|
462 | 490 | """
|
463 | 491 | A quasi-logarithmic scale based on the inverse hyperbolic sine (asinh)
|
@@ -494,89 +522,12 @@ def __init__(self, axis, *, linear_width=1.0, **kwargs):
|
494 | 522 | self.linear_width = linear_width
|
495 | 523 |
|
496 | 524 | def get_transform(self):
|
497 |
| - return self.AsinhTransform(self.linear_width) |
| 525 | + return AsinhTransform(self.linear_width) |
498 | 526 |
|
499 | 527 | 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), |
501 | 529 | major_formatter='{x:.3g}')
|
502 | 530 |
|
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 |
| - |
580 | 531 | class LogitTransform(Transform):
|
581 | 532 | input_dims = output_dims = 1
|
582 | 533 |
|
|
0 commit comments