Skip to content

Commit f99590c

Browse files
committed
DOC: modernize gridspec tutorial
1 parent 02d0c0c commit f99590c

File tree

9 files changed

+327
-273
lines changed

9 files changed

+327
-273
lines changed

.flake8

-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@ per-file-ignores =
9494
tutorials/colors/colormap-manipulation.py: E402
9595
tutorials/intermediate/artists.py: E402
9696
tutorials/intermediate/constrainedlayout_guide.py: E402
97-
tutorials/intermediate/gridspec.py: E402
9897
tutorials/intermediate/legend_guide.py: E402
9998
tutorials/intermediate/tight_layout_guide.py: E402
10099
tutorials/introductory/customizing.py: E501

doc/users/prev_whats_new/whats_new_1.0.rst

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ Sophisticated subplot grid layout
2323

2424
Jae-Joon Lee has written :mod:`~matplotlib.gridspec`, a new module for
2525
doing complex subplot layouts, featuring row and column spans and
26-
more. See :doc:`/tutorials/intermediate/gridspec` for a tutorial overview.
26+
more. See :doc:`/tutorials/intermediate/arranging_axes` for a tutorial
27+
overview.
2728

2829
.. figure:: ../../gallery/userdemo/images/sphx_glr_demo_gridspec01_001.png
2930
:target: ../../gallery/userdemo/demo_gridspec01.html

examples/lines_bars_and_markers/scatter_hist.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def scatter_hist(x, y, ax, ax_histx, ax_histy):
6060
# --------------------------------------------
6161
#
6262
# We define a gridspec with unequal width- and height-ratios to achieve desired
63-
# layout. Also see the :doc:`/tutorials/intermediate/gridspec` tutorial.
63+
# layout. Also see the :doc:`/tutorials/intermediate/arranging_axes` tutorial.
6464

6565
# Start with a square Figure.
6666
fig = plt.figure(figsize=(6, 6))

examples/subplots_axes_and_figures/gridspec_and_subplots.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88
and then remove the covered axes and fill the gap with a new bigger axes.
99
Here we create a layout with the bottom two axes in the last column combined.
1010
11-
See also :doc:`/tutorials/intermediate/gridspec`.
11+
To start with this layout (rather than removing the overlapping axes) use
12+
`~.pyplot.subplot_mosaic`.
13+
14+
See also :doc:`/tutorials/intermediate/arranging_axes`.
1215
"""
1316

1417
import matplotlib.pyplot as plt

lib/matplotlib/gridspec.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55
The `GridSpec` specifies the overall grid structure. Individual cells within
66
the grid are referenced by `SubplotSpec`\s.
77
8-
See the tutorial :doc:`/tutorials/intermediate/gridspec` for a comprehensive
9-
usage guide.
8+
Often, users need not access this module directly, and can use higher-level
9+
methods like `~.pyplot.subplots`, `~.pyplot.subplot_mosaic` and
10+
`~.Figure.subfigures`. See the tutorial
11+
:doc:`/tutorials/intermediate/arranging_axes` for a guide.
1012
"""
1113

