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 846fde4

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 0abca30 commit 846fde4
Copy full SHA for 846fde4
Expand file treeCollapse file tree

23 files changed

+3490
-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
@@ -21,7 +21,7 @@
2121
from matplotlib.testing.decorators import image_comparison, cleanup
2222
import matplotlib.pyplot as plt
2323
import matplotlib.markers as mmarkers
24-
from numpy.testing import assert_array_equal
24+
from numpy.testing import assert_allclose, assert_array_equal
2525
import warnings
2626
from matplotlib.cbook import IgnoredKeywordWarning
2727

@@ -3708,8 +3708,7 @@ def test_vline_limit():
37083708
ax.axvline(0.5)
37093709
ax.plot([-0.1, 0, 0.2, 0.1])
37103710
(ymin, ymax) = ax.get_ylim()
3711-
assert ymin == -0.1
3712-
assert ymax == 0.25
3711+
assert_allclose(ax.get_ylim(), (-.1, .2))
37133712

37143713

37153714
@cleanup

‎lib/matplotlib/ticker.py

Copy file name to clipboardExpand all lines: lib/matplotlib/ticker.py
+38-38Lines changed: 38 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
@@ -1218,7 +1228,7 @@ def view_limits(self, vmin, vmax):
12181228
vmax += 1
12191229

12201230
if rcParams['axes.autolimit_mode'] == 'round_numbers':
1221-
exponent, remainder = divmod(math.log10(vmax - vmin), 1)
1231+
exponent, remainder = _divmod(math.log10(vmax - vmin), 1)
12221232
if remainder < 0.5:
12231233
exponent -= 1
12241234
scale = 10 ** (-exponent)
@@ -1244,30 +1254,30 @@ def __init__(self, base):
12441254

12451255
def lt(self, x):
12461256
'return the largest multiple of base < x'
1247-
d, m = divmod(x, self._base)
1257+
d, m = _divmod(x, self._base)
12481258
if closeto(m, 0) and not closeto(m / self._base, 1):
12491259
return (d - 1) * self._base
12501260
return d * self._base
12511261

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

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

12681278
def ge(self, x):
12691279
'return the smallest multiple of base >= x'
1270-
d, m = divmod(x, self._base)
1280+
d, m = _divmod(x, self._base)
12711281
if closeto(m, 0) and not closeto(m / self._base, 1):
12721282
return d * self._base
12731283
return (d + 1) * self._base
@@ -1323,23 +1333,13 @@ def view_limits(self, dmin, dmax):
13231333

13241334

