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 e67c7fc

Browse filesBrowse files
committed
More precise choice of axes limits.
plt.plot([-.1, .2]) used to pick (in round numbers mode) [-.1, .25] as ylims due to floating point inaccuracies; change it to pick [-.1, .2] (up to floating point inaccuracies). Support for the (unused and deprecated-in-comment) "trim" keyword argument has been dropped as the way ticks are picked has changed. Many test images have changed! Implementation notes: - A bug in numpy's implementation of divmod (numpy/numpy#6127) is worked around. - The implementation of scale_range has also been cleaned. See #5767, #5738.
1 parent 8e38a54 commit e67c7fc
Copy full SHA for e67c7fc
Expand file treeCollapse file tree

23 files changed

+3495
-3593
lines changed
Binary file not shown.
Loading

‎lib/matplotlib/tests/baseline_images/test_axes/autoscale_tiny_range.svg

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/baseline_images/test_axes/autoscale_tiny_range.svg
+194-242Lines changed: 194 additions & 242 deletions
Loading
Loading
Binary file not shown.

‎lib/matplotlib/tests/baseline_images/test_axes/errorbar_mixed.svg

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/baseline_images/test_axes/errorbar_mixed.svg
+506-506Lines changed: 506 additions & 506 deletions
Loading
Binary file not shown.
Loading

‎lib/matplotlib/tests/baseline_images/test_axes/formatter_large_small.svg

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/baseline_images/test_axes/formatter_large_small.svg
+107-127Lines changed: 107 additions & 127 deletions
Loading
Loading
Binary file not shown.
Loading

‎lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_raster.svg

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_raster.svg
+30-42Lines changed: 30 additions & 42 deletions
Loading

‎lib/matplotlib/tests/test_axes.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_axes.py
+2-3Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
from matplotlib.testing.decorators import image_comparison, cleanup
2323
import matplotlib.pyplot as plt
2424
import matplotlib.markers as mmarkers
25-
from numpy.testing import assert_array_equal
25+
from numpy.testing import assert_allclose, assert_array_equal
2626
import warnings
2727
from matplotlib.cbook import IgnoredKeywordWarning
2828

@@ -3715,8 +3715,7 @@ def test_vline_limit():
37153715
ax.axvline(0.5)
37163716
ax.plot([-0.1, 0, 0.2, 0.1])
37173717
(ymin, ymax) = ax.get_ylim()
3718-
assert ymin == -0.1
3719-
assert ymax == 0.25
3718+
assert_allclose(ax.get_ylim(), (-.1, .2))
37203719

37213720

37223721
@cleanup

‎lib/matplotlib/ticker.py

Copy file name to clipboardExpand all lines: lib/matplotlib/ticker.py
+43-38Lines changed: 43 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -161,13 +161,23 @@
161161
from matplotlib import rcParams
162162
from matplotlib import cbook
163163
from matplotlib import transforms as mtransforms
164+
from matplotlib.cbook import mplDeprecation
164165

165166
import warnings
166167

167168
if six.PY3:
168169
long = int
169170

170171

