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

Skip to content

Commit ade70d7

Browse files
committed
Added InvLogLocator and Formatter to ticker.py
1 parent 9b15a52 commit ade70d7

File tree

1 file changed

+233
-2
lines changed

1 file changed

+233
-2
lines changed

lib/matplotlib/ticker.py

Lines changed: 233 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@
5050
:class:`LogLocator`
5151
Space ticks logarithmically from min to max.
5252
53+
:class:`InvLogLocator`
54+
Space ticks logarithmically from min to max, but from right to left.
55+
5356
:class:`MultipleLocator`
5457
Ticks and range are a multiple of base; either integer or float.
5558
@@ -138,6 +141,9 @@
138141
:class:`LogFormatterSciNotation`
139142
Format values for log axis using scientific notation.
140143
144+
:class:`InvLogFormatter`
145+
Formatter for inverted log axes.
146+
141147
:class:`LogitFormatter`
142148
Probability formatter.
143149
@@ -187,10 +193,10 @@
187193
'NullFormatter', 'FuncFormatter', 'FormatStrFormatter',
188194
'StrMethodFormatter', 'ScalarFormatter', 'LogFormatter',
189195
'LogFormatterExponent', 'LogFormatterMathtext',
190-
'IndexFormatter', 'LogFormatterSciNotation',
196+
'IndexFormatter', 'LogFormatterSciNotation', 'InvLogFormatter',
191197
'LogitFormatter', 'EngFormatter', 'PercentFormatter',
192198
'Locator', 'IndexLocator', 'FixedLocator', 'NullLocator',
193-
'LinearLocator', 'LogLocator', 'AutoLocator',
199+
'LinearLocator', 'LogLocator', 'InvLogLocator', 'AutoLocator',
194200
'MultipleLocator', 'MaxNLocator', 'AutoMinorLocator',
195201
'SymmetricalLogLocator', 'LogitLocator')
196202

@@ -1150,6 +1156,95 @@ def _non_decade_format(self, sign_string, base, fx, usetex):
11501156
(sign_string, coeff, base, exponent)))
11511157

11521158

