Files
notes_estom/Python/matplotlab/intermediate/gridspec.md
2020-09-26 22:03:11 +08:00

305 lines
12 KiB
Markdown

---
sidebarDepth: 3
sidebar: auto
---
# Customizing Figure Layouts Using GridSpec and Other Functions
How to create grid-shaped combinations of axes.
[``subplots()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.subplots.html#matplotlib.pyplot.subplots)
[``GridSpec``](https://matplotlib.orgapi/_as_gen/matplotlib.gridspec.GridSpec.html#matplotlib.gridspec.GridSpec)
[``SubplotSpec``](https://matplotlib.orgapi/_as_gen/matplotlib.gridspec.SubplotSpec.html#matplotlib.gridspec.SubplotSpec)
[``subplot2grid()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.subplot2grid.html#matplotlib.pyplot.subplot2grid)
``` python
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
```
## Basic Quickstart Guide
These first two examples show how to create a basic 2-by-2 grid using
both [``subplots()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.subplots.html#matplotlib.pyplot.subplots) and [``gridspec``](https://matplotlib.orgapi/gridspec_api.html#module-matplotlib.gridspec).
Using [``subplots()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.subplots.html#matplotlib.pyplot.subplots) is quite simple.
It returns a [``Figure``](https://matplotlib.orgapi/_as_gen/matplotlib.figure.Figure.html#matplotlib.figure.Figure) instance and an array of
[``Axes``](https://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes) objects.
``` python
fig1, f1_axes = plt.subplots(ncols=2, nrows=2, constrained_layout=True)
```
![sphx_glr_gridspec_001](https://matplotlib.org/_images/sphx_glr_gridspec_001.png)
For a simple use case such as this, [``gridspec``](https://matplotlib.orgapi/gridspec_api.html#module-matplotlib.gridspec) is
perhaps overly verbose.
You have to create the figure and [``GridSpec``](https://matplotlib.orgapi/_as_gen/matplotlib.gridspec.GridSpec.html#matplotlib.gridspec.GridSpec)
instance separately, then pass elements of gridspec instance to the
[``add_subplot()``](https://matplotlib.orgapi/_as_gen/matplotlib.figure.Figure.html#matplotlib.figure.Figure.add_subplot) method to create the axes
objects.
The elements of the gridspec are accessed in generally the same manner as
numpy arrays.
``` python
fig2 = plt.figure(constrained_layout=True)
spec2 = gridspec.GridSpec(ncols=2, nrows=2, figure=fig2)
f2_ax1 = fig2.add_subplot(spec2[0, 0])
f2_ax2 = fig2.add_subplot(spec2[0, 1])
f2_ax3 = fig2.add_subplot(spec2[1, 0])
f2_ax4 = fig2.add_subplot(spec2[1, 1])
```
![sphx_glr_gridspec_002](https://matplotlib.org/_images/sphx_glr_gridspec_002.png)
The power of gridspec comes in being able to create subplots that span
rows and columns. Note the
[Numpy slice](https://docs.scipy.org/doc/numpy/reference/arrays.indexing.html)
syntax for selecting the part of the gridspec each subplot will occupy.
Note that we have also used the convenience method [``Figure.add_gridspec``](https://matplotlib.orgapi/_as_gen/matplotlib.figure.Figure.html#matplotlib.figure.Figure.add_gridspec)
instead of [``gridspec.GridSpec``](https://matplotlib.orgapi/_as_gen/matplotlib.gridspec.GridSpec.html#matplotlib.gridspec.GridSpec), potentially saving the user an import,
and keeping the namespace cleaner.
``` python
fig3 = plt.figure(constrained_layout=True)
gs = fig3.add_gridspec(3, 3)
f3_ax1 = fig3.add_subplot(gs[0, :])
f3_ax1.set_title('gs[0, :]')
f3_ax2 = fig3.add_subplot(gs[1, :-1])
f3_ax2.set_title('gs[1, :-1]')
f3_ax3 = fig3.add_subplot(gs[1:, -1])
f3_ax3.set_title('gs[1:, -1]')
f3_ax4 = fig3.add_subplot(gs[-1, 0])
f3_ax4.set_title('gs[-1, 0]')
f3_ax5 = fig3.add_subplot(gs[-1, -2])
f3_ax5.set_title('gs[-1, -2]')
```
![sphx_glr_gridspec_003](https://matplotlib.org/_images/sphx_glr_gridspec_003.png)
[``gridspec``](https://matplotlib.orgapi/gridspec_api.html#module-matplotlib.gridspec) is also indispensable for creating subplots
of different widths via a couple of methods.
The method shown here is similar to the one above and initializes a
uniform grid specification,
and then uses numpy indexing and slices to allocate multiple
"cells" for a given subplot.
``` python
fig4 = plt.figure(constrained_layout=True)
spec4 = fig4.add_gridspec(ncols=2, nrows=2)
anno_opts = dict(xy=(0.5, 0.5), xycoords='axes fraction',
va='center', ha='center')
f4_ax1 = fig4.add_subplot(spec4[0, 0])
f4_ax1.annotate('GridSpec[0, 0]', **anno_opts)
fig4.add_subplot(spec4[0, 1]).annotate('GridSpec[0, 1:]', **anno_opts)
fig4.add_subplot(spec4[1, 0]).annotate('GridSpec[1:, 0]', **anno_opts)
fig4.add_subplot(spec4[1, 1]).annotate('GridSpec[1:, 1:]', **anno_opts)
```
![sphx_glr_gridspec_004](https://matplotlib.org/_images/sphx_glr_gridspec_004.png)
Another option is to use the ``width_ratios`` and ``height_ratios``
parameters. These keyword arguments are lists of numbers.
Note that absolute values are meaningless, only their relative ratios
matter. That means that ``width_ratios=[2, 4, 8]`` is equivalent to
``width_ratios=[1, 2, 4]`` within equally wide figures.
For the sake of demonstration, we'll blindly create the axes within
``for`` loops since we won't need them later.
``` python
fig5 = plt.figure(constrained_layout=True)
widths = [2, 3, 1.5]
heights = [1, 3, 2]
spec5 = fig5.add_gridspec(ncols=3, nrows=3, width_ratios=widths,
height_ratios=heights)
for row in range(3):
for col in range(3):
ax = fig5.add_subplot(spec5[row, col])
label = 'Width: {}\nHeight: {}'.format(widths[col], heights[row])
ax.annotate(label, (0.1, 0.5), xycoords='axes fraction', va='center')
```
![sphx_glr_gridspec_005](https://matplotlib.org/_images/sphx_glr_gridspec_005.png)
Learning to use ``width_ratios`` and ``height_ratios`` is particularly
useful since the top-level function [``subplots()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.subplots.html#matplotlib.pyplot.subplots)
accepts them within the ``gridspec_kw`` parameter.
For that matter, any parameter accepted by
[``GridSpec``](https://matplotlib.orgapi/_as_gen/matplotlib.gridspec.GridSpec.html#matplotlib.gridspec.GridSpec) can be passed to
[``subplots()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.subplots.html#matplotlib.pyplot.subplots) via the ``gridspec_kw`` parameter.
This example recreates the previous figure without directly using a
gridspec instance.
``` python
gs_kw = dict(width_ratios=widths, height_ratios=heights)
fig6, f6_axes = plt.subplots(ncols=3, nrows=3, constrained_layout=True,
gridspec_kw=gs_kw)
for r, row in enumerate(f6_axes):
for c, ax in enumerate(row):
label = 'Width: {}\nHeight: {}'.format(widths[c], heights[r])
ax.annotate(label, (0.1, 0.5), xycoords='axes fraction', va='center')
```
![sphx_glr_gridspec_006](https://matplotlib.org/_images/sphx_glr_gridspec_006.png)
The ``subplots`` and ``gridspec`` methods can be combined since it is
sometimes more convenient to make most of the subplots using ``subplots``
and then remove some and combine them. Here we create a layout with
the bottom two axes in the last column combined.
``` python
fig7, f7_axs = plt.subplots(ncols=3, nrows=3)
gs = f7_axs[1, 2].get_gridspec()
# remove the underlying axes
for ax in f7_axs[1:, -1]:
ax.remove()
axbig = fig7.add_subplot(gs[1:, -1])
axbig.annotate('Big Axes \nGridSpec[1:, -1]', (0.1, 0.5),
xycoords='axes fraction', va='center')
fig7.tight_layout()
```
![sphx_glr_gridspec_007](https://matplotlib.org/_images/sphx_glr_gridspec_007.png)
## Fine Adjustments to a Gridspec Layout
When a GridSpec is explicitly used, you can adjust the layout
parameters of subplots that are created from the GridSpec. Note this
option is not compatible with ``constrained_layout`` or
[``Figure.tight_layout``](https://matplotlib.orgapi/_as_gen/matplotlib.figure.Figure.html#matplotlib.figure.Figure.tight_layout) which both adjust subplot sizes to fill the
figure.
``` python
fig8 = plt.figure(constrained_layout=False)
gs1 = fig8.add_gridspec(nrows=3, ncols=3, left=0.05, right=0.48, wspace=0.05)
f8_ax1 = fig8.add_subplot(gs1[:-1, :])
f8_ax2 = fig8.add_subplot(gs1[-1, :-1])
f8_ax3 = fig8.add_subplot(gs1[-1, -1])
```
![sphx_glr_gridspec_008](https://matplotlib.org/_images/sphx_glr_gridspec_008.png)
This is similar to [``subplots_adjust()``](https://matplotlib.orgapi/_as_gen/matplotlib.pyplot.subplots_adjust.html#matplotlib.pyplot.subplots_adjust), but it only
affects the subplots that are created from the given GridSpec.
For example, compare the left and right sides of this figure:
``` python
fig9 = plt.figure(constrained_layout=False)
gs1 = fig9.add_gridspec(nrows=3, ncols=3, left=0.05, right=0.48,
wspace=0.05)
f9_ax1 = fig9.add_subplot(gs1[:-1, :])
f9_ax2 = fig9.add_subplot(gs1[-1, :-1])
f9_ax3 = fig9.add_subplot(gs1[-1, -1])
gs2 = fig9.add_gridspec(nrows=3, ncols=3, left=0.55, right=0.98,
hspace=0.05)
f9_ax4 = fig9.add_subplot(gs2[:, :-1])
f9_ax5 = fig9.add_subplot(gs2[:-1, -1])
f9_ax6 = fig9.add_subplot(gs2[-1, -1])
```
![sphx_glr_gridspec_009](https://matplotlib.org/_images/sphx_glr_gridspec_009.png)
## GridSpec using SubplotSpec
You can create GridSpec from the [``SubplotSpec``](https://matplotlib.orgapi/_as_gen/matplotlib.gridspec.SubplotSpec.html#matplotlib.gridspec.SubplotSpec),
in which case its layout parameters are set to that of the location of
the given SubplotSpec.
Note this is also available from the more verbose
[``gridspec.GridSpecFromSubplotSpec``](https://matplotlib.orgapi/_as_gen/matplotlib.gridspec.GridSpecFromSubplotSpec.html#matplotlib.gridspec.GridSpecFromSubplotSpec).
``` python
fig10 = plt.figure(constrained_layout=True)
gs0 = fig10.add_gridspec(1, 2)
gs00 = gs0[0].subgridspec(2, 3)
gs01 = gs0[1].subgridspec(3, 2)
for a in range(2):
for b in range(3):
fig10.add_subplot(gs00[a, b])
fig10.add_subplot(gs01[b, a])
```
![sphx_glr_gridspec_010](https://matplotlib.org/_images/sphx_glr_gridspec_010.png)
## A Complex Nested GridSpec using SubplotSpec
Here's a more sophisticated example of nested GridSpec where we put
a box around each cell of the outer 4x4 grid, by hiding appropriate
spines in each of the inner 3x3 grids.
``` python
import numpy as np
from itertools import product
def squiggle_xy(a, b, c, d, i=np.arange(0.0, 2*np.pi, 0.05)):
return np.sin(i*a)*np.cos(i*b), np.sin(i*c)*np.cos(i*d)
fig11 = plt.figure(figsize=(8, 8), constrained_layout=False)
# gridspec inside gridspec
outer_grid = fig11.add_gridspec(4, 4, wspace=0.0, hspace=0.0)
for i in range(16):
inner_grid = outer_grid[i].subgridspec(3, 3, wspace=0.0, hspace=0.0)
a, b = int(i/4)+1, i % 4+1
for j, (c, d) in enumerate(product(range(1, 4), repeat=2)):
ax = fig11.add_subplot(inner_grid[j])
ax.plot(*squiggle_xy(a, b, c, d))
ax.set_xticks([])
ax.set_yticks([])
fig11.add_subplot(ax)
all_axes = fig11.get_axes()
# show only the outside spines
for ax in all_axes:
for sp in ax.spines.values():
sp.set_visible(False)
if ax.is_first_row():
ax.spines['top'].set_visible(True)
if ax.is_last_row():
ax.spines['bottom'].set_visible(True)
if ax.is_first_col():
ax.spines['left'].set_visible(True)
if ax.is_last_col():
ax.spines['right'].set_visible(True)
plt.show()
```
![sphx_glr_gridspec_011](https://matplotlib.org/_images/sphx_glr_gridspec_011.png)
### References
The usage of the following functions and methods is shown in this example:
``` python
matplotlib.pyplot.subplots
matplotlib.figure.Figure.add_gridspec
matplotlib.figure.Figure.add_subplot
matplotlib.gridspec.GridSpec
matplotlib.gridspec.SubplotSpec.subgridspec
matplotlib.gridspec.GridSpecFromSubplotSpec
```
**Total running time of the script:** ( 0 minutes 8.732 seconds)
## Download
- [Download Python source code: gridspec.py](https://matplotlib.org/_downloads/54501e30d0a29665618afe715673cb41/gridspec.py)
- [Download Jupyter notebook: gridspec.ipynb](https://matplotlib.org/_downloads/0eaf234b06f4f7a6a52fa9ca11b63755/gridspec.ipynb)