Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 4e1792b

Browse filesBrowse files
committed
Merge pull request #5785 from anntzer/better-offsettext-choice
Better choice of offset-text.
2 parents b16e6f3 + 782b8a1 commit 4e1792b
Copy full SHA for 4e1792b

File tree

3 files changed

+90
-19
lines changed
Filter options

3 files changed

+90
-19
lines changed
+6Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Improved offset text choice
2+
---------------------------
3+
The default offset-text choice was changed to only use significant digits that
4+
are common to all ticks (e.g. 1231..1239 -> 1230, instead of 1231), except when
5+
they straddle a relatively large multiple of a power of ten, in which case that
6+
multiple is chosen (e.g. 1999..2001->2000).

‎lib/matplotlib/tests/test_ticker.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_ticker.py
+48-1Lines changed: 48 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,53 @@ 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 check_offset_for(left, right, offset):
168+
ax.set_xlim(left, right)
169+
# Update ticks.
170+
next(ax.get_xaxis().iter_ticks())
171+
assert_equal(formatter.offset, offset)
172+
173+
test_data = [(123, 189, 0),
174+
(-189, -123, 0),
175+
(12341, 12349, 12340),
176+
(-12349, -12341, -12340),
177+
(99999.5, 100010.5, 100000),
178+
(-100010.5, -99999.5, -100000),
179+
(99990.5, 100000.5, 100000),
180+
(-100000.5, -99990.5, -100000),
181+
(1233999, 1234001, 1234000),
182+
(-1234001, -1233999, -1234000),
183+
(1, 1, 1),
184+
(123, 123, 120),
185+
# Test cases courtesy of @WeatherGod
186+
(.4538, .4578, .45),
187+
(3789.12, 3783.1, 3780),
188+
(45124.3, 45831.75, 45000),
189+
(0.000721, 0.0007243, 0.00072),
190+
(12592.82, 12591.43, 12590),
191+
(9., 12., 0),
192+
(900., 1200., 0),
193+
(1900., 1200., 0),
194+
(0.99, 1.01, 1),
195+
(9.99, 10.01, 10),
196+
(99.99, 100.01, 100),
197+
(5.99, 6.01, 6),
198+
(15.99, 16.01, 16),
199+
(-0.452, 0.492, 0),
200+
(-0.492, 0.492, 0),
201+
(12331.4, 12350.5, 12300),
202+
(-12335.3, 12335.3, 0)]
203+
204+
for left, right, offset in test_data:
205+
yield check_offset_for, left, right, offset
206+
yield check_offset_for, right, left, offset
207+
208+
162209
def _logfe_helper(formatter, base, locs, i, expected_result):
163210
vals = base**locs
164211
labels = [formatter(x, pos) for (x, pos) in zip(vals, i)]

‎lib/matplotlib/ticker.py

Copy file name to clipboardExpand all lines: lib/matplotlib/ticker.py
+36-18Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@
164164
from matplotlib.externals import six
165165

166166
import decimal
167+
import itertools
167168
import locale
168169
import math
169170
import numpy as np
@@ -663,33 +664,50 @@ def set_locs(self, locs):
663664
vmin, vmax = self.axis.get_view_interval()
664665
d = abs(vmax - vmin)
665666
if self._useOffset:
666-
self._set_offset(d)
667+
self._compute_offset()
667668
self._set_orderOfMagnitude(d)
668669
self._set_format(vmin, vmax)
669670

670-
def _set_offset(self, range):
671-
# offset of 20,001 is 20,000, for example
671+
def _compute_offset(self):
672672
locs = self.locs
673-
674-
if locs is None or not len(locs) or range == 0:
673+
if locs is None or not len(locs):
675674
self.offset = 0
676675
return
676+
# Restrict to visible ticks.
677677
vmin, vmax = sorted(self.axis.get_view_interval())
678678
locs = np.asarray(locs)
679679
locs = locs[(vmin <= locs) & (locs <= vmax)]
680-
ave_loc = np.mean(locs)
681-
if len(locs) and ave_loc: # dont want to take log10(0)
682-
ave_oom = math.floor(math.log10(np.mean(np.absolute(locs))))
683-
range_oom = math.floor(math.log10(range))
684-
685-
if np.absolute(ave_oom - range_oom) >= 3: # four sig-figs
686-
p10 = 10 ** range_oom
687-
if ave_loc < 0:
688-
self.offset = (math.ceil(np.max(locs) / p10) * p10)
689-
else:
690-
self.offset = (math.floor(np.min(locs) / p10) * p10)
691-
else:
692-
self.offset = 0
680+
if not len(locs):
681+
self.offset = 0
682+
return
683+
lmin, lmax = locs.min(), locs.max()
684+
# Only use offset if there are at least two ticks and every tick has
685+
# the same sign.
686+
if lmin == lmax or lmin <= 0 <= lmax:
687+
self.offset = 0
688+
return
689+
# min, max comparing absolute values (we want division to round towards
690+
# zero so we work on absolute values).
691+
abs_min, abs_max = sorted([abs(float(lmin)), abs(float(lmax))])
692+
sign = math.copysign(1, lmin)
693+
# What is the smallest power of ten such that abs_min and abs_max are
694+
# equal up to that precision?
695+
# Note: Internally using oom instead of 10 ** oom avoids some numerical
696+
# accuracy issues.
697+
oom_max = math.ceil(math.log10(abs_max))
698+
oom = 1 + next(oom for oom in itertools.count(oom_max, -1)
699+
if abs_min // 10 ** oom != abs_max // 10 ** oom)
700+
if (abs_max - abs_min) / 10 ** oom <= 1e-2:
701+
# Handle the case of straddling a multiple of a large power of ten
702+
# (relative to the span).
703+
# What is the smallest power of ten such that abs_min and abs_max
704+
# are no more than 1 apart at that precision?
705+
oom = 1 + next(oom for oom in itertools.count(oom_max, -1)
706+
if abs_max // 10 ** oom - abs_min // 10 ** oom > 1)
707+
# Only use offset if it saves at least two significant digits.
708+
self.offset = (sign * (abs_max // 10 ** oom) * 10 ** oom
709+
if abs_max // 10 ** oom >= 10
710+
else 0)
693711

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

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.