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

Skip to content

Commit ed204cd

Browse files
committed
Move symlog helper functions to utility class
We will use it in the formatter, too.
1 parent 55b45cc commit ed204cd

File tree

1 file changed

+100
-82
lines changed

1 file changed

+100
-82
lines changed

lib/matplotlib/ticker.py

Lines changed: 100 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,92 @@ def _set_format(self):
812812
self.format = r'$\mathdefault{%s}$' % self.format
813813

814814

815+
class _SymmetricalLogUtil:
816+
"""
817+
Helper class for working with symmetrical log scales.
818+
819+
Parameters
820+
----------
821+
transform : `~.scale.SymmetricalLogTransform`, optional
822+
If set, defines *base*, *linthresh* and *linscale* of the symlog transform.
823+
base, linthresh, linscale : float, optional
824+
The *base*, *linthresh* and *linscale* of the symlog transform, as
825+
documented for `.SymmetricalLogScale`. These parameters are only used
826+
if *transform* is not set.
827+
"""
828+
829+
def __init__(self, transform=None, base=None, linthresh=None, linscale=None):
830+
if transform is not None:
831+
self.base = transform.base
832+
self.linthresh = transform.linthresh
833+
self.linscale = transform.linscale
834+
elif base is not None and linthresh is not None and linscale is not None:
835+
self.base = base
836+
self.linthresh = linthresh
837+
self.linscale = linscale
838+
else:
839+
raise ValueError("Either transform, or all of base, linthresh and "
840+
"linscale must be provided.")
841+
842+
def pos(self, val):
843+
"""
844+
Calculate the normalized position of the value on the axis.
845+
It is normalized such that the distance between two logarithmic decades
846+
is 1 and the position of linthresh is linscale.
847+
"""
848+
sign, val = np.sign(val), np.abs(val) / self.linthresh
849+
if val > 1:
850+
val = self.linscale + np.log(val) / np.log(self.base)
851+
else:
852+
val *= self.linscale
853+
return sign * val
854+
855+
def unpos(self, val):
856+
"""The inverse of _pos."""
857+
sign, val = np.sign(val), np.abs(val)
858+
if val > self.linscale:
859+
val = np.power(self.base, val - self.linscale)
860+
else:
861+
val /= self.linscale
862+
return sign * val * self.linthresh
863+
864+
def firstdec(self):
865+
"""
866+
Get the first decade (i.e. first positive major tick candidate).
867+
It shall be at least half the width of a logarithmic decade from the
868+
origin (i.e. its _pos shall be at least 0.5).
869+
"""
870+
firstexp = np.ceil(np.log(self.unpos(0.5)) / np.log(self.base))
871+
firstpow = np.power(self.base, firstexp)
872+
return firstexp, firstpow
873+
874+
def dec(self, val):
875+
"""
876+
Calculate the decade number of the value. The first decade to have a
877+
position (given by _pos) of at least 0.5 is given the number 1, the
878+
value 0 is given the decade number 0.
879+
"""
880+
firstexp, firstpow = self.firstdec()
881+
sign, val = np.sign(val), np.abs(val)
882+
if val > firstpow:
883+
val = np.log(val) / np.log(self.base) - firstexp + 1
884+
else:
885+
# We scale linearly in order to get a monotonous mapping between
886+
# 0 and 1, though the linear nature is arbitrary.
887+
val /= firstpow
888+
return sign * val
889+
890+
def undec(self, val):
891+
"""The inverse of _dec."""
892+
firstexp, firstpow = self.firstdec()
893+
sign, val = np.sign(val), np.abs(val)
894+
if val > 1:
895+
val = np.power(self.base, val - 1 + firstexp)
896+
else:
897+
val *= firstpow
898+
return sign * val
899+
900+
815901
class LogFormatter(Formatter):
816902
"""
817903
Base class for formatting ticks on a log or symlog scale.
@@ -2474,17 +2560,7 @@ class SymmetricalLogLocator(Locator):
24742560
def __init__(self, transform=None, subs=None, numticks=None,
24752561
base=None, linthresh=None, linscale=None):
24762562
"""Place ticks on the locations : subs[j] * base**i."""
2477-
if transform is not None:
2478-
self._base = transform.base
2479-
self._linthresh = transform.linthresh
2480-
self._linscale = transform.linscale
2481-
elif base is not None and linthresh is not None and linscale is not None:
2482-
self._base = base
2483-
self._linthresh = linthresh
2484-
self._linscale = linscale
2485-
else:
2486-
raise ValueError("Either transform, or all of base, linthresh and "
2487-
"linscale must be provided.")
2563+
self._symlogutil = _SymmetricalLogUtil(transform, base, linthresh, linscale)
24882564
self._set_subs(subs)
24892565
if numticks is None:
24902566
if mpl.rcParams['_internal.classic_mode']:
@@ -2501,11 +2577,11 @@ def set_params(self, subs=None, numticks=None,
25012577
if numticks is not None:
25022578
self.numticks = numticks
25032579
if base is not None:
2504-
self._base = float(base)
2580+
self._symlogutil.base = float(base)
25052581
if linthresh is not None:
2506-
self._linthresh = float(linthresh)
2582+
self._symlogutil.linthresh = float(linthresh)
25072583
if linscale is not None:
2508-
self._linscale = float(linscale)
2584+
self._symlogutil.linscale = float(linscale)
25092585

25102586
def _set_subs(self, subs):
25112587
"""
@@ -2547,8 +2623,8 @@ def tick_values(self, vmin, vmax):
25472623
vmin, vmax = vmax, vmin
25482624

