8000 Simplify decade up- and down-rounding. · matplotlib/matplotlib@3b5dc4d · GitHub
[go: up one dir, main page]

Skip to content

Commit 3b5dc4d

Browse files
committed
Simplify decade up- and down-rounding.
Instead of `decade_up` and `decade_down`, use more explicit `_decade_less`, `_decade_less_equal`, etc. which moreover also handle negative values (used by the symlog scale). Previously, `semilogx([.1, .1], [1, 2])` would autoscale the x-axis from a bit less than 0.1 to a bit more than 1, because 0.1 == 10**-1 so decade_down(0.1) would use `floor(-1) == -1` and return 0.1, while decade_up(0.1) would return 1. With this PR, `semilogx([.1, .1], [1, 2])` instead autoscales the x-axis from a bit less than 0.01 to a bit more than 1 (i.e. one decade on each side), which seems more logical.
1 parent 121102b commit 3b5dc4d

File tree

2 files changed

+69
-23
lines changed

2 files changed

+69
-23
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Deprecations
2+
````````````
3+
``ticker.decade_up`` and ``ticker.decade_down`` are deprecated.
4+
5+
Autoscaling changes
6+
```````````````````
7+
On log-axes where a single value is plotted at a "full" decade (1, 10, 100,
8+
etc.), the autoscaling now expands the axis symmetrically around that point,
9+
instead of adding a decade only to the right.

lib/matplotlib/ticker.py

Lines changed: 60 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2058,6 +2058,7 @@ def view_limits(self, dmin, dmax):
20582058
return dmin, dmax
20592059

20602060

2061+
@cbook.deprecated("3.1")
20612062
def decade_down(x, base=10):
20622063
'floor x to the nearest lower decade'
20632064
if x == 0.0:
@@ -2066,6 +2067,7 @@ def decade_down(x, base=10):
20662067
return base ** lx
20672068

20682069

2070+
@cbook.deprecated("3.1")
20692071
def decade_up(x, base=10):
20702072
'ceil x to the nearest higher decade'
20712073
if x == 0.0:
@@ -2091,6 +2093,56 @@ def is_decade(x, base=10):
20912093
return is_close_to_int(lx)
20922094

20932095

2096+
def _decade_less_equal(x, base):
2097+
"""
2098+
Return the largest integer power of *base* that's less or equal to *x*.
2099+
2100+
If *x* is negative, the exponent will be *greater*.
2101+
"""
2102+
return (x if x == 0 else
2103+
-_decade_greater_equal(-x, base) if x < 0 else
2104+
base ** np.floor(np.log(x) / np.log(base)))
2105+
2106+
2107+
def _decade_greater_equal(x, base):
2108+
"""
2109+
Return the smallest integer power of *base* that's greater or equal to *x*.
2110+
2111+
If *x* is negative, the exponent will be *smaller*.
2112+
"""
2113+
return (x if x == 0 else
2114+
-_decade_less_equal(-x, base) if x < 0 else
2115+
base ** np.ceil(np.log(x) / np.log(base)))
2116+
2117+
2118+
def _decade_less(x, base):
2119+
"""
2120+
Return the smallest integer power of *base* that's less than *x*.
2121+
2122+
If *x* is negative, the exponent will be *greater*.
2123+
"""
2124+
if x < 0:
2125+
return -_decade_greater(-x, base)
2126+
less = _decade_less_equal(x, base)
2127+
if less == x:
2128+
less /= base
2129+
return less
2130+
2131+
2132+
def _decade_greater(x, base):
2133+
"""
2134+
Return the smallest integer power of *base* that's greater than *x*.
2135+
2136+
If *x* is negative, the exponent will be *smaller*.
2137+
"""
2138+
if x < 0:
2139+
return -_decade_less(-x, base)
2140+
greater = _decade_greater_equal(x, base)
2141+
if greater == x:
2142+
greater *= base
2143+
return greater
2144+
2145+
20942146
def is_close_to_int(x):
20952147
return abs(x - np.round(x)) < 1e-10
20962148

@@ -2272,10 +2324,8 @@ def view_limits(self, vmin, vmax):
22722324
vmin = b ** (vmax - self.numdecs)
22732325

22742326
if rcParams['axes.autolimit_mode'] == 'round_numbers':
2275-
if not is_decade(vmin, self._base):
2276-
vmin = decade_down(vmin, self._base)
2277-
if not is_decade(vmax, self._base):
2278-
vmax = decade_up(vmax, self._base)
2327+
vmin = _decade_less_equal(vmin, self._base)
2328+
vmax = _decade_greater_equal(vmax, self._base)
22792329

22802330
return vmin, vmax
22812331

@@ -2297,8 +2347,8 @@ def nonsingular(self, vmin, vmax):
22972347
if vmin <= 0:
22982348
vmin = minpos
22992349
if vmin == vmax:
2300-
vmin = decade_down(vmin, self._base)
2301-
vmax = decade_up(vmax, self._base)
2350+
vmin = _decade_less(vmin, self._base)
2351+
vmax = _decade_greater(vmax, self._base)
23022352
return vmin, vmax
23032353

23042354

@@ -2452,24 +2502,11 @@ def view_limits(self, vmin, vmax):
24522502
vmin, vmax = vmax, vmin
24532503

24542504
if rcParams['axes.autolimit_mode'] == 'round_numbers':
2455-
if not is_decade(abs(vmin), b):
2456-
if vmin < 0:
2457-
vmin = -decade_up(-vmin, b)
2458-
else:
2459-
vmin = decade_down(vmin, b)
2460-
if not is_decade(abs(vmax), b):
2461-
if vmax < 0:
2462-
vmax = -decade_down(-vmax, b)
2463-
else:
2464-
vmax = decade_up(vmax, b)
2465-
2505+
vmin = _decade_less_equal(vmin, b)
2506+
vmax = _decade_greater_equal(vmax, b)
24662507
if vmin == vmax:
2467-
if vmin < 0:
2468-
vmin = -decade_up(-vmin, b)
2469-
vmax = -decade_down(-vmax, b)
2470-
else:
2471-
vmin = decade_down(vmin, b)
2472-
vmax = decade_up(vmax, b)
2508+
vmin = _decade_less(vmin, b)
2509+
vmax = _decade_greater(vmax, b)
24732510

24742511
result = mtransforms.nonsingular(vmin, vmax)
24752512
return result

0 commit comments

Comments
 (0)
0