13251335
def scale_range(vmin, vmax, n=1, threshold=100):
1326-
dv = abs(vmax - vmin)
1327-
if dv == 0: # maxabsv == 0 is a special case of this.
1328-
return 1.0, 0.0
1329-
# Note: this should never occur because
1330-
# vmin, vmax should have been checked by nonsingular(),
1331-
# and spread apart if necessary.
1332-
meanv = 0.5 * (vmax + vmin)
1336+
dv = abs(vmax - vmin) # > 0 as nonsingular is called before.
1337+
meanv = (vmax + vmin) / 2
13331338
if abs(meanv) / dv < threshold:
13341339
offset = 0
1335-
elif meanv > 0:
1336-
ex = divmod(math.log10(meanv), 1)[0]
1337-
offset = 10 ** ex
13381340
else:
1339-
ex = divmod(math.log10(-meanv), 1)[0]
1340-
offset = -10 ** ex
1341-
ex = divmod(math.log10(dv / n), 1)[0]
1342-
scale = 10 ** ex
1341+
offset = math.copysign(10 ** (math.log10(abs(meanv)) // 1), meanv)
1342+
scale = 10 ** (math.log10(dv / n) // 1)
13431343
return scale, offset
13441344

13451345

@@ -1349,7 +1349,6 @@ class MaxNLocator(Locator):
13491349
"""
13501350
default_params = dict(nbins=10,
13511351
steps=None,
1352-
trim=True,
13531352
integer=False,
13541353
symmetric=False,
13551354
prune=None)
@@ -1385,9 +1384,6 @@ def __init__(self, *args, **kwargs):
13851384
will be removed. If prune==None, no ticks will be removed.
13861385
13871386
"""
1388-
# I left "trim" out; it defaults to True, and it is not
1389-
# clear that there is any use case for False, so we may
1390-
# want to remove that kwarg. EF 2010/04/18
13911387
if args:
13921388
kwargs['nbins'] = args[0]
13931389
if len(args) > 1:
@@ -1403,7 +1399,8 @@ def set_params(self, **kwargs):
14031399
if self._nbins != 'auto':
14041400
self._nbins = int(self._nbins)
14051401
if 'trim' in kwargs:
1406-
self._trim = kwargs['trim']
1402+
warnings.warn("The 'trim' keyword has no effect anymore",
1403+
mplDeprecation)
14071404
if 'integer' in kwargs:
14081405
self._integer = kwargs['integer']
14091406
if 'symmetric' in kwargs:
@@ -1426,9 +1423,9 @@ def set_params(self, **kwargs):
14261423
if 'integer' in kwargs:
14271424
self._integer = kwargs['integer']
14281425
if self._integer:
1429-
self._steps = [n for n in self._steps if divmod(n, 1)[1] < 0.001]
1426+
self._steps = [n for n in self._steps if _divmod(n, 1)[1] < 0.001]
14301427

1431-
def bin_boundaries(self, vmin, vmax):
1428+
def _raw_ticks(self, vmin, vmax):
14321429
nbins = self._nbins
14331430
if nbins == 'auto':
14341431
nbins = self.axis.get_tick_space()
@@ -1446,23 +1443,26 @@ def bin_boundaries(self, vmin, vmax):
14461443
if step < scaled_raw_step:
14471444
continue
14481445
step *= scale
1449-
best_vmin = step * divmod(vmin, step)[0]
1446+
best_vmin = vmin // step * step
14501447
best_vmax = best_vmin + step * nbins
1451-
if (best_vmax >= vmax):
1448+
if best_vmax >= vmax:
14521449
break
1453-
if self._trim:
1454-
extra_bins = int(divmod((best_vmax - vmax), step)[0])
1455-
nbins -= extra_bins
1456-
return (np.arange(nbins + 1) * step + best_vmin + offset)
1450+
1451+
# More than nbins may be required, e.g. vmin, vmax = -4.1, 4.1 gives
1452+
# nbins=9 but 10 bins are actually required after rounding. So we just
1453+
# create the bins that span the range we need instead.
1454+
low = round(Base(step).le(vmin - best_vmin) / step)
1455+
high = round(Base(step).ge(vmax - best_vmin) / step)
1456+
return np.arange(low, high + 1) * step + best_vmin + offset
14571457

14581458
def __call__(self):
14591459
vmin, vmax = self.axis.get_view_interval()
14601460
return self.tick_values(vmin, vmax)
14611461

14621462
def tick_values(self, vmin, vmax):
1463-
vmin, vmax = mtransforms.nonsingular(vmin, vmax, expander=1e-13,
1464-
tiny=1e-14)
1465-
locs = self.bin_boundaries(vmin, vmax)
1463+
vmin, vmax = mtransforms.nonsingular(
1464+
vmin, vmax, expander=1e-13, tiny=1e-14)
1465+
locs = self._raw_ticks(vmin, vmax)
14661466
prune = self._prune
14671467
if prune == 'lower':
14681468
locs = locs[1:]
@@ -1479,11 +1479,11 @@ def view_limits(self, dmin, dmax):
14791479
dmin = -maxabs
14801480
dmax = maxabs
14811481

1482-
dmin, dmax = mtransforms.nonsingular(dmin, dmax, expander=1e-12,
1483-
tiny=1.e-13)
1482+
dmin, dmax = mtransforms.nonsingular(
1483+
dmin, dmax, expander=1e-12, tiny=1e-13)
14841484

14851485
if rcParams['axes.autolimit_mode'] == 'round_numbers':
1486-
return np.take(self.bin_boundaries(dmin, dmax), [0, -1])
1486+
return self._raw_ticks(dmin, dmax)[[0, -1]]
14871487
else:
14881488
return dmin, dmax
14891489

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.