25492625
haszero = vmin <= 0 <= vmax
2550-
firstdec = np.ceil(self._dec(vmin))
2551-
lastdec = np.floor(self._dec(vmax))
2626+
firstdec = np.ceil(self._symlogutil.dec(vmin))
2627+
lastdec = np.floor(self._symlogutil.dec(vmax))
25522628
maxdec = max(abs(firstdec), abs(lastdec))
25532629
# Number of decades completely contained in the range.
25542630
numdec = lastdec - firstdec
@@ -2565,7 +2641,7 @@ def tick_values(self, vmin, vmax):
25652641
subs = np.array([1.0])
25662642
else:
25672643
_first = 2.0 if self._subs == 'auto' else 1.0
2568-
subs = np.arange(_first, self._base)
2644+
subs = np.arange(_first, self._symlogutil.base)
25692645
else:
25702646
subs = self._subs
25712647

@@ -2599,16 +2675,16 @@ def tick_values(self, vmin, vmax):
25992675
ticklocs = []
26002676
for dec in decades:
26012677
if dec > 0:
2602-
ticklocs.append(subs * self._undec(dec))
2678+
ticklocs.append(subs * self._symlogutil.undec(dec))
26032679
elif dec < 0:
2604-
ticklocs.append(np.flip(subs * self._undec(dec)))
2680+
ticklocs.append(np.flip(subs * self._symlogutil.undec(dec)))
26052681
else:
2606-
if self._linscale < 0.5:
2682+
if self._symlogutil.linscale < 0.5:
26072683
# Don't add minor ticks around 0, it's too camped.
26082684
zeroticks = np.array([])
26092685
else:
26102686
# We add the usual subs as well as the next lower decade.
2611-
zeropow = self._undec(1) / self._base
2687+
zeropow = self._symlogutil.undec(1) / self._symlogutil.base
26122688
zeroticks = subs * zeropow
26132689
if subs[0] != 1.0:
26142690
zeroticks = np.concatenate(([zeropow], zeroticks))
@@ -2620,7 +2696,7 @@ def tick_values(self, vmin, vmax):
26202696
ticklocs = np.array([])
26212697
else:
26222698
# Major locator.
2623-
ticklocs = np.array([self._undec(dec) for dec in decades])
2699+
ticklocs = np.array([self._symlogutil.undec(dec) for dec in decades])
26242700

26252701
_log.debug('ticklocs %r', ticklocs)
26262702
if (len(subs) > 1
@@ -2633,70 +2709,12 @@ def tick_values(self, vmin, vmax):
26332709
else:
26342710
return self.raise_if_exceeds(ticklocs)
26352711

2636-
def _pos(self, val):
2637-
"""
2638-
Calculate the normalized position of the value on the axis.
2639-
It is normalized such that the distance between two logarithmic decades
2640-
is 1 and the position of linthresh is linscale.
2641-
"""
2642-
sign, val = np.sign(val), np.abs(val) / self._linthresh
2643-
if val > 1:
2644-
val = self._linscale + np.log(val) / np.log(self._base)
2645-
else:
2646-
val *= self._linscale
2647-
return sign * val
2648-
2649-
def _unpos(self, val):
2650-
"""The inverse of _pos."""
2651-
sign, val = np.sign(val), np.abs(val)
2652-
if val > self._linscale:
2653-
val = np.power(self._base, val - self._linscale)
2654-
else:
2655-
val /= self._linscale
2656-
return sign * val * self._linthresh
2657-
2658-
def _firstdec(self):
2659-
"""
2660-
Get the first decade (i.e. first positive major tick candidate).
2661-
It shall be at least half the width of a logarithmic decade from the
2662-
origin (i.e. its _pos shall be at least 0.5).
2663-
"""
2664-
firstexp = np.ceil(np.log(self._unpos(0.5)) / np.log(self._base))
2665-
firstpow = np.power(self._base, firstexp)
2666-
return firstexp, firstpow
2667-
2668-
def _dec(self, val):
2669-
"""
2670-
Calculate the decade number of the value. The first decade to have a
2671-
position (given by _pos) of at least 0.5 is given the number 1, the
2672-
value 0 is given the decade number 0.
2673-
"""
2674-
firstexp, firstpow = self._firstdec()
2675-
sign, val = np.sign(val), np.abs(val)
2676-
if val > firstpow:
2677-
val = np.log(val) / np.log(self._base) - firstexp + 1
2678-
else:
2679-
# We scale linearly in order to get a monotonous mapping between
2680-
# 0 and 1, though the linear nature is arbitrary.
2681-
val /= firstpow
2682-
return sign * val
2683-
2684-
def _undec(self, val):
2685-
"""The inverse of _dec."""
2686-
firstexp, firstpow = self._firstdec()
2687-
sign, val = np.sign(val), np.abs(val)
2688-
if val > 1:
2689-
val = np.power(self._base, val - 1 + firstexp)
2690-
else:
2691-
val *= firstpow
2692-
return sign * val
2693-
26942712
def view_limits(self, vmin, vmax):
26952713
"""Try to choose the view limits intelligently."""
26962714
vmin, vmax = self.nonsingular(vmin, vmax)
26972715
if mpl.rcParams['axes.autolimit_mode'] == 'round_numbers':
2698-
vmin = self._undec(np.floor(self._dec(vmin)))
2699-
vmax = self._undec(np.ceil(self._dec(vmax)))
2716+
vmin = self._symlogutil.undec(np.floor(self._symlogutil.dec(vmin)))
2717+
vmax = self._symlogutil.undec(np.ceil(self._symlogutil.dec(vmax)))
27002718
return vmin, vmax
27012719

27022720
class AsinhLocator(Locator):

0 commit comments

Comments
 (0)