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

Skip to content

Commit c732394

Browse files
committed
Only autoscale_view() when needed, not after every plotting call.
This avoids quadratic complexity when accumulating sticky edges. Mostly, this just replaces autoscale_view() calls with setting a flag requesting that autoscale_view() be called the next time viewLim is accessed. Note that we cannot just do this in draw as this would break common idioms like ``` ax.plot(...) ax.set_xlim(0, None) # keep top limit to what plot() set ``` The main nontrivial changes are - Removal of sticky_edges from hexbin(): Previously, hexbin() actually did *not* respect the sticky_egdes settings for some reason (this can be checked visually); but with this patch it would respect them -- breaking the baseline images. So just don't set sticky_edges instead. - Making LinearLocator.numticks a property: Previously, some code using LinearLocator would only work after the locator has been used once, so that tick_values properly set numticks to not-None; but with this patch, tick_values is no longer called early enough; making numticks a property fixes that. Note that LinearLocator is likely extremely rarely used anyways... - In test_bbox_inches_tight (which uses the old "round_numbers" autolimits mode), the autolimits change depending on whether autoscaling happens before the call to `xticks([])` (old behavior) or after (because there's no notion of "round numbers" anymore. Here we can just force these limits. - test_multi_color_hatch relied on ax.bar() triggering an autoscale but ax.add_patch *not* doing so. Just disable autoscaling then. This patch also prepares towards fixing collections autoscaling problems when switching from linear to log scales (as that also needs to be deferred to as late as possible, once the scale is actually known).
1 parent ba0ea86 commit c732394

File tree

9 files changed

+143
-68
lines changed

9 files changed

