Skip to content

Commit fdd8a14

Browse files
QuLogicImgBotApp
authored andcommitted
Merge pull request matplotlib#21631 from jklymak/doc-update-gridspec-tutorial
DOC: change gridspec tutorial to arranging_axes tutorial
1 parent 869d86f commit fdd8a14

1 file changed

Lines changed: 382 additions & 0 deletions

File tree

Lines changed: 382 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,382 @@
1+
"""
2+
=====================================
3+
Arranging multiple Axes in a Figure
4+
=====================================
5+
6+
Often more than one Axes is wanted on a figure at a time, usually
7+
organized into a regular grid. Matplotlib has a variety of tools for
8+
working with grids of Axes that have evolved over the history of the library.
9+
Here we will discuss the tools we think users should use most often, the tools
10+
that underpin how Axes are organized, and mention some of the older tools.
11+
12+
.. note::
13+
14+
Matplotlib uses *Axes* to refer to the drawing area that contains
15+
data, x- and y-axis, ticks, labels, title, etc. See <figure_parts>_ for
16+
more details. Another term that is often used is "subplot", which refers
17+
to an Axes that is in a grid with other Axes objects.
18+
19+
Overview
20+
========
21+
22+
Create grid-shaped combinations of Axes
23+
---------------------------------------
24+
25+
`~matplotlib.pyplot.subplots`
26+
The primary function used to create figures and a grid of Axes. It
27+
creates and places all Axes on the figure at once, and returns an
28+
object array with handles for the Axes in the grid. See
29+
`.Figure.subplots`.
30+
31+
or
32+
33+
`~matplotlib.pyplot.subplot_mosaic`
34+
A simple way to create figures and a grid of Axes, with the added
35+
flexibility that Axes can also span rows or columns. The Axes
36+
are returned in a labelled dictionary instead of an array. See also
37+
`.Figure.subplot_mosaic` and :doc:`/tutorials/provisional/mosaic`.
38+
39+
Sometimes it is natural to have more than one distinct group of Axes grids,
40+
in which case Matplotlib has the concept of `~.figure.SubFigure`:
41+
42+
`~matplotlib.figure.SubFigure`
43+
A virtual figure within a figure.
44+
45+
Underlying tools
46+
----------------
47+
48+
Underlying these are the concept of a `~.gridspec.GridSpec` and
49+
a `~.SubplotSpec`:
50+
51+
`~matplotlib.gridspec.GridSpec`
52+
Specifies the geometry of the grid that a subplot will be
53+
placed. The number of rows and number of columns of the grid
54+
need to be set. Optionally, the subplot layout parameters
55+
(e.g., left, right, etc.) can be tuned.
56+
57+
`~matplotlib.gridspec.SubplotSpec`
58+
Specifies the location of the subplot in the given `.GridSpec`.
59+
60+
Adding single Axes at a time
61+
----------------------------
62+
63+
The above functions create all Axes in a single function call. It is also
64+
possible to add Axes one at a time, and this was originally how Matplotlib
65+
used to work. Doing so is generally less elegant and flexible, though
66+
sometimes useful for interactive work or to place an Axes in a custom
67+
location:
68+
69+
`~matplotlib.figure.Figure.add_axes`
70+
Adds a single axes at a location specified by
71+
``[left, bottom, width, height]`` in fractions of figure width or height.
72+
73+
`~matplotlib.pyplot.subplot` or `.Figure.add_subplot`
74+
Adds a single subplot on a figure, with 1-based indexing (inherited from
75+
Matlab). Columns and rows can be spanned by specifying a range of grid
76+
cells.
77+
78+
`~matplotlib.pyplot.subplot2grid`
79+
Similar to `.pyplot.subplot`, but uses 0-based indexing and two-d python
80+
slicing to choose cells.
81+
82+
.. redirect-from:: /tutorials/intermediate/gridspec
83+
84+
"""
85+
############################################################################
86+
# High-level methods for making grids
87+
# ===================================
88+
#
89+
# Basic 2x2 grid
90+
# --------------
91+
#
92+
# We can create a basic 2-by-2 grid of Axes using
93+
# `~matplotlib.pyplot.subplots`. It returns a `~matplotlib.figure.Figure`
94+
# instance and an array of `~matplotlib.axes.Axes` objects. The Axes
95+
# objects can be used to access methods to place artists on the Axes; here
96+
# we use `~.Axes.annotate`, but other examples could be `~.Axes.plot`,
97+
# `~.Axes.pcolormesh`, etc.
98+
99+
import matplotlib.pyplot as plt
100+
import numpy as np
101+
102+
fig, axs = plt.subplots(ncols=2, nrows=2, figsize=(5.5, 3.5),
103+
constrained_layout=True)
104+
# add an artist, in this case a nice label in the middle...
105+
for row in range(2):
106+
for col in range(2):
107+
axs[row, col].annotate(f'axs[{row}, {col}]', (0.5, 0.5),
108+
transform=axs[row, col].transAxes,
109+
ha='center', va='center', fontsize=18,
110+
color='darkgrey')
111+
fig.suptitle('plt.subplots()')
112+
113+
##############################################################################
114+
# We will annotate a lot of Axes, so lets encapsulate the annotation, rather
115+
# than having that large piece of annotation code every time we need it:
116+
117+
118+
def annotate_axes(ax, text, fontsize=18):
119+
ax.text(0.5, 0.5, text, transform=ax.transAxes,
120+
ha="center", va="center", fontsize=fontsize, color="darkgrey")
121+
122+
123+
##############################################################################
124+
# The same effect can be achieved with `~.pyplot.subplot_mosaic`,
125+
# but the return type is a dictionary instead of an array, where the user
126+
# can give the keys useful meanings. Here we provide two lists, each list
127+
# representing a row, and each element in the list a key representing the
128+
# column.
129+
130+
fig, axd = plt.subplot_mosaic([['upper left', 'upper right'],
131+
['lower left', 'lower right']],
132+
figsize=(5.5, 3.5), constrained_layout=True)
133+
for k in axd:
134+
annotate_axes(axd[k], f'axd["{k}"]', fontsize=14)
135+
fig.suptitle('plt.subplot_mosaic()')
136+
137+
############################################################################
138+
# Axes spanning rows or columns in a grid
139+
# ---------------------------------------
140+
#
141+
# Sometimes we want Axes to span rows or columns of the grid.
142+
# There are actually multiple ways to accomplish this, but the most
143+
# convenient is probably to use `~.pyplot.subplot_mosaic` by repeating one
144+
# of the keys:
145+
146+
fig, axd = plt.subplot_mosaic([['upper left', 'right'],
147+
['lower left', 'right']],
148+
figsize=(5.5, 3.5), constrained_layout=True)
149+
for k in axd:
150+
annotate_axes(axd[k], f'axd["{k}"]', fontsize=14)
151+
fig.suptitle('plt.subplot_mosaic()')
152+
153+
############################################################################
154+
# See below for the description of how to do the same thing using
155+
# `~matplotlib.gridspec.GridSpec` or `~matplotlib.pyplot.subplot2grid`.
156+
#
157+
# Variable widths or heights in a grid
158+
# ------------------------------------
159+
#
160+
# Both `~.pyplot.subplots` and `~.pyplot.subplot_mosaic` allow the rows
161+
# in the grid to be different heights, and the columns to be different
162+
# widths using the *gridspec_kw* keyword argument.
163+
# Spacing parameters accepted by `~matplotlib.gridspec.GridSpec`
164+
# can be passed to `~matplotlib.pyplot.subplots` and
165+
# `~matplotlib.pyplot.subplot_mosaic`:
166+
167+
gs_kw = dict(width_ratios=[1.4, 1], height_ratios=[1, 2])
168+
fig, axd = plt.subplot_mosaic([['upper left', 'right'],
169+
['lower left', 'right']],
170+
gridspec_kw=gs_kw, figsize=(5.5, 3.5),
171+
constrained_layout=True)
172+
for k in axd:
173+
annotate_axes(axd[k], f'axd["{k}"]', fontsize=14)
174+
fig.suptitle('plt.subplot_mosaic()')
175+
176+
############################################################################
177+
# Nested Axes layouts
178+
# -------------------
179+
#
180+
# Sometimes it is helpful to have two or more grids of Axes that
181+
# may not need to be related to one another. The most simple way toin
182+
# accomplish this is to use `.Figure.subfigures`. Note that the subfigure
183+
# layouts are independent, so the Axes spines in each subfigure are not
184+
# necessarily aligned. See below for a more verbose way to achieve the same
185+
# effect with `~.gridspec.GridSpecFromSubplotSpec`.
186+
187+
fig = plt.figure(constrained_layout=True)
188+
subfigs = fig.subfigures(1, 2, wspace=0.07, width_ratios=[1.5, 1.])
189+
axs0 = subfigs[0].subplots(2, 2)
190+
subfigs[0].set_facecolor('0.9')
191+
subfigs[0].suptitle('subfigs[0]\nLeft side')
192+
subfigs[0].supxlabel('xlabel for subfigs[0]')
193+
194+
axs1 = subfigs[1].subplots(3, 1)
195+
subfigs[1].suptitle('subfigs[1]')
196+
subfigs[1].supylabel('ylabel for subfigs[1]')
197+
198+
############################################################################
199+
# It is also possible to nest Axes using `~.pyplot.subplot_mosaic` using
200+
# nested lists. This method does not use subfigures, like above, so lacks
201+
# the ability to add per-subfigure ``suptitle`` and ``supxlabel``, etc.
202+
# Rather it is a convenience wrapper around the `~.SubplotSpec.subgridspec`
203+
# method described below.
204+
205+
inner = [['innerA'],
206+
['innerB']]
207+
outer = [['upper left', inner],
208+
['lower left', 'lower right']]
209+
210+
fig, axd = plt.subplot_mosaic(outer, constrained_layout=True)
211+
for k in axd:
212+
annotate_axes(axd[k], f'axd["{k}"]')
213+
214+
############################################################################
215+
# Low-level and advanced grid methods
216+
# ===================================
217+
#
218+
# Internally, the arrangement of a grid of Axes is controlled by creating
219+
# instances of `~.GridSpec` and `~.SubplotSpec`. *GridSpec* defines a
220+
# (possibly non-uniform) grid of cells. Indexing into the *GridSpec* returns
221+
# a SubplotSpec that covers one or more grid cells, and can be used to
222+
# specify the location of an Axes.
223+
#
224+
# The following examples show how to use low-level methods to arrange Axes
225+
# using *GridSpec* objects.
226+
#
227+
# Basic 2x2 grid
228+
# --------------
229+
#
230+
# We can accopmplish a 2x2 grid in the same manner as
231+
# ``plt.subplots(2, 2)``:
232+
233+
fig = plt.figure(figsize=(5.5, 3.5), constrained_layout=True)
234+
spec = fig.add_gridspec(ncols=2, nrows=2)
235+
236+
ax0 = fig.add_subplot(spec[0, 0])
237+
annotate_axes(ax0, 'ax0')
238+
239+
ax1 = fig.add_subplot(spec[0, 1])
240+
annotate_axes(ax1, 'ax1')
241+
242+
ax2 = fig.add_subplot(spec[1, 0])
243+
annotate_axes(ax2, 'ax2')
244+
245+
ax3 = fig.add_subplot(spec[1, 1])
246+
annotate_axes(ax3, 'ax3')
247+
248+
fig.suptitle('Manually added subplots using add_gridspec')
249+
250+
##############################################################################
251+
# Axes spanning rows or grids in a grid
252+
# -------------------------------------
253+
#
254+
# We can index the *spec* array using `NumPy slice syntax
255+
# <https://numpy.org/doc/stable/reference/arrays.indexing.html>`_
256+
# and the new Axes will span the slice. This would be the same
257+
# as ``fig, axd = plt.subplot_mosaic([['ax0', 'ax0'], ['ax1', 'ax2']], ...)``:
258+
259+
fig = plt.figure(figsize=(5.5, 3.5), constrained_layout=True)
260+
spec = fig.add_gridspec(2, 2)
261+
262+
ax0 = fig.add_subplot(spec[0, :])
263+
annotate_axes(ax0, 'ax0')
264+
265+
ax10 = fig.add_subplot(spec[1, 0])
266+
annotate_axes(ax10, 'ax10')
267+
268+
ax11 = fig.add_subplot(spec[1, 1])
269+
annotate_axes(ax11, 'ax11')
270+
271+
fig.suptitle('Manually added subplots, spanning a column')
272+
273+
###############################################################################
274+
# Manual adjustments to a *GridSpec* layout
275+
# -----------------------------------------
276+
#
277+
# When a *GridSpec* is explicitly used, you can adjust the layout
278+
# parameters of subplots that are created from the *GridSpec*. Note this
279+
# option is not compatible with ``constrained_layout`` or
280+
# `.Figure.tight_layout` which both ignore *left* and *right* and adjust
281+
# subplot sizes to fill the figure. Usually such manual placement
282+
# requires iterations to make the Axes tick labels not overlap the Axes.
283+
#
284+
# These spacing parameters can also be passed to `~.pyplot.subplots` and
285+
# `~.pyplot.subplot_mosaic` as the *gridspec_kw* argument.
286+
287+
fig = plt.figure(constrained_layout=False, facecolor='0.9')
288+
gs = fig.add_gridspec(nrows=3, ncols=3, left=0.05, right=0.75,
289+
hspace=0.1, wspace=0.05)
290+
ax0 = fig.add_subplot(gs[:-1, :])
291+
annotate_axes(ax0, 'ax0')
292+
ax1 = fig.add_subplot(gs[-1, :-1])
293+
annotate_axes(ax1, 'ax1')
294+
ax2 = fig.add_subplot(gs[-1, -1])
295+
annotate_axes(ax2, 'ax2')
296+
fig.suptitle('Manual gridspec with right=0.75')
297+
298+
###############################################################################
299+
# Nested layouts with SubplotSpec
300+
# -------------------------------
301+
#
302+
# You can create nested layout similar to `~.Figure.subfigures` using
303+
# `~.gridspec.SubplotSpec.subgridspec`. Here the Axes spines *are*
304+
# aligned.
305+
#
306+
# Note this is also available from the more verbose
307+
# `.gridspec.GridSpecFromSubplotSpec`.
308+
309+
fig = plt.figure(constrained_layout=True)
310+
gs0 = fig.add_gridspec(1, 2)
311+
312+
gs00 = gs0[0].subgridspec(2, 2)
313+
gs01 = gs0[1].subgridspec(3, 1)
314+
315+
for a in range(2):
316+
for b in range(2):
317+
ax = fig.add_subplot(gs00[a, b])
318+
annotate_axes(ax, f'axLeft[{a}, {b}]', fontsize=10)
319+
if a == 1 and b == 1:
320+
ax.set_xlabel('xlabel')
321+
for a in range(3):
322+
ax = fig.add_subplot(gs01[a])
323+
annotate_axes(ax, f'axRight[{a}, {b}]')
324+
if a == 2:
325+
ax.set_ylabel('ylabel')
326+
327+
fig.suptitle('nested gridspecs')
328+
329+
###############################################################################
330+
# Here's a more sophisticated example of nested *GridSpec*: We create an outer
331+
# 4x4 grid with each cell containing an inner 3x3 grid of Axes. We outline
332+
# the outer 4x4 grid by hiding appropriate spines in each of the inner 3x3
333+
# grids.
334+
335+
336+
def squiggle_xy(a, b, c, d, i=np.arange(0.0, 2*np.pi, 0.05)):
337+
return np.sin(i*a)*np.cos(i*b), np.sin(i*c)*np.cos(i*d)
338+
339+
fig = plt.figure(figsize=(8, 8), constrained_layout=False)
340+
outer_grid = fig.add_gridspec(4, 4, wspace=0, hspace=0)
341+
342+
for a in range(4):
343+
for b in range(4):
344+
# gridspec inside gridspec
345+
inner_grid = outer_grid[a, b].subgridspec(3, 3, wspace=0, hspace=0)
346+
axs = inner_grid.subplots() # Create all subplots for the inner grid.
347+
for (c, d), ax in np.ndenumerate(axs):
348+
ax.plot(*squiggle_xy(a + 1, b + 1, c + 1, d + 1))
349+
ax.set(xticks=[], yticks=[])
350+
351+
# show only the outside spines
352+
for ax in fig.get_axes():
353+
ss = ax.get_subplotspec()
354+
ax.spines.top.set_visible(ss.is_first_row())
355+
ax.spines.bottom.set_visible(ss.is_last_row())
356+
ax.spines.left.set_visible(ss.is_first_col())
357+
ax.spines.right.set_visible(ss.is_last_col())
358+
359+
plt.show()
360+
361+
#############################################################################
362+
#
363+
# More reading
364+
# ============
365+
#
366+
# - More details about :doc:`subplot mosaic </tutorials/provisional/mosaic>`.
367+
# - More details about :doc:`constrained layout
368+
# </tutorials/intermediate/constrainedlayout_guide>`, used to align
369+
# spacing in most of these examples.
370+
#
371+
# .. admonition:: References
372+
#
373+
# The use of the following functions, methods, classes and modules is shown
374+
# in this example:
375+
#
376+
# - `matplotlib.pyplot.subplots`
377+
# - `matplotlib.pyplot.subplot_mosaic`
378+
# - `matplotlib.figure.Figure.add_gridspec`
379+
# - `matplotlib.figure.Figure.add_subplot`
380+
# - `matplotlib.gridspec.GridSpec`
381+
# - `matplotlib.gridspec.SubplotSpec.subgridspec`
382+
# - `matplotlib.gridspec.GridSpecFromSubplotSpec`

0 commit comments

Comments
 (0)