1214
import copy
+314
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,314 @@
1+
"""
2+
==========================
3+
Arranging Axes in a Figure
4+
==========================
5+
6+
Often more than one axes is wanted on a figure at a time, and usually
7+
we want to organize those axes into a regular grid. Matplotlib has a
8+
variety of tools for working with grids of axes that have evolved over
9+
the history of the library. Here we will discuss the tools we think
10+
users should use most often, and then dicuss some of the older tools,
11+
and the tools that underpin how axes are organized.
12+
13+
How to create grid-shaped combinations of axes:
14+
15+
`~matplotlib.pyplot.subplots`
16+
The primary function used to create figures and a grid of axes. It is
17+
similar to `.pyplot.subplot`, but creates and places all axes on the
18+
figure at once, and returns an object array with handles for
19+
the axes in the grid. See also `.Figure.subplots`.
20+
21+
or
22+
23+
`~matplotlib.pyplot.subplot_mosaic`
24+
A more flexible function used to create figures and a grid of axes,
25+
with the added flexibility that some of the axes can span rows or
26+
columns, and that the axes are returned in a labelled dictionary instead
27+
of an array. See also `.Figure.subplot_mosaic`.
28+
29+
Sometimes it is natural to have more than one distinct group of axes grids,
30+
in which case Matplotlib has the concept of `~.figure.SubFigure`:
31+
32+
`~matplotlib.figure.SubFigure`
33+
A virtual figure within a figure.
34+
35+
Underlying these is the concept of a `~.gridspec.GridSpec` and
36+
`~.SubplotSpec`:
37+
38+
`~matplotlib.gridspec.GridSpec`
39+
Specifies the geometry of the grid that a subplot will be
40+
placed. The number of rows and number of columns of the grid
41+
need to be set. Optionally, the subplot layout parameters
42+
(e.g., left, right, etc.) can be tuned.
43+
44+
`~matplotlib.gridspec.SubplotSpec`
45+
Specifies the location of the subplot in the given `.GridSpec`.
46+
47+
An older way to add axes in grids:
48+
49+
`~matplotlib.pyplot.subplot2grid`
50+
A helper function that is similar to `.pyplot.subplot`,
51+
but uses 0-based indexing and let subplot to occupy multiple cells.
52+
This function is not covered in this tutorial.
53+
54+
.. redirect-from:: /tutorials/intermediate/gridspec
55+
56+
"""
57+
58+
import matplotlib.pyplot as plt
59+
60+
import numpy as np
61+
62+
############################################################################
63+
# High-level methods for making grids
64+
# ===================================
65+
#
66+
# Basic 2x2 grid
67+
# --------------
68+
#
69+
# We can create a basic 2-by-2 grid of axes using
70+
# :func:`~matplotlib.pyplot.subplots`. It returns a
71+
# :class:`~matplotlib.figure.Figure` instance and an array of
72+
# :class:`~matplotlib.axes.Axes` objects. The axes objects can
73+
# be used to access methods to place artists on the axes; here we
74+
# use `~.Axes.annotate`, but other examples could be `~.Axes.plot`,
75+
# `~.Axes.pcolormesh`, etc.
76+
77+
fig, axs = plt.subplots(ncols=2, nrows=2, figsize=(4.5, 3.5),
78+
constrained_layout=True)
79+
for row in range(2):
80+
for col in range(2):
81+
axs[row, col].annotate(f'axs[{row}, {col}]', (0.1, 0.5),
82+
xycoords='axes fraction', va='center')
83+
fig.suptitle('subplots')
84+
85+
##############################################################################
86+
# The same effect can be achieved with `~.pyplot.subplot_mosaic`,
87+
# but the return type is a dictionary instead of an array, where the user
88+
# can give the keys useful meanings. Here we provide two lists, each list
89+
# representing a row, and each element in the list a key representing the
90+
# column. Note that keys can be any dictionary key, but we typically use
91+
# strings:
92+
93+
fig, axs = plt.subplot_mosaic([['upleft', 'upright'], ['loleft', 'loright']],
94+
figsize=(4.5, 3.5), constrained_layout=True)
95+
for k in axs.keys():
96+
axs[k].annotate(f'axs["{k}"]', (0.1, 0.5),
97+
xycoords='axes fraction', va='center')
98+
fig.suptitle('subplot_mosaic')
99+
100+
############################################################################
101+
# Axes spanning rows or columns in a grid
102+
# ---------------------------------------
103+
#
104+
# Sometimes we want axes to span rows or columns of the grid.
105+
# There are actually multiple ways to accomplish this, but the most
106+
# convenient is probably to use `~.pyplot.subplot_mosaic` by repeating one
107+
# of the keys:
108+
109+
fig, axs = plt.subplot_mosaic([['upleft', 'right'], ['loleft', 'right']],
110+
figsize=(4.5, 3.5), constrained_layout=True)
111+
for k in axs.keys():
112+
axs[k].annotate(f'axs["{k}"]', (0.1, 0.5),
113+
xycoords='axes fraction', va='center')
114+
fig.suptitle('subplot_mosaic')
115+
116+
############################################################################
117+
# See below for the description of how to do the same thing using
118+
# `~matplotlib.gridspec.GridSpec` or ~matplotlib.pyplot.subplot2grid`.
119+
#
120+
# Variable widths or heights in a grid
121+
# ------------------------------------
122+
#
123+
# Both `~.pyplot.subplots` and `~.pyplot.subplot_mosaic` allow the rows
124+
# in the grid to be different heights, and the columns to be different
125+
# widths using the *gridspec_kw* keyword argument.
126+
# Any parameter accepted by :class:`~matplotlib.gridspec.GridSpec` can
127+
# be passed to `~matplotlib.pyplot.subplots` and
128+
# `~matplotlib.pyplot.subplot_mosaic`:
129+
130+
gs_kw = dict(width_ratios=[1, 2.2], height_ratios=[1, 2])
131+
fig, axs = plt.subplot_mosaic([['upleft', 'right'], ['loleft', 'right']],
132+
gridspec_kw=gs_kw, figsize=(4.5, 3.5),
133+
constrained_layout=True)
134+
for k in axs.keys():
135+
axs[k].annotate(f'axs["{k}"]', (0.1, 0.5),
136+
xycoords='axes fraction', va='center')
137+
fig.suptitle('subplot_mosaic')
138+
139+
############################################################################
140+
# Nested axes layouts
141+
# -------------------
142+
#
143+
# Sometimes it is helpful to have two or more grids of axes that
144+
# may not need to be related to one another. The most simple way to
145+
# accomplish this is to use `.Figure.subfigures`. Note that the alignement
146+
# of the subfigure layouts is independent with the axes spines in each
147+
# subfigure having independent positions. See below for a more verbose
148+
# way to acheive the same effect with `~.gridspec.GridSpecFromSubplotSpec`.
149+
150+
fig = plt.figure(constrained_layout=True)
151+
subfigs = fig.subfigures(1, 2, wspace=0.07, width_ratios=[1.5, 1.])
152+
axs0 = subfigs[0].subplots(2, 2)
153+
subfigs[0].set_facecolor('0.9')
154+
subfigs[0].suptitle('subfigs[0]\nLeft side')
155+
subfigs[0].supxlabel('xlabel for subfigs[0]')
156+
157+
axs1 = subfigs[1].subplots(3, 1)
158+
subfigs[1].suptitle('subfigs[1]')
159+
subfigs[1].supylabel('ylabel for subfigs[1]')
160+
161+
############################################################################
162+
# Low-level and advanced grid methods
163+
# ===================================
164+
#
165+
# `~.pyplot.subplots` and `~.pyplot.subplot_mosaic` provide high-level
166+
# interfaces to create `~.gridspec.GridSpec` objects and add associated
167+
# new axes to a figure at given `~.gridspec.SubplotSpec` inside the
168+
# *GridSpec*.
169+
#
170+
# Basic 2x2 grid
171+
# --------------
172+
#
173+
# We can accopmplish a 2x2 grid in the same manner as
174+
# ``plt.subplots(2, 2)``:
175+
176+
fig = plt.figure(figsize=(4.5, 3.5), constrained_layout=True)
177+
spec = fig.add_gridspec(ncols=2, nrows=2)
178+
ax0 = fig.add_subplot(spec[0, 0])
179+
ax0.annotate('ax0', (0.1, 0.5), xycoords='axes fraction', va='center')
180+
ax1 = fig.add_subplot(spec[0, 1])
181+
ax1.annotate('ax1', (0.1, 0.5), xycoords='axes fraction', va='center')
182+
ax2 = fig.add_subplot(spec[1, 0])
183+
ax2.annotate('ax2', (0.1, 0.5), xycoords='axes fraction', va='center')
184+
ax3 = fig.add_subplot(spec[1, 1])
185+
ax3.annotate('ax3', (0.1, 0.5), xycoords='axes fraction', va='center')
186+
fig.suptitle('Manually added subplots using add_gridspec')
187+
188+
#############################################################################
189+
# Axes spanning rows or grids in a grid
190+
# -------------------------------------
191+
#
192+
# We can index the *spec* array using `NumPy slice syntax
193+
# <https://numpy.org/doc/stable/reference/arrays.indexing.html>`_
194+
# and the new axes will span the slice:
195+
196+
fig = plt.figure(figsize=(4.5, 3.5), constrained_layout=True)
197+
spec = fig.add_gridspec(2, 2)
198+
ax0 = fig.add_subplot(spec[0, :])
199+
ax0.annotate('ax0', (0.1, 0.5), xycoords='axes fraction', va='center')
200+
ax10 = fig.add_subplot(spec[1, 0])
201+
ax10.annotate('ax10', (0.1, 0.5), xycoords='axes fraction', va='center')
202+
ax11 = fig.add_subplot(spec[1, 1])
203+
ax11.annotate('ax11', (0.1, 0.5), xycoords='axes fraction', va='center')
204+
fig.suptitle('Manually added subplots, spanning a column')
205+
206+
###############################################################################
207+
# Manual Adjustments to a Gridspec Layout
208+
# ---------------------------------------
209+
#
210+
# When a GridSpec is explicitly used, you can adjust the layout
211+
# parameters of subplots that are created from the GridSpec. Note this
212+
# option is not compatible with ``constrained_layout`` or
213+
# `.Figure.tight_layout` which both ignore *left* and *right* and adjust
214+
# subplot sizes to fill the figure. Usually such manual placement
215+
# requires iterations to make the axes tick labels not overlap the axes.
216+
217+
fig = plt.figure(constrained_layout=False)
218+
gs = fig.add_gridspec(nrows=3, ncols=3, left=0.05, right=0.75,
219+
hspace=0.1, wspace=0.05)
220+
ax0 = fig.add_subplot(gs[:-1, :])
221+
ax0.annotate('ax0', (0.1, 0.5), xycoords='axes fraction', va='center')
222+
ax1 = fig.add_subplot(gs[-1, :-1])
223+
ax1.annotate('ax1', (0.1, 0.5), xycoords='axes fraction', va='center')
224+
ax2 = fig.add_subplot(gs[-1, -1])
225+
ax2.annotate('ax2', (0.1, 0.5), xycoords='axes fraction', va='center')
226+
fig.suptitle('Manual gridspec with right=0.75')
227+
228+
###############################################################################
229+
# Nested layouts with SubPlotSpec
230+
# ===============================
231+
#
232+
# You can create nested layout similar to `~.Figure.subfigures` using
233+
# `~.gridspec.SubplotSpec.subgridspec`. Here the axes spines _are_
234+
# aligned.
235+
#
236+
# Note this is also available from the more verbose
237+
# `.gridspec.GridSpecFromSubplotSpec`.
238+
239+
fig = plt.figure(constrained_layout=True)
240+
gs0 = fig.add_gridspec(1, 2)
241+
242+
gs00 = gs0[0].subgridspec(2, 2)
243+
gs01 = gs0[1].subgridspec(3, 1)
244+
245+
for a in range(2):
246+
for b in range(2):
247+
ax = fig.add_subplot(gs00[a, b])
248+
ax.annotate(f'axLeft[{a}, {b}]', (0.1, 0.5), xycoords='axes fraction',
249+
va='center')
250+
if a == 1 and b == 1:
251+
ax.set_xlabel('xlabel')
252+
for a in range(3):
253+
ax = fig.add_subplot(gs01[a])
254+
ax.annotate(f'axRight[{a}]', (0.1, 0.5), xycoords='axes fraction',
255+
va='center')
256+
if a == 2:
257+
ax.set_ylabel('ylabel')
258+
259+
fig.suptitle('nested gridspecs')
260+
261+
###############################################################################
262+
# Here's a more sophisticated example of nested GridSpec where we put
263+
# a box around each cell of the outer 4x4 grid, by hiding appropriate
264+
# spines in each of the inner 3x3 grids.
265+
266+
267+
def squiggle_xy(a, b, c, d, i=np.arange(0.0, 2*np.pi, 0.05)):
268+
return np.sin(i*a)*np.cos(i*b), np.sin(i*c)*np.cos(i*d)
269+
270+
fig = plt.figure(figsize=(8, 8), constrained_layout=False)
271+
outer_grid = fig.add_gridspec(4, 4, wspace=0, hspace=0)
272+
273+
for a in range(4):
274+
for b in range(4):
275+
# gridspec inside gridspec
276+
inner_grid = outer_grid[a, b].subgridspec(3, 3, wspace=0, hspace=0)
277+
axs = inner_grid.subplots() # Create all subplots for the inner grid.
278+
for (c, d), ax in np.ndenumerate(axs):
279+
ax.plot(*squiggle_xy(a + 1, b + 1, c + 1, d + 1))
280+
ax.set(xticks=[], yticks=[])
281+
282+
# show only the outside spines
283+
for ax in fig.get_axes():
284+
ss = ax.get_subplotspec()
285+
ax.spines.top.set_visible(ss.is_first_row())
286+
ax.spines.bottom.set_visible(ss.is_last_row())
287+
ax.spines.left.set_visible(ss.is_first_col())
288+
ax.spines.right.set_visible(ss.is_last_col())
289+
290+
plt.show()
291+
292+
#############################################################################
293+
#
294+
# More reading
295+
# ============
296+
#
297+
# - More details about :doc:`subplot mosaic </tutorials/provisional/mosaic>`.
298+
# - more details about
299+
# :doc:`constrained layout
300+
# </tutorials/intermediate/constrainedlayout_guide>`, used to align
301+
# spacing in most of these examples.
302+
#
303+
# .. admonition:: References
304+
#
305+
# The use of the following functions, methods, classes and modules is shown
306+
# in this example:
307+
#
308+
# - `matplotlib.pyplot.subplots`
309+
# - `matplotlib.pyplot.subplot_mosaic`
310+
# - `matplotlib.figure.Figure.add_gridspec`
311+
# - `matplotlib.figure.Figure.add_subplot`
312+
# - `matplotlib.gridspec.GridSpec`
313+
# - `matplotlib.gridspec.SubplotSpec.subgridspec`
314+
# - `matplotlib.gridspec.GridSpecFromSubplotSpec`

0 commit comments

Comments
 (0)