+143
-68
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Autoscaling changes
2+
```````````````````
3+
4+
Matplotlib used to recompute autoscaled limits after every plotting
5+
(``plot()``, ``bar()``, etc.) call. It now only does so when actually
6+
rendering the canvas, or when the user queries the Axes limits. This is a
7+
major performance improvement for plots with a large number of artists.
8+
9+
In particular, this means that artists added manually with `Axes.add_line`,
10+
`Axes.add_patch`, etc. will be taken into account by the autoscale, even
11+
without an explicit call to `Axes.autoscale_view`.
12+
13+
In some cases, this can result in different limits being reported. If this is
14+
an issue, consider triggering a draw with `fig.canvas.draw`.
15+
16+
LogLocator.nonsingular now maintains the orders of its arguments
17+
````````````````````````````````````````````````````````````````
18+
19+
It no longer reorders them in increasing order. The new behavior is consistent
20+
with MaxNLocator.

lib/matplotlib/axes/_axes.py

Lines changed: 26 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -840,7 +840,7 @@ def axhline(self, y=0, xmin=0, xmax=1, **kwargs):
840840
trans = self.get_yaxis_transform(which='grid')
841841
l = mlines.Line2D([xmin, xmax], [y, y], transform=trans, **kwargs)
842842
self.add_line(l)
843-
self.autoscale_view(scalex=False, scaley=scaley)
843+
self._request_autoscale_view(scalex=False, scaley=scaley)
844844
return l
845845

846846
@docstring.dedent_interpd
@@ -909,7 +909,7 @@ def axvline(self, x=0, ymin=0, ymax=1, **kwargs):
909909
trans = self.get_xaxis_transform(which='grid')
910910
l = mlines.Line2D([x, x], [ymin, ymax], transform=trans, **kwargs)
911911
self.add_line(l)
912-
self.autoscale_view(scalex=scalex, scaley=False)
912+
self._request_autoscale_view(scalex=scalex, scaley=False)
913913
return l
914914

915915
@docstring.dedent_interpd
@@ -965,7 +965,7 @@ def axhspan(self, ymin, ymax, xmin=0, xmax=1, **kwargs):
965965
p = mpatches.Polygon(verts, **kwargs)
966966
p.set_transform(trans)
967967
self.add_patch(p)
968-
self.autoscale_view(scalex=False)
968+
self._request_autoscale_view(scalex=False)
969969
return p
970970

971971
def axvspan(self, xmin, xmax, ymin=0, ymax=1, **kwargs):
@@ -1030,7 +1030,7 @@ def axvspan(self, xmin, xmax, ymin=0, ymax=1, **kwargs):
10301030
p = mpatches.Polygon(verts, **kwargs)
10311031
p.set_transform(trans)
10321032
self.add_patch(p)
1033-
self.autoscale_view(scaley=False)
1033+
self._request_autoscale_view(scaley=False)
10341034
return p
10351035

10361036
@_preprocess_data(replace_names=["y", "xmin", "xmax", "colors"],
@@ -1105,7 +1105,7 @@ def hlines(self, y, xmin, xmax, colors='k', linestyles='solid',
11051105
corners = (minx, miny), (maxx, maxy)
11061106

11071107
self.update_datalim(corners)
1108-
self.autoscale_view()
1108+
self._request_autoscale_view()
11091109

11101110
return lines
11111111

@@ -1182,7 +1182,7 @@ def vlines(self, x, ymin, ymax, colors='k', linestyles='solid',
11821182

11831183
corners = (minx, miny), (maxx, maxy)
11841184
self.update_datalim(corners)
1185-
self.autoscale_view()
1185+
self._request_autoscale_view()
11861186

11871187
return lines
11881188

@@ -1398,7 +1398,7 @@ def eventplot(self, positions, orientation='horizontal', lineoffsets=1,
13981398
else: # "horizontal", None or "none" (see EventCollection)
13991399
corners = (minpos, minline), (maxpos, maxline)
14001400
self.update_datalim(corners)
1401-
self.autoscale_view()
1401+
self._request_autoscale_view()
14021402

14031403
return colls
14041404

@@ -1642,7 +1642,7 @@ def plot(self, *args, scalex=True, scaley=True, data=None, **kwargs):
16421642
lines = [*self._get_lines(*args, data=data, **kwargs)]
16431643
for line in lines:
16441644
self.add_line(line)
1645-
self.autoscale_view(scalex=scalex, scaley=scaley)
1645+
self._request_autoscale_view(scalex=scalex, scaley=scaley)
16461646
return lines
16471647

16481648
@_preprocess_data(replace_names=["x", "y"], label_namer="y")
@@ -1718,7 +1718,7 @@ def plot_date(self, x, y, fmt='o', tz=None, xdate=True, ydate=False,
17181718

17191719
ret = self.plot(x, y, fmt, **kwargs)
17201720

1721-
self.autoscale_view()
1721+
self._request_autoscale_view()
17221722

17231723
return ret
17241724

@@ -2422,7 +2422,7 @@ def bar(self, x, height, width=0.8, bottom=None, *, align="center",
24222422
ymin = ymin - np.max(yerr)
24232423
ymin = max(ymin * 0.9, 1e-100)
24242424
self.dataLim.intervaly = (ymin, ymax)
2425-
self.autoscale_view()
2425+
self._request_autoscale_view()
24262426

24272427
bar_container = BarContainer(patches, errorbar, label=label)
24282428
self.add_container(bar_container)
@@ -2623,7 +2623,7 @@ def broken_barh(self, xranges, yrange, **kwargs):
26232623

26242624
col = mcoll.BrokenBarHCollection(xranges_conv, yrange_conv, **kwargs)
26252625
self.add_collection(col, autolim=True)
2626-
self.autoscale_view()
2626+
self._request_autoscale_view()
26272627

26282628
return col
26292629

@@ -3429,7 +3429,7 @@ def extract_err(err, data):
34293429
for l in caplines:
34303430
self.add_line(l)
34313431

3432-
self.autoscale_view()
3432+
self._request_autoscale_view()
34333433
errorbar_container = ErrorbarContainer((data_line, tuple(caplines),
34343434
tuple(barcols)),
34353435
has_xerr=(xerr is not None),
@@ -4101,7 +4101,7 @@ def dopatch(xs, ys, **kwargs):
41014101
axis.set_major_formatter(formatter)
41024102
formatter.seq = [*formatter.seq, *datalabels]
41034103

4104-
self.autoscale_view(
4104+
self._request_autoscale_view(
41054105
scalex=self._autoscaleXon, scaley=self._autoscaleYon)
41064106

41074107
return dict(whiskers=whiskers, caps=caps, boxes=boxes,
@@ -4479,7 +4479,7 @@ def scatter(self, x, y, s=None, c=None, marker=None, cmap=None, norm=None,
44794479
self.set_ymargin(0.05)
44804480

44814481
self.add_collection(collection)
4482-
self.autoscale_view()
4482+
self._request_autoscale_view()
44834483

44844484
return collection
44854485

@@ -4832,9 +4832,7 @@ def hexbin(self, x, y, C=None, gridsize=100, bins=None,
48324832

48334833
corners = ((xmin, ymin), (xmax, ymax))
48344834
self.update_datalim(corners)
4835-
collection.sticky_edges.x[:] = [xmin, xmax]
4836-
collection.sticky_edges.y[:] = [ymin, ymax]
4837-
self.autoscale_view(tight=True)
4835+
self._request_autoscale_view(tight=True)
48384836

48394837
# add the collection last
48404838
self.add_collection(collection, autolim=False)
@@ -5004,7 +5002,7 @@ def quiver(self, *args, **kw):
50045002
q = mquiver.Quiver(self, *args, **kw)
50055003

50065004
self.add_collection(q, autolim=True)
5007-
self.autoscale_view()
5005+
self._request_autoscale_view()
50085006
return q
50095007
quiver.__doc__ = mquiver.Quiver.quiver_doc
50105008

@@ -5020,7 +5018,7 @@ def barbs(self, *args, **kw):
50205018

50215019
b = mquiver.Barbs(self, *args, **kw)
50225020
self.add_collection(b, autolim=True)
5023-
self.autoscale_view()
5021+
self._request_autoscale_view()
50245022
return b
50255023

50265024
# Uses a custom implementation of data-kwarg handling in
@@ -5075,7 +5073,7 @@ def fill(self, *args, data=None, **kwargs):
50755073
for poly in self._get_patches_for_fill(*args, data=data, **kwargs):
50765074
self.add_patch(poly)
50775075
patches.append(poly)
5078-
self.autoscale_view()
5076+
self._request_autoscale_view()
50795077
return patches
50805078

50815079
@_preprocess_data(replace_names=["x", "y1", "y2", "where"])
@@ -5257,7 +5255,7 @@ def get_interp_point(ind):
52575255
self.dataLim.update_from_data_xy(XY2, self.ignore_existing_data_limits,
52585256
updatex=False, updatey=True)
52595257
self.add_collection(collection, autolim=False)
5260-
self.autoscale_view()
5258+
self._request_autoscale_view()
52615259
return collection
52625260

52635261
@_preprocess_data(replace_names=["y", "x1", "x2", "where"])
@@ -5438,7 +5436,7 @@ def get_interp_point(ind):
54385436
self.dataLim.update_from_data_xy(X2Y, self.ignore_existing_data_limits,
54395437
updatex=True, updatey=False)
54405438
self.add_collection(collection, autolim=False)
5441-
self.autoscale_view()
5439+
self._request_autoscale_view()
54425440
return collection
54435441

54445442
#### plotting z(x,y): imshow, pcolor and relatives, contour
@@ -5937,7 +5935,7 @@ def pcolor(self, *args, alpha=None, norm=None, cmap=None, vmin=None,
59375935
collection.sticky_edges.y[:] = [miny, maxy]
59385936
corners = (minx, miny), (maxx, maxy)
59395937
self.update_datalim(corners)
5940-
self.autoscale_view()
5938+
self._request_autoscale_view()
59415939
return collection
59425940

59435941
@_preprocess_data()
@@ -6150,7 +6148,7 @@ def pcolormesh(self, *args, alpha=None, norm=None, cmap=None, vmin=None,
61506148
collection.sticky_edges.y[:] = [miny, maxy]
61516149
corners = (minx, miny), (maxx, maxy)
61526150
self.update_datalim(corners)
6153-
self.autoscale_view()
6151+
self._request_autoscale_view()
61546152
return collection
61556153

61566154
@_preprocess_data()
@@ -6320,22 +6318,22 @@ def pcolorfast(self, *args, alpha=None, norm=None, cmap=None, vmin=None,
63206318
ret.sticky_edges.x[:] = [xl, xr]
63216319
ret.sticky_edges.y[:] = [yb, yt]
63226320
self.update_datalim(np.array([[xl, yb], [xr, yt]]))
6323-
self.autoscale_view(tight=True)
6321+
self._request_autoscale_view(tight=True)
63246322
return ret
63256323

63266324
@_preprocess_data()
63276325
def contour(self, *args, **kwargs):
63286326
kwargs['filled'] = False
63296327
contours = mcontour.QuadContourSet(self, *args, **kwargs)
6330-
self.autoscale_view()
6328+
self._request_autoscale_view()
63316329
return contours
63326330
contour.__doc__ = mcontour.QuadContourSet._contour_doc
63336331

63346332
@_preprocess_data()
63356333
def contourf(self, *args, **kwargs):
63366334
kwargs['filled'] = True
63376335
contours = mcontour.QuadContourSet(self, *args, **kwargs)
6338-
self.autoscale_view()
6336+
self._request_autoscale_view()
63396337
return contours
63406338
contourf.__doc__ = mcontour.QuadContourSet._contour_doc
63416339

@@ -6842,7 +6840,7 @@ def hist(self, x, bins=None, range=None, density=None, weights=None,
68426840

68436841
self.set_autoscalex_on(_saved_autoscalex)
68446842
self.set_autoscaley_on(_saved_autoscaley)
6845-
self.autoscale_view()
6843+
self._request_autoscale_view()
68466844

68476845
if label is None:
68486846
labels = [None]

0 commit comments

Comments
 (0)