Skip to content

Commit d1dad03

Browse files
deep-jkljklymaktimhoffm
authored
Add tutorial about autoscaling (#18840)
* Add tutorial about autoscaling * Condensed arguments formatting * Language corrections. * Apply suggestions from code review Many thanks to @jklymak. I accept the suggestions as they are, but I sense that there will be need for some code fixups. Co-authored-by: Jody Klymak <jklymak@gmail.com> * Fix code after applying suggestions. * Apply all suggestions from code review Will be revised. Co-authored-by: Jody Klymak <jklymak@gmail.com> * Fix flake8 formatting. * Apply suggestions from code review Co-authored-by: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> * Remove fig.show() * Don't use "unchanged" use "default" Co-authored-by: Jody Klymak <jklymak@gmail.com> Co-authored-by: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com>
1 parent 39bf7ad commit d1dad03

File tree

1 file changed

+172
-0
lines changed

1 file changed

+172
-0
lines changed

tutorials/intermediate/autoscale.py

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
"""
2+
Autoscaling
3+
===========
4+
5+
The limits on an axis can be set manually (e.g. ``ax.set_xlim(xmin, xmax)``)
6+
or Matplotlib can set them automatically based on the data already on the axes.
7+
There are a number of options to this autoscaling behaviour, discussed below.
8+
"""
9+
10+
###############################################################################
11+
# We will start with a simple line plot showing that autoscaling
12+
# extends the axis limits 5% beyond the data limits (-2π, 2π).
13+
14+
import numpy as np
15+
import matplotlib as mpl
16+
import matplotlib.pyplot as plt
17+
18+
x = np.linspace(-2 * np.pi, 2 * np.pi, 100)
19+
y = np.sinc(x)
20+
21+
fig, ax = plt.subplots()
22+
ax.plot(x, y)
23+
24+
###############################################################################
25+
# Margins
26+
# -------
27+
# The default margin around the data limits is 5%:
28+
29+
ax.margins()
30+
31+
###############################################################################
32+
# The margins can be made larger using `~matplotlib.axes.Axes.margins`:
33+
34+
fig, ax = plt.subplots()
35+
ax.plot(x, y)
36+
ax.margins(0.2, 0.2)
37+
38+
###############################################################################
39+
# In general, margins can be in the range (-0.5, ∞), where negative margins set
40+
# the axes limits to a subrange of the data range, i.e. they clip data.
41+
# Using a single number for margins affects both axes, a single margin can be
42+
# customized using keyword arguments ``x`` or ``y``, but positional and keyword
43+
# interface cannot be combined.
44+
45+
fig, ax = plt.subplots()
46+
ax.plot(x, y)
47+
ax.margins(y=-0.2)
48+
49+
###############################################################################
50+
# Sticky edges
51+
# ------------
52+
# There are plot elements (`.Artist`\s) that are usually used without margins.
53+
# For example false-color images (e.g. created with `.Axes.imshow`) are not
54+
# considered in the margins calculation.
55+
#
56+
57+
xx, yy = np.meshgrid(x, x)
58+
zz = np.sinc(np.sqrt((xx - 1)**2 + (yy - 1)**2))
59+
60+
fig, ax = plt.subplots(ncols=2, figsize=(12, 8))
61+
ax[0].imshow(zz)
62+
ax[0].set_title("default margins")
63+
ax[1].imshow(zz)
64+
ax[1].margins(0.2)
65+
ax[1].set_title("margins(0.2)")
66+
67+
###############################################################################
68+
# This override of margins is determined by "sticky edges", a
69+
# property of `.Artist` class that can suppress adding margins to axis
70+
# limits. The effect of sticky edges can be disabled on an Axes by changing
71+
# `~matplotlib.axes.Axes.use_sticky_edges`.
72+
# Artists have a property `.Artist.sticky_edges`, and the values of
73+
# sticky edges can be changed by writing to ``Artist.sticky_edges.x`` or
74+
# ``.Artist.sticky_edges.y``.
75+
#
76+
# The following example shows how overriding works and when it is needed.
77+
78+
fig, ax = plt.subplots(ncols=3, figsize=(16, 10))
79+
ax[0].imshow(zz)
80+
ax[0].margins(0.2)
81+
ax[0].set_title("default use_sticky_edges\nmargins(0.2)")
82+
ax[1].imshow(zz)
83+
ax[1].margins(0.2)
84+
ax[1].use_sticky_edges = False
85+
ax[1].set_title("use_sticky_edges=False\nmargins(0.2)")
86+
ax[2].imshow(zz)
87+
ax[2].margins(-0.2)
88+
ax[2].set_title("default use_sticky_edges\nmargins(-0.2)")
89+
90+
###############################################################################
91+
# We can see that setting ``use_sticky_edges`` to *False* renders the image
92+
# with requested margins.
93+
#
94+
# While sticky edges don't increase the axis limits through extra margins,
95+
# negative margins are still taken into accout. This can be seen in
96+
# the reduced limits of the third image.
97+
#
98+
# Controlling autoscale
99+
# ---------------------
100+
#
101+
# By default, the limits are
102+
# recalculated every time you add a new curve to the plot:
103+
104+
fig, ax = plt.subplots(ncols=2, figsize=(12, 8))
105+
ax[0].plot(x, y)
106+
ax[0].set_title("Single curve")
107+
ax[1].plot(x, y)
108+
ax[1].plot(x * 2.0, y)
109+
ax[1].set_title("Two curves")
110+
111+
###############################################################################
112+
# However, there are cases when you don't want to automatically adjust the
113+
# viewport to new data.
114+
#
115+
# One way to disable autoscaling is to manually set the
116+
# axis limit. Let's say that we want to see only a part of the data in
117+
# greater detail. Setting the ``xlim`` persists even if we add more curves to
118+
# the data. To recalculate the new limits calling `.Axes.autoscale` will
119+
# toggle the functionality manually.
120+
121+
fig, ax = plt.subplots(ncols=2, figsize=(12, 8))
122+
ax[0].plot(x, y)
123+
ax[0].set_xlim(left=-1, right=1)
124+
ax[0].plot(x + np.pi * 0.5, y)
125+
ax[0].set_title("set_xlim(left=-1, right=1)\n")
126+
ax[1].plot(x, y)
127+
ax[1].set_xlim(left=-1, right=1)
128+
ax[1].plot(x + np.pi * 0.5, y)
129+
ax[1].autoscale()
130+
ax[1].set_title("set_xlim(left=-1, right=1)\nautoscale()")
131+
132+
###############################################################################
133+
# We can check that the first plot has autoscale disabled and that the second
134+
# plot has it enabled again by using `.Axes.get_autoscale_on()`:
135+
136+
print(ax[0].get_autoscale_on()) # False means disabled
137+
print(ax[1].get_autoscale_on()) # True means enabled -> recalculated
138+
139+
###############################################################################
140+
# Arguments of the autoscale function give us precise control over the process
141+
# of autoscaling. A combination of arguments ``enable``, and ``axis`` sets the
142+
# autoscaling feature for the selected axis (or both). The argument ``tight``
143+
# sets the margin of the selected axis to zero. To preserve settings of either
144+
# ``enable`` or ``tight`` you can set the opposite one to *None*, that way
145+
# it should not be modified. However, setting ``enable`` to *None* and tight
146+
# to *True* affects both axes regardless of the ``axis`` argument.
147+
148+
fig, ax = plt.subplots()
149+
ax.plot(x, y)
150+
ax.margins(0.2, 0.2)
151+
ax.autoscale(enable=None, axis="x", tight=True)
152+
153+
print(ax.margins())
154+
155+
###############################################################################
156+
# Working with collections
157+
# ------------------------
158+
#
159+
# Autoscale works out of the box for all lines, patches, and images added to
160+
# the axes. One of the artists that it won't work with is a `.Collection`.
161+
# After adding a collection to the axes, one has to manually trigger the
162+
# `~matplotlib.axes.Axes.autoscale_view()` to recalculate
163+
# axes limits.
164+
165+
fig, ax = plt.subplots()
166+
collection = mpl.collections.StarPolygonCollection(
167+
5, 0, [250, ], # five point star, zero angle, size 250px
168+
offsets=np.column_stack([x, y]), # Set the positions
169+
transOffset=ax.transData, # Propagate transformations of the Axes
170+
)
171+
ax.add_collection(collection)
172+
ax.autoscale_view()

0 commit comments

Comments
 (0)