172+
# Work around numpy/numpy#6127.
173+
def _divmod(x, y):
174+
if isinstance(x, np.generic):
175+
x = x.item()
176+
if isinstance(y, np.generic):
177+
y = y.item()
178+
return six.moves.builtins.divmod(x, y)
179+
180+
171181
def _mathdefault(s):
172182
"""
173183
For backward compatibility, in classic mode we display
@@ -1221,7 +1231,7 @@ def view_limits(self, vmin, vmax):
12211231
vmax += 1
12221232

12231233
if rcParams['axes.autolimit_mode'] == 'round_numbers':
1224-
exponent, remainder = divmod(math.log10(vmax - vmin), 1)
1234+
exponent, remainder = _divmod(math.log10(vmax - vmin), 1)
12251235
if remainder < 0.5:
12261236
exponent -= 1
12271237
scale = 10 ** (-exponent)
@@ -1247,30 +1257,30 @@ def __init__(self, base):
12471257

12481258
def lt(self, x):
12491259
'return the largest multiple of base < x'
1250-
d, m = divmod(x, self._base)
1260+
d, m = _divmod(x, self._base)
12511261
if closeto(m, 0) and not closeto(m / self._base, 1):
12521262
return (d - 1) * self._base
12531263
return d * self._base
12541264

12551265
def le(self, x):
12561266
'return the largest multiple of base <= x'
1257-
d, m = divmod(x, self._base)
1267+
d, m = _divmod(x, self._base)
12581268
if closeto(m / self._base, 1): # was closeto(m, self._base)
12591269
#looks like floating point error
12601270
return (d + 1) * self._base
12611271
return d * self._base
12621272

12631273
def gt(self, x):
12641274
'return the smallest multiple of base > x'
1265-
d, m = divmod(x, self._base)
1275+
d, m = _divmod(x, self._base)
12661276
if closeto(m / self._base, 1):
12671277
#looks like floating point error
12681278
return (d + 2) * self._base
12691279
return (d + 1) * self._base
12701280

12711281
def ge(self, x):
12721282
'return the smallest multiple of base >= x'
1273-
d, m = divmod(x, self._base)
1283+
d, m = _divmod(x, self._base)
12741284
if closeto(m, 0) and not closeto(m / self._base, 1):
12751285
return d * self._base
12761286
return (d + 1) * self._base
@@ -1326,23 +1336,13 @@ def view_limits(self, dmin, dmax):
13261336

13271337

13281338
def scale_range(vmin, vmax, n=1, threshold=100):
1329-
dv = abs(vmax - vmin)
1330-
if dv == 0: # maxabsv == 0 is a special case of this.
1331-
return 1.0, 0.0
1332-
# Note: this should never occur because
1333-
# vmin, vmax should have been checked by nonsingular(),
1334-
# and spread apart if necessary.
1335-
meanv = 0.5 * (vmax + vmin)
1339+
dv = abs(vmax - vmin) # > 0 as nonsingular is called before.
1340+
meanv = (vmax + vmin) / 2
13361341
if abs(meanv) / dv < threshold:
13371342
offset = 0
1338-
elif meanv > 0:
1339-
ex = divmod(math.log10(meanv), 1)[0]
1340-
offset = 10 ** ex
13411343
else:
1342-
ex = divmod(math.log10(-meanv), 1)[0]
1343-
offset = -10 ** ex
1344-
ex = divmod(math.log10(dv / n), 1)[0]
1345-
scale = 10 ** ex
1344+
offset = math.copysign(10 ** (math.log10(abs(meanv)) // 1), meanv)
1345+
scale = 10 ** (math.log10(dv / n) // 1)
13461346
return scale, offset
13471347

13481348

@@ -1352,7 +1352,6 @@ class MaxNLocator(Locator):
13521352
"""
13531353
default_params = dict(nbins=10,
13541354
steps=None,
1355-
trim=True,
13561355
integer=False,
13571356
symmetric=False,
13581357
prune=None)
@@ -1388,9 +1387,6 @@ def __init__(self, *args, **kwargs):
13881387
will be removed. If prune==None, no ticks will be removed.
13891388
13901389
"""
1391-
# I left "trim" out; it defaults to True, and it is not
1392-
# clear that there is any use case for False, so we may
1393-
# want to remove that kwarg. EF 2010/04/18
13941390
if args:
13951391
kwargs['nbins'] = args[0]
13961392
if len(args) > 1:
@@ -1406,7 +1402,9 @@ def set_params(self, **kwargs):
14061402
if self._nbins != 'auto':
14071403
self._nbins = int(self._nbins)
14081404
if 'trim' in kwargs:
1409-
self._trim = kwargs['trim']
1405+
warnings.warn(
1406+
"The 'trim' keyword has no effect since version 2.0.",
1407+
mplDeprecation)
14101408
if 'integer' in kwargs:
14111409
self._integer = kwargs['integer']
14121410
if 'symmetric' in kwargs:
@@ -1429,9 +1427,9 @@ def set_params(self, **kwargs):
14291427
if 'integer' in kwargs:
14301428
self._integer = kwargs['integer']
14311429
if self._integer:
1432-
self._steps = [n for n in self._steps if divmod(n, 1)[1] < 0.001]
1430+
self._steps = [n for n in self._steps if _divmod(n, 1)[1] < 0.001]
14331431

1434-
def bin_boundaries(self, vmin, vmax):
1432+
def _raw_ticks(self, vmin, vmax):
14351433
nbins = self._nbins
14361434
if nbins == 'auto':
14371435
nbins = max(min(self.axis.get_tick_space(), 9), 1)
@@ -1449,23 +1447,30 @@ def bin_boundaries(self, vmin, vmax):
14491447
if step < scaled_raw_step:
14501448
continue
14511449
step *= scale
1452-
best_vmin = step * divmod(vmin, step)[0]
1450+
best_vmin = vmin // step * step
14531451
best_vmax = best_vmin + step * nbins
1454-
if (best_vmax >= vmax):
1452+
if best_vmax >= vmax:
14551453
break
1456-
if self._trim:
1457-
extra_bins = int(divmod((best_vmax - vmax), step)[0])
1458-
nbins -= extra_bins
1459-
return (np.arange(nbins + 1) * step + best_vmin + offset)
1454+
1455+
# More than nbins may be required, e.g. vmin, vmax = -4.1, 4.1 gives
1456+
# nbins=9 but 10 bins are actually required after rounding. So we just
1457+
# create the bins that span the range we need instead.
1458+
low = round(Base(step).le(vmin - best_vmin) / step)
1459+
high = round(Base(step).ge(vmax - best_vmin) / step)
1460+
return np.arange(low, high + 1) * step + best_vmin + offset
1461+
1462+
@cbook.deprecated("2.0")
1463+
def bin_boundaries(self, vmin, vmax):
1464+
return self._raw_ticks(vmin, vmax)
14601465

14611466
def __call__(self):
14621467
vmin, vmax = self.axis.get_view_interval()
14631468
return self.tick_values(vmin, vmax)
14641469

14651470
def tick_values(self, vmin, vmax):
1466-
vmin, vmax = mtransforms.nonsingular(vmin, vmax, expander=1e-13,
1467-
tiny=1e-14)
1468-
locs = self.bin_boundaries(vmin, vmax)
1471+
vmin, vmax = mtransforms.nonsingular(
1472+
vmin, vmax, expander=1e-13, tiny=1e-14)
1473+
locs = self._raw_ticks(vmin, vmax)
14691474
prune = self._prune
14701475
if prune == 'lower':
14711476
locs = locs[1:]
@@ -1482,11 +1487,11 @@ def view_limits(self, dmin, dmax):
14821487
dmin = -maxabs
14831488
dmax = maxabs
14841489

1485-
dmin, dmax = mtransforms.nonsingular(dmin, dmax, expander=1e-12,
1486-
tiny=1.e-13)
1490+
dmin, dmax = mtransforms.nonsingular(
1491+
dmin, dmax, expander=1e-12, tiny=1e-13)
14871492

14881493
if rcParams['axes.autolimit_mode'] == 'round_numbers':
1489-
return np.take(self.bin_boundaries(dmin, dmax), [0, -1])
1494+
return self._raw_ticks(dmin, dmax)[[0, -1]]
14901495
else:
14911496
return dmin, dmax
14921497

Binary file not shown.
Loading

0 commit comments

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