Skip to content

Commit

Permalink
Only use offset if it saves >=2 sig. digits.
Browse files Browse the repository at this point in the history
Tests cases courtesy of @WeatherGod.  Slightly improved numerical
accuracy.
  • Loading branch information
anntzer committed Jan 7, 2016
1 parent c73a275 commit 313a998
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 57 deletions.
79 changes: 37 additions & 42 deletions lib/matplotlib/tests/test_ticker.py
Expand Up @@ -164,48 +164,43 @@ def test_ScalarFormatter_offset_value():
fig, ax = plt.subplots()
formatter = ax.get_xaxis().get_major_formatter()

def update_ticks(ax):
return next(ax.get_xaxis().iter_ticks())

ax.set_xlim(123, 189)
update_ticks(ax)
assert_equal(formatter.offset, 0)

ax.set_xlim(-189, -123)
update_ticks(ax)
assert_equal(formatter.offset, 0)

ax.set_xlim(12341, 12349)
update_ticks(ax)
assert_equal(formatter.offset, 12340)

ax.set_xlim(-12349, -12341)
update_ticks(ax)
assert_equal(formatter.offset, -12340)

ax.set_xlim(99999.5, 100010.5)
update_ticks(ax)
assert_equal(formatter.offset, 100000)

ax.set_xlim(-100010.5, -99999.5)
update_ticks(ax)
assert_equal(formatter.offset, -100000)

ax.set_xlim(99990.5, 100000.5)
update_ticks(ax)
assert_equal(formatter.offset, 100000)

ax.set_xlim(-100000.5, -99990.5)
update_ticks(ax)
assert_equal(formatter.offset, -100000)

ax.set_xlim(1233999, 1234001)
update_ticks(ax)
assert_equal(formatter.offset, 1234000)

ax.set_xlim(-1234001, -1233999)
update_ticks(ax)
assert_equal(formatter.offset, -1234000)
def check_offset_for(left, right, offset):
ax.set_xlim(left, right)
# Update ticks.
next(ax.get_xaxis().iter_ticks())
assert_equal(formatter.offset, offset)

test_data = [(123, 189, 0),
(-189, -123, 0),
(12341, 12349, 12340),
(-12349, -12341, -12340),
(99999.5, 100010.5, 100000),
(-100010.5, -99999.5, -100000),
(99990.5, 100000.5, 100000),
(-100000.5, -99990.5, -100000),
(1233999, 1234001, 1234000),
(-1234001, -1233999, -1234000),
# Test cases courtesy of @WeatherGod
(.4538, .4578, .45),
(3789.12, 3783.1, 3780),
(45124.3, 45831.75, 45000),
(0.000721, 0.0007243, 0.00072),
(12592.82, 12591.43, 12590),
(9., 12., 0),
(900., 1200., 0),
(1900., 1200., 0),
(0.99, 1.01, 1),
(9.99, 10.01, 10),
(99.99, 100.01, 100),
(5.99, 6.01, 6),
(15.99, 16.01, 16),
(-0.452, 0.492, 0),
(-0.492, 0.492, 0),
(12331.4, 12350.5, 12300),
(-12335.3, 12335.3, 0)]

for left, right, offset in test_data:
yield check_offset_for, left, right, offset


def _logfe_helper(formatter, base, locs, i, expected_result):
Expand Down
34 changes: 19 additions & 15 deletions lib/matplotlib/ticker.py
Expand Up @@ -564,34 +564,38 @@ def _compute_offset(self):
lmin, lmax = locs.min(), locs.max()
# min, max comparing absolute values (we want division to round towards
# zero so we work on absolute values).
abs_min, abs_max = sorted(map(abs, [lmin, lmax]))
# Only use offset if there are at least two ticks, every tick has the
# same sign, and if the span is small compared to the absolute values.
if (lmin == lmax or lmin <= 0 <= lmax or
(abs_max - abs_min) / abs_max >= 1e-2):
abs_min, abs_max = sorted([abs(float(lmin)), abs(float(lmax))])
# Only use offset if there are at least two ticks and every tick has
# the same sign.
if lmin == lmax or lmin <= 0 <= lmax:
self.offset = 0
return
sign = math.copysign(1, lmin)
# What is the smallest power of ten such that abs_min and abs_max are
# equal up to that precision?
oom = 10 ** int(math.log10(abs_max) + 1)
# Note: Internally using oom instead of 10 ** oom avoids some numerical
# accuracy issues.
oom = math.ceil(math.log10(abs_max))
while True:
if abs_min // oom != abs_max // oom:
oom *= 10
if abs_min // 10 ** oom != abs_max // 10 ** oom:
oom += 1
break
oom /= 10
if (abs_max - abs_min) / oom <= 1e-2:
oom -= 1
if (abs_max - abs_min) / 10 ** oom <= 1e-2:
# Handle the case of straddling a multiple of a large power of ten
# (relative to the span).
# What is the smallest power of ten such that abs_min and abs_max
# at most 1 apart?
oom = 10 ** int(math.log10(abs_max) + 1)
oom = math.ceil(math.log10(abs_max))
while True:
if abs_max // oom - abs_min // oom > 1:
oom *= 10
if abs_max // 10 ** oom - abs_min // 10 ** oom > 1:
oom += 1
break
oom /= 10
self.offset = sign * (abs_max // oom) * oom
oom -= 1
# Only use offset if it saves at least two significant digits.
self.offset = (sign * (abs_max // 10 ** oom) * 10 ** oom
if abs_max // 10 ** oom >= 10
else 0)

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

0 comments on commit 313a998

Please sign in to comment.