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

Skip to content

Commit c73a275

Browse files
committed
Better choice of offset-text.
The axis offset text is chosen as follows: xlims => offsettext 123, 189 => 0 12341, 12349 => 12340 99999.5, 100010.5 => 100000 # (also a test for #5780) 99990.5, 100000.5 => 100000 1233999, 1234001 => 1234000 (and the same for negative limits). See #5755.
1 parent f35177e commit c73a275

File tree

2 files changed

+85
-19
lines changed

2 files changed

+85
-19
lines changed

lib/matplotlib/tests/test_ticker.py

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
from matplotlib.externals import six
55
import nose.tools
6-
from nose.tools import assert_raises
6+
from nose.tools import assert_equal, assert_raises
77
from numpy.testing import assert_almost_equal
88
import numpy as np
99
import matplotlib
@@ -159,6 +159,55 @@ def test_SymmetricalLogLocator_set_params():
159159
nose.tools.assert_equal(sym.numticks, 8)
160160

161161

162+
@cleanup
163+
def test_ScalarFormatter_offset_value():
164+
fig, ax = plt.subplots()
165+
formatter = ax.get_xaxis().get_major_formatter()
166+
167+
def update_ticks(ax):
168+
return next(ax.get_xaxis().iter_ticks())
169+
170+
ax.set_xlim(123, 189)
171+
update_ticks(ax)
172+
assert_equal(formatter.offset, 0)
173+
174+
ax.set_xlim(-189, -123)
175+
update_ticks(ax)
176+
assert_equal(formatter.offset, 0)
177+
178+
ax.set_xlim(12341, 12349)
179+
update_ticks(ax)
180+
assert_equal(formatter.offset, 12340)
181+
182+
ax.set_xlim(-12349, -12341)
183+
update_ticks(ax)
184+
assert_equal(formatter.offset, -12340)
185+
186+
ax.set_xlim(99999.5, 100010.5)
187+
update_ticks(ax)
188+
assert_equal(formatter.offset, 100000)
189+
190+
ax.set_xlim(-100010.5, -99999.5)
191+
update_ticks(ax)
192+
assert_equal(formatter.offset, -100000)
193+
194+
ax.set_xlim(99990.5, 100000.5)
195+
update_ticks(ax)
196+
assert_equal(formatter.offset, 100000)
197+
198+
ax.set_xlim(-100000.5, -99990.5)
199+
update_ticks(ax)
200+
assert_equal(formatter.offset, -100000)
201+
202+
ax.set_xlim(1233999, 1234001)
203+
update_ticks(ax)
204+
assert_equal(formatter.offset, 1234000)
205+
206+
ax.set_xlim(-1234001, -1233999)
207+
update_ticks(ax)
208+
assert_equal(formatter.offset, -1234000)
209+
210+
162211
def _logfe_helper(formatter, base, locs, i, expected_result):
163212
vals = base**locs
164213
labels = [formatter(x, pos) for (x, pos) in zip(vals, i)]

lib/matplotlib/ticker.py

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -548,33 +548,50 @@ def set_locs(self, locs):
548548
vmin, vmax = self.axis.get_view_interval()
549549
d = abs(vmax - vmin)
550550
if self._useOffset:
551-
self._set_offset(d)
551+
self._compute_offset()
552552
self._set_orderOfMagnitude(d)
553553
self._set_format(vmin, vmax)
554554

555-
def _set_offset(self, range):
556-
# offset of 20,001 is 20,000, for example
555+
def _compute_offset(self):
557556
locs = self.locs
558-
559-
if locs is None or not len(locs) or range == 0:
557+
if locs is None or not len(locs):
560558
self.offset = 0
561559
return
560+
# Restrict to visible ticks.
562561
vmin, vmax = sorted(self.axis.get_view_interval())
563562
locs = np.asarray(locs)
564563
locs = locs[(vmin <= locs) & (locs <= vmax)]
565-
ave_loc = np.mean(locs)
566-
if ave_loc: # dont want to take log10(0)
567-
ave_oom = math.floor(math.log10(np.mean(np.absolute(locs))))
568-
range_oom = math.floor(math.log10(range))
569-
570-
if np.absolute(ave_oom - range_oom) >= 3: # four sig-figs
571-
p10 = 10 ** range_oom
572-
if ave_loc < 0:
573-
self.offset = (math.ceil(np.max(locs) / p10) * p10)
574-
else:
575-
self.offset = (math.floor(np.min(locs) / p10) * p10)
576-
else:
577-
self.offset = 0
564+
lmin, lmax = locs.min(), locs.max()
565+
# min, max comparing absolute values (we want division to round towards
566+
# zero so we work on absolute values).
567+
abs_min, abs_max = sorted(map(abs, [lmin, lmax]))
568+
# Only use offset if there are at least two ticks, every tick has the
569+
# same sign, and if the span is small compared to the absolute values.
570+
if (lmin == lmax or lmin <= 0 <= lmax or
571+
(abs_max - abs_min) / abs_max >= 1e-2):
572+
self.offset = 0
573+
return
574+
sign = math.copysign(1, lmin)
575+
# What is the smallest power of ten such that abs_min and abs_max are
576+
# equal up to that precision?
577+
oom = 10 ** int(math.log10(abs_max) + 1)
578+
while True:
579+
if abs_min // oom != abs_max // oom:
580+
oom *= 10
581+
break
582+
oom /= 10
583+
if (abs_max - abs_min) / oom <= 1e-2:
584+
# Handle the case of straddling a multiple of a large power of ten
585+
# (relative to the span).
586+
# What is the smallest power of ten such that abs_min and abs_max
587+
# at most 1 apart?
588+
oom = 10 ** int(math.log10(abs_max) + 1)
589+
while True:
590+
if abs_max // oom - abs_min // oom > 1:
591+
oom *= 10
592+
break
593+
oom /= 10
594+
self.offset = sign * (abs_max // oom) * oom
578595

579596
def _set_orderOfMagnitude(self, range):
580597
# if scientific notation is to be used, find the appropriate exponent

0 commit comments

Comments
 (0)