1159+
class InvLogFormatter(LogFormatter):
1160+
"""
1161+
Base class for formatting ticks on an inverted log scale. Being a
1162+
subclass of LogFormatter, it inherits most of its attributes, methods and
1163+
instantiation parameters (base, labelOnlyBase, minor_thresholds and
1164+
linthresh; see LogFormatter documentation for more on those). Note that
1165+
symlog mode does not work.
1166+
1167+
It may be instantiated directly, or subclassed.
1168+
1169+
Parameters
1170+
----------
1171+
inv_base : float, optional, default: 10.
1172+
Base of the logarithm used in all calculations for the inverse scale.
1173+
1174+
inv_factor : float, optional, default: 1.
1175+
Multiply the data on the inverted axis by this number. This can be
1176+
useful when one wants to compare data on a logarithmic axis ``x`` not
1177+
to the reciprocal axis ``1/x``, but rather to an axis ``inv_factor/x``.
1178+
This is common for instance in Fourier analysis where the inverse axis
1179+
could be ``2*np.pi/x`` (depending on the choice of frequency/scale
1180+
space coordinates).
1181+
1182+
Examples
1183+
--------
1184+
To label a subset of minor ticks when the view limits span up
1185+
to 2 decades, and all of the ticks when zoomed in to 0.5 decades
1186+
or less, use ``minor_thresholds=(2, 0.5)``.
1187+
1188+
To label all minor ticks when the view limits span up to 1.5
1189+
decades, use ``minor_thresholds=(1.5, 1.5)``.
1190+
1191+
"""
1192+
def __init__(self, inv_base=10.0, inv_factor=1., **kwargs):
1193+
"""
1194+
*base* kwarg is used to locate the decade tick,
1195+
which will be the only one to be labeled if *labelOnlyBase*
1196+
is ``False``
1197+
"""
1198+
super(InvLogFormatter, self).__init__(**kwargs)
1199+
self.inv_base(inv_base)
1200+
self.inv_factor(inv_factor)
1201+
1202+
def inv_base(self, inv_base):
1203+
"""
1204+
set the base of the log scaling (major tick every base**i, i integer)
1205+
for the inverse of the data - warning: should always match the
1206+
base used for :class:`InvLogLocator`.
1207+
"""
1208+
self._inv_base = inv_base + 0.0
1209+
1210+
def inv_factor(self, inv_factor):
1211+
"""
1212+
After inverting the data, it can be multiplied by this factor. This
1213+
way, any inverse logarithmic quantity can be computed from a
1214+
logarithmic input quantity - warning: should always match the base used
1215+
for :class:`InvLogLocator`.
1216+
"""
1217+
self._inv_factor = inv_factor + 0.0
1218+
1219+
def __call__(self, x, pos=None):
1220+
"""
1221+
Return the format for tick val `x`.
1222+
"""
1223+
ix = abs(self._inv_factor / x)
1224+
1225+
if ix == 0.0: # Symlog
1226+
return '0'
1227+
1228+
ib = self._inv_base
1229+
# only label the decades
1230+
fx = math.log(ix) / math.log(ib)
1231+
is_ix_decade = is_close_to_int(fx)
1232+
exponent = np.round(fx) if is_ix_decade else np.floor(fx)
1233+
coeff = np.round(ix / ib ** exponent)
1234+
1235+
if self.labelOnlyBase and not is_ix_decade:
1236+
return ''
1237+
if self._sublabels is not None and coeff not in self._sublabels:
1238+
return ''
1239+
1240+
vmin, vmax = self.axis.get_view_interval()
1241+
# N.B.: max and min swapped
1242+
ivmax = self._inv_factor / vmin
1243+
ivmin = self._inv_factor / vmax
1244+
s = self._num_to_string(ix, ivmin, ivmax)
1245+
return self.fix_minus(s)
1246+
1247+
11531248
class LogitFormatter(Formatter):
11541249
"""
11551250
Probability formatter (using Math text).
@@ -2377,6 +2472,142 @@ def view_limits(self, vmin, vmax):
23772472
return result
23782473

23792474

2475+
class InvLogLocator(LogLocator):
2476+
"""
2477+
Determine the tick locations for inverse log axes. Being a subclass of
2478+
LogLocator, it shares most of its characteristics, except for two added
2479+
instantiation keyword arguments: inv_base and inv_factor. See the docstring
2480+
of InvLogFormatter for a description of these parameters.
2481+
"""
2482+
2483+
def __init__(self, inv_base=10.0, inv_factor=1., **kwargs):
2484+
"""
2485+
place ticks on the location= inv_base**i*subs[j]
2486+
Set subs to None for autosub (for minor locator).
2487+
"""
2488+
super(InvLogLocator, self).__init__(**kwargs)
2489+
self.inv_base(inv_base)
2490+
self.inv_factor(inv_factor)
2491+
2492+
def set_params(self, inv_base=None, inv_factor=None, **kwargs):
2493+
"""Set parameters within this locator."""
2494+
super(InvLogLocator, self).set_params(**kwargs)
2495+
if inv_base is not None:
2496+
self.inv_base = inv_base
2497+
if inv_factor is not None:
2498+
self.inv_factor = inv_factor
2499+
2500+
def inv_base(self, inv_base):
2501+
"""
2502+
set the base of the log scaling (major tick every base**i, i integer)
2503+
for the inverse of the data
2504+
"""
2505+
self._inv_base = inv_base + 0.0
2506+
2507+
def inv_factor(self, inv_factor):
2508+
"""
2509+
After inverting the data, it can be multiplied by this factor. This
2510+
way, any inverse logarithmic quantity can be computed from a
2511+
logarithmic input quantity.
2512+
"""
2513+
self._inv_factor = inv_factor + 0.0
2514+
2515+
def __call__(self):
2516+
'Return the locations of the ticks'
2517+
vmin, vmax = self.axis.get_view_interval()
2518+
return self.tick_values(vmin, vmax)
2519+
2520+
def tick_values(self, vmin, vmax):
2521+
if self.numticks == 'auto':
2522+
if self.axis is not None:
2523+
numticks = np.clip(self.axis.get_tick_space(), 2, 9)
2524+
else:
2525+
numticks = 9
2526+
else:
2527+
numticks = self.numticks
2528+
2529+
ib = self._inv_base
2530+
# max and min swapped
2531+
ivmax = self._inv_factor / vmin
2532+
ivmin = self._inv_factor / vmax
2533+
# dummy axis has no axes attribute
2534+
if hasattr(self.axis, 'axes') and self.axis.axes.name == 'polar':
2535+
ivmax = math.ceil(math.log(ivmax) / math.log(ib))
2536+
decades = np.arange(ivmax - self.numdecs, ivmax)
2537+
i_ticklocs = ib ** decades
2538+
2539+
ticklocs = self._inv_factor / i_ticklocs
2540+
2541+
return ticklocs
2542+
2543+
if ivmax <= 0.0:
2544+
if self.axis is not None:
2545+
ivmax = self._inv_factor / self.axis.get_minpos()
2546+
2547+
if ivmax <= 0.0 or not np.isfinite(ivmax):
2548+
raise ValueError(
2549+
"Data has no positive values, and therefore can not be "
2550+
"log-scaled.")
2551+
2552+
ivmin = math.log(ivmin) / math.log(ib)
2553+
ivmax = math.log(ivmax) / math.log(ib)
2554+
2555+
if ivmax < ivmin:
2556+
ivmin, ivmax = ivmax, ivmin
2557+
2558+
numdec = math.floor(ivmax) - math.ceil(ivmin)
2559+
2560+
if isinstance(self._subs, six.string_types):
2561+
_first = 2.0 if self._subs == 'auto' else 1.0
2562+
if numdec > 10 or ib < 3:
2563+
if self._subs == 'auto':
2564+
return np.array([]) # no minor or major ticks
2565+
else:
2566+
subs = np.array([1.0]) # major ticks
2567+
else:
2568+
subs = np.arange(_first, ib)
2569+
else:
2570+
subs = self._subs
2571+
2572+
# get decades between major ticks.
2573+
stride = 1
2574+
if rcParams['_internal.classic_mode']:
2575+
# Leave the bug left over from the PY2-PY3 transition.
2576+
while numdec / stride + 1 > numticks:
2577+
stride += 1
2578+
else:
2579+
while numdec // stride + 1 > numticks:
2580+
stride += 1
2581+
2582+
# Does subs include anything other than 1?
2583+
have_subs = len(subs) > 1 or (len(subs == 1) and subs[0] != 1.0)
2584+
2585+
decades = np.arange(math.floor(ivmin) - stride,
2586+
math.ceil(ivmax) + 2 * stride, stride)
2587+
if hasattr(self, '_transform'):
2588+
i_ticklocs = self._transform.inverted().transform(decades)
2589+
if have_subs:
2590+
if stride == 1:
2591+
i_ticklocs = np.ravel(np.outer(subs, i_ticklocs))
2592+
else:
2593+
# no ticklocs if we have more than one decade
2594+
# between major ticks.
2595+
i_ticklocs = []
2596+
else:
2597+
if have_subs:
2598+
i_ticklocs = []
2599+
if stride == 1:
2600+
for decadeStart in ib ** decades:
2601+
i_ticklocs.extend(subs * decadeStart)
2602+
else:
2603+
i_ticklocs = ib ** decades
2604+
2605+
ticklocs = self._inv_factor / np.asarray(i_ticklocs)
2606+
2607+
_log.debug('ticklocs %r', ticklocs)
2608+
return self.raise_if_exceeds(np.asarray(ticklocs))
2609+
2610+
23802611
class LogitLocator(Locator):
23812612
"""
23822613
Determine the tick locations for logit axes

0 commit comments

Comments
 (0)