From 81440bedd24d83626946cb70167c495caa89cc91 Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Sun, 14 Nov 2021 10:37:22 +0100 Subject: [PATCH] DOC: modernize gridspec tutorial Co-authored-by: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Co-authored-by: Elliott Sales de Andrade --- .flake8 | 1 - doc/users/prev_whats_new/whats_new_1.0.rst | 3 +- .../lines_bars_and_markers/scatter_hist.py | 2 +- .../gridspec_and_subplots.py | 5 +- lib/matplotlib/gridspec.py | 6 +- tutorials/intermediate/arranging_axes.py | 382 ++++++++++++++++++ tutorials/intermediate/gridspec.py | 265 ------------ tutorials/intermediate/tight_layout_guide.py | 2 +- tutorials/provisional/mosaic.py | 2 +- 9 files changed, 395 insertions(+), 273 deletions(-) create mode 100644 tutorials/intermediate/arranging_axes.py delete mode 100644 tutorials/intermediate/gridspec.py diff --git a/.flake8 b/.flake8 index 9de6a96bfc59..a33c5e93afac 100644 --- a/.flake8 +++ b/.flake8 @@ -94,7 +94,6 @@ per-file-ignores = tutorials/colors/colormap-manipulation.py: E402 tutorials/intermediate/artists.py: E402 tutorials/intermediate/constrainedlayout_guide.py: E402 - tutorials/intermediate/gridspec.py: E402 tutorials/intermediate/legend_guide.py: E402 tutorials/intermediate/tight_layout_guide.py: E402 tutorials/introductory/customizing.py: E501 diff --git a/doc/users/prev_whats_new/whats_new_1.0.rst b/doc/users/prev_whats_new/whats_new_1.0.rst index bce014e5e4e2..af078f2a734d 100644 --- a/doc/users/prev_whats_new/whats_new_1.0.rst +++ b/doc/users/prev_whats_new/whats_new_1.0.rst @@ -23,7 +23,8 @@ Sophisticated subplot grid layout Jae-Joon Lee has written :mod:`~matplotlib.gridspec`, a new module for doing complex subplot layouts, featuring row and column spans and -more. See :doc:`/tutorials/intermediate/gridspec` for a tutorial overview. +more. See :doc:`/tutorials/intermediate/arranging_axes` for a tutorial +overview. .. figure:: ../../gallery/userdemo/images/sphx_glr_demo_gridspec01_001.png :target: ../../gallery/userdemo/demo_gridspec01.html diff --git a/examples/lines_bars_and_markers/scatter_hist.py b/examples/lines_bars_and_markers/scatter_hist.py index 1e18932a76db..29b2f96947a4 100644 --- a/examples/lines_bars_and_markers/scatter_hist.py +++ b/examples/lines_bars_and_markers/scatter_hist.py @@ -60,7 +60,7 @@ def scatter_hist(x, y, ax, ax_histx, ax_histy): # -------------------------------------------- # # We define a gridspec with unequal width- and height-ratios to achieve desired -# layout. Also see the :doc:`/tutorials/intermediate/gridspec` tutorial. +# layout. Also see the :doc:`/tutorials/intermediate/arranging_axes` tutorial. # Start with a square Figure. fig = plt.figure(figsize=(6, 6)) diff --git a/examples/subplots_axes_and_figures/gridspec_and_subplots.py b/examples/subplots_axes_and_figures/gridspec_and_subplots.py index fbb7ebd013a0..133fd37b5d71 100644 --- a/examples/subplots_axes_and_figures/gridspec_and_subplots.py +++ b/examples/subplots_axes_and_figures/gridspec_and_subplots.py @@ -8,7 +8,10 @@ and then remove the covered axes and fill the gap with a new bigger axes. Here we create a layout with the bottom two axes in the last column combined. -See also :doc:`/tutorials/intermediate/gridspec`. +To start with this layout (rather than removing the overlapping axes) use +`~.pyplot.subplot_mosaic`. + +See also :doc:`/tutorials/intermediate/arranging_axes`. """ import matplotlib.pyplot as plt diff --git a/lib/matplotlib/gridspec.py b/lib/matplotlib/gridspec.py index 6cce5d1e3e0e..980be6a25e45 100644 --- a/lib/matplotlib/gridspec.py +++ b/lib/matplotlib/gridspec.py @@ -5,8 +5,10 @@ The `GridSpec` specifies the overall grid structure. Individual cells within the grid are referenced by `SubplotSpec`\s. -See the tutorial :doc:`/tutorials/intermediate/gridspec` for a comprehensive -usage guide. +Often, users need not access this module directly, and can use higher-level +methods like `~.pyplot.subplots`, `~.pyplot.subplot_mosaic` and +`~.Figure.subfigures`. See the tutorial +:doc:`/tutorials/intermediate/arranging_axes` for a guide. """ import copy diff --git a/tutorials/intermediate/arranging_axes.py b/tutorials/intermediate/arranging_axes.py new file mode 100644 index 000000000000..a4f8850761d8 --- /dev/null +++ b/tutorials/intermediate/arranging_axes.py @@ -0,0 +1,382 @@ +""" +===================================== +Arranging multiple Axes in a Figure +===================================== + +Often more than one Axes is wanted on a figure at a time, usually +organized into a regular grid. Matplotlib has a variety of tools for +working with grids of Axes that have evolved over the history of the library. +Here we will discuss the tools we think users should use most often, the tools +that underpin how Axes are organized, and mention some of the older tools. + +.. note:: + + Matplotlib uses *Axes* to refer to the drawing area that contains + data, x- and y-axis, ticks, labels, title, etc. See _ for + more details. Another term that is often used is "subplot", which refers + to an Axes that is in a grid with other Axes objects. + +Overview +======== + +Create grid-shaped combinations of Axes +--------------------------------------- + +`~matplotlib.pyplot.subplots` + The primary function used to create figures and a grid of Axes. It + creates and places all Axes on the figure at once, and returns an + object array with handles for the Axes in the grid. See + `.Figure.subplots`. + +or + +`~matplotlib.pyplot.subplot_mosaic` + A simple way to create figures and a grid of Axes, with the added + flexibility that Axes can also span rows or columns. The Axes + are returned in a labelled dictionary instead of an array. See also + `.Figure.subplot_mosaic` and :doc:`/tutorials/provisional/mosaic`. + +Sometimes it is natural to have more than one distinct group of Axes grids, +in which case Matplotlib has the concept of `~.figure.SubFigure`: + +`~matplotlib.figure.SubFigure` + A virtual figure within a figure. + +Underlying tools +---------------- + +Underlying these are the concept of a `~.gridspec.GridSpec` and +a `~.SubplotSpec`: + +`~matplotlib.gridspec.GridSpec` + Specifies the geometry of the grid that a subplot will be + placed. The number of rows and number of columns of the grid + need to be set. Optionally, the subplot layout parameters + (e.g., left, right, etc.) can be tuned. + +`~matplotlib.gridspec.SubplotSpec` + Specifies the location of the subplot in the given `.GridSpec`. + +Adding single Axes at a time +---------------------------- + +The above functions create all Axes in a single function call. It is also +possible to add Axes one at a time, and this was originally how Matplotlib +used to work. Doing so is generally less elegant and flexible, though +sometimes useful for interactive work or to place an Axes in a custom +location: + +`~matplotlib.figure.Figure.add_axes` + Adds a single axes at a location specified by + ``[left, bottom, width, height]`` in fractions of figure width or height. + +`~matplotlib.pyplot.subplot` or `.Figure.add_subplot` + Adds a single subplot on a figure, with 1-based indexing (inherited from + Matlab). Columns and rows can be spanned by specifying a range of grid + cells. + +`~matplotlib.pyplot.subplot2grid` + Similar to `.pyplot.subplot`, but uses 0-based indexing and two-d python + slicing to choose cells. + +.. redirect-from:: /tutorials/intermediate/gridspec + +""" +############################################################################ +# High-level methods for making grids +# =================================== +# +# Basic 2x2 grid +# -------------- +# +# We can create a basic 2-by-2 grid of Axes using +# `~matplotlib.pyplot.subplots`. It returns a `~matplotlib.figure.Figure` +# instance and an array of `~matplotlib.axes.Axes` objects. The Axes +# objects can be used to access methods to place artists on the Axes; here +# we use `~.Axes.annotate`, but other examples could be `~.Axes.plot`, +# `~.Axes.pcolormesh`, etc. + +import matplotlib.pyplot as plt +import numpy as np + +fig, axs = plt.subplots(ncols=2, nrows=2, figsize=(5.5, 3.5), + constrained_layout=True) +# add an artist, in this case a nice label in the middle... +for row in range(2): + for col in range(2): + axs[row, col].annotate(f'axs[{row}, {col}]', (0.5, 0.5), + transform=axs[row, col].transAxes, + ha='center', va='center', fontsize=18, + color='darkgrey') +fig.suptitle('plt.subplots()') + +############################################################################## +# We will annotate a lot of Axes, so lets encapsulate the annotation, rather +# than having that large piece of annotation code every time we need it: + + +def annotate_axes(ax, text, fontsize=18): + ax.text(0.5, 0.5, text, transform=ax.transAxes, + ha="center", va="center", fontsize=fontsize, color="darkgrey") + + +############################################################################## +# The same effect can be achieved with `~.pyplot.subplot_mosaic`, +# but the return type is a dictionary instead of an array, where the user +# can give the keys useful meanings. Here we provide two lists, each list +# representing a row, and each element in the list a key representing the +# column. + +fig, axd = plt.subplot_mosaic([['upper left', 'upper right'], + ['lower left', 'lower right']], + figsize=(5.5, 3.5), constrained_layout=True) +for k in axd: + annotate_axes(axd[k], f'axd["{k}"]', fontsize=14) +fig.suptitle('plt.subplot_mosaic()') + +############################################################################ +# Axes spanning rows or columns in a grid +# --------------------------------------- +# +# Sometimes we want Axes to span rows or columns of the grid. +# There are actually multiple ways to accomplish this, but the most +# convenient is probably to use `~.pyplot.subplot_mosaic` by repeating one +# of the keys: + +fig, axd = plt.subplot_mosaic([['upper left', 'right'], + ['lower left', 'right']], + figsize=(5.5, 3.5), constrained_layout=True) +for k in axd: + annotate_axes(axd[k], f'axd["{k}"]', fontsize=14) +fig.suptitle('plt.subplot_mosaic()') + +############################################################################ +# See below for the description of how to do the same thing using +# `~matplotlib.gridspec.GridSpec` or `~matplotlib.pyplot.subplot2grid`. +# +# Variable widths or heights in a grid +# ------------------------------------ +# +# Both `~.pyplot.subplots` and `~.pyplot.subplot_mosaic` allow the rows +# in the grid to be different heights, and the columns to be different +# widths using the *gridspec_kw* keyword argument. +# Spacing parameters accepted by `~matplotlib.gridspec.GridSpec` +# can be passed to `~matplotlib.pyplot.subplots` and +# `~matplotlib.pyplot.subplot_mosaic`: + +gs_kw = dict(width_ratios=[1.4, 1], height_ratios=[1, 2]) +fig, axd = plt.subplot_mosaic([['upper left', 'right'], + ['lower left', 'right']], + gridspec_kw=gs_kw, figsize=(5.5, 3.5), + constrained_layout=True) +for k in axd: + annotate_axes(axd[k], f'axd["{k}"]', fontsize=14) +fig.suptitle('plt.subplot_mosaic()') + +############################################################################ +# Nested Axes layouts +# ------------------- +# +# Sometimes it is helpful to have two or more grids of Axes that +# may not need to be related to one another. The most simple way toin +# accomplish this is to use `.Figure.subfigures`. Note that the subfigure +# layouts are independent, so the Axes spines in each subfigure are not +# necessarily aligned. See below for a more verbose way to achieve the same +# effect with `~.gridspec.GridSpecFromSubplotSpec`. + +fig = plt.figure(constrained_layout=True) +subfigs = fig.subfigures(1, 2, wspace=0.07, width_ratios=[1.5, 1.]) +axs0 = subfigs[0].subplots(2, 2) +subfigs[0].set_facecolor('0.9') +subfigs[0].suptitle('subfigs[0]\nLeft side') +subfigs[0].supxlabel('xlabel for subfigs[0]') + +axs1 = subfigs[1].subplots(3, 1) +subfigs[1].suptitle('subfigs[1]') +subfigs[1].supylabel('ylabel for subfigs[1]') + +############################################################################ +# It is also possible to nest Axes using `~.pyplot.subplot_mosaic` using +# nested lists. This method does not use subfigures, like above, so lacks +# the ability to add per-subfigure ``suptitle`` and ``supxlabel``, etc. +# Rather it is a convenience wrapper around the `~.SubplotSpec.subgridspec` +# method described below. + +inner = [['innerA'], + ['innerB']] +outer = [['upper left', inner], + ['lower left', 'lower right']] + +fig, axd = plt.subplot_mosaic(outer, constrained_layout=True) +for k in axd: + annotate_axes(axd[k], f'axd["{k}"]') + +############################################################################ +# Low-level and advanced grid methods +# =================================== +# +# Internally, the arrangement of a grid of Axes is controlled by creating +# instances of `~.GridSpec` and `~.SubplotSpec`. *GridSpec* defines a +# (possibly non-uniform) grid of cells. Indexing into the *GridSpec* returns +# a SubplotSpec that covers one or more grid cells, and can be used to +# specify the location of an Axes. +# +# The following examples show how to use low-level methods to arrange Axes +# using *GridSpec* objects. +# +# Basic 2x2 grid +# -------------- +# +# We can accopmplish a 2x2 grid in the same manner as +# ``plt.subplots(2, 2)``: + +fig = plt.figure(figsize=(5.5, 3.5), constrained_layout=True) +spec = fig.add_gridspec(ncols=2, nrows=2) + +ax0 = fig.add_subplot(spec[0, 0]) +annotate_axes(ax0, 'ax0') + +ax1 = fig.add_subplot(spec[0, 1]) +annotate_axes(ax1, 'ax1') + +ax2 = fig.add_subplot(spec[1, 0]) +annotate_axes(ax2, 'ax2') + +ax3 = fig.add_subplot(spec[1, 1]) +annotate_axes(ax3, 'ax3') + +fig.suptitle('Manually added subplots using add_gridspec') + +############################################################################## +# Axes spanning rows or grids in a grid +# ------------------------------------- +# +# We can index the *spec* array using `NumPy slice syntax +# `_ +# and the new Axes will span the slice. This would be the same +# as ``fig, axd = plt.subplot_mosaic([['ax0', 'ax0'], ['ax1', 'ax2']], ...)``: + +fig = plt.figure(figsize=(5.5, 3.5), constrained_layout=True) +spec = fig.add_gridspec(2, 2) + +ax0 = fig.add_subplot(spec[0, :]) +annotate_axes(ax0, 'ax0') + +ax10 = fig.add_subplot(spec[1, 0]) +annotate_axes(ax10, 'ax10') + +ax11 = fig.add_subplot(spec[1, 1]) +annotate_axes(ax11, 'ax11') + +fig.suptitle('Manually added subplots, spanning a column') + +############################################################################### +# Manual 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` which both ignore *left* and *right* and adjust +# subplot sizes to fill the figure. Usually such manual placement +# requires iterations to make the Axes tick labels not overlap the Axes. +# +# These spacing parameters can also be passed to `~.pyplot.subplots` and +# `~.pyplot.subplot_mosaic` as the *gridspec_kw* argument. + +fig = plt.figure(constrained_layout=False, facecolor='0.9') +gs = fig.add_gridspec(nrows=3, ncols=3, left=0.05, right=0.75, + hspace=0.1, wspace=0.05) +ax0 = fig.add_subplot(gs[:-1, :]) +annotate_axes(ax0, 'ax0') +ax1 = fig.add_subplot(gs[-1, :-1]) +annotate_axes(ax1, 'ax1') +ax2 = fig.add_subplot(gs[-1, -1]) +annotate_axes(ax2, 'ax2') +fig.suptitle('Manual gridspec with right=0.75') + +############################################################################### +# Nested layouts with SubplotSpec +# ------------------------------- +# +# You can create nested layout similar to `~.Figure.subfigures` using +# `~.gridspec.SubplotSpec.subgridspec`. Here the Axes spines *are* +# aligned. +# +# Note this is also available from the more verbose +# `.gridspec.GridSpecFromSubplotSpec`. + +fig = plt.figure(constrained_layout=True) +gs0 = fig.add_gridspec(1, 2) + +gs00 = gs0[0].subgridspec(2, 2) +gs01 = gs0[1].subgridspec(3, 1) + +for a in range(2): + for b in range(2): + ax = fig.add_subplot(gs00[a, b]) + annotate_axes(ax, f'axLeft[{a}, {b}]', fontsize=10) + if a == 1 and b == 1: + ax.set_xlabel('xlabel') +for a in range(3): + ax = fig.add_subplot(gs01[a]) + annotate_axes(ax, f'axRight[{a}, {b}]') + if a == 2: + ax.set_ylabel('ylabel') + +fig.suptitle('nested gridspecs') + +############################################################################### +# Here's a more sophisticated example of nested *GridSpec*: We create an outer +# 4x4 grid with each cell containing an inner 3x3 grid of Axes. We outline +# the outer 4x4 grid by hiding appropriate spines in each of the inner 3x3 +# grids. + + +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) + +fig = plt.figure(figsize=(8, 8), constrained_layout=False) +outer_grid = fig.add_gridspec(4, 4, wspace=0, hspace=0) + +for a in range(4): + for b in range(4): + # gridspec inside gridspec + inner_grid = outer_grid[a, b].subgridspec(3, 3, wspace=0, hspace=0) + axs = inner_grid.subplots() # Create all subplots for the inner grid. + for (c, d), ax in np.ndenumerate(axs): + ax.plot(*squiggle_xy(a + 1, b + 1, c + 1, d + 1)) + ax.set(xticks=[], yticks=[]) + +# show only the outside spines +for ax in fig.get_axes(): + ss = ax.get_subplotspec() + ax.spines.top.set_visible(ss.is_first_row()) + ax.spines.bottom.set_visible(ss.is_last_row()) + ax.spines.left.set_visible(ss.is_first_col()) + ax.spines.right.set_visible(ss.is_last_col()) + +plt.show() + +############################################################################# +# +# More reading +# ============ +# +# - More details about :doc:`subplot mosaic `. +# - More details about :doc:`constrained layout +# `, used to align +# spacing in most of these examples. +# +# .. admonition:: References +# +# The use of the following functions, methods, classes and modules is shown +# in this example: +# +# - `matplotlib.pyplot.subplots` +# - `matplotlib.pyplot.subplot_mosaic` +# - `matplotlib.figure.Figure.add_gridspec` +# - `matplotlib.figure.Figure.add_subplot` +# - `matplotlib.gridspec.GridSpec` +# - `matplotlib.gridspec.SubplotSpec.subgridspec` +# - `matplotlib.gridspec.GridSpecFromSubplotSpec` diff --git a/tutorials/intermediate/gridspec.py b/tutorials/intermediate/gridspec.py deleted file mode 100644 index d9c5f10bd49e..000000000000 --- a/tutorials/intermediate/gridspec.py +++ /dev/null @@ -1,265 +0,0 @@ -""" -============================================================= -Customizing Figure Layouts Using GridSpec and Other Functions -============================================================= - -How to create grid-shaped combinations of axes. - -`~matplotlib.pyplot.subplots` - The primary function used to create figures and axes. It is similar to - `.pyplot.subplot`, but creates and places all axes on the figure at once. - See also `.Figure.subplots`. - -`~matplotlib.gridspec.GridSpec` - Specifies the geometry of the grid that a subplot will be - placed. The number of rows and number of columns of the grid - need to be set. Optionally, the subplot layout parameters - (e.g., left, right, etc.) can be tuned. - -`~matplotlib.gridspec.SubplotSpec` - Specifies the location of the subplot in the given `.GridSpec`. - -`~matplotlib.pyplot.subplot2grid` - A helper function that is similar to `.pyplot.subplot`, - but uses 0-based indexing and let subplot to occupy multiple cells. - This function is not covered in this tutorial. -""" - -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 :func:`~matplotlib.pyplot.subplots` and :mod:`~matplotlib.gridspec`. -# -# Using :func:`~matplotlib.pyplot.subplots` is quite simple. -# It returns a :class:`~matplotlib.figure.Figure` instance and an array of -# :class:`~matplotlib.axes.Axes` objects. - -fig1, f1_axes = plt.subplots(ncols=2, nrows=2, constrained_layout=True) - -############################################################################ -# For a simple use case such as this, :mod:`~matplotlib.gridspec` is -# perhaps overly verbose. -# You have to create the figure and :class:`~matplotlib.gridspec.GridSpec` -# instance separately, then pass elements of gridspec instance to the -# :func:`~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. - -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]) - -############################################################################# -# The power of gridspec comes in being able to create subplots that span -# rows and columns. Note the `NumPy slice syntax -# `_ -# for selecting the part of the gridspec each subplot will occupy. -# -# Note that we have also used the convenience method `.Figure.add_gridspec` -# instead of `.gridspec.GridSpec`, potentially saving the user an import, -# and keeping the namespace cleaner. - -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]') - -############################################################################# -# :mod:`~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. - -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) - -############################################################################ -# 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. - -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') - -############################################################################ -# Learning to use ``width_ratios`` and ``height_ratios`` is particularly -# useful since the top-level function :func:`~matplotlib.pyplot.subplots` -# accepts them within the ``gridspec_kw`` parameter. -# For that matter, any parameter accepted by -# :class:`~matplotlib.gridspec.GridSpec` can be passed to -# :func:`~matplotlib.pyplot.subplots` via the ``gridspec_kw`` parameter. -# This example recreates the previous figure without directly using a -# gridspec instance. - -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') - -############################################################################ -# The ``subplots`` and ``get_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. - -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() - -############################################################################### -# 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` which both adjust subplot sizes to fill the -# figure. - -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]) - -############################################################################### -# This is similar to :func:`~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: - -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]) - -############################################################################### -# GridSpec using SubplotSpec -# ========================== -# -# You can create GridSpec from the :class:`~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`. - -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]) - -############################################################################### -# 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. - -import numpy as np - - -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) -outer_grid = fig11.add_gridspec(4, 4, wspace=0, hspace=0) - -for a in range(4): - for b in range(4): - # gridspec inside gridspec - inner_grid = outer_grid[a, b].subgridspec(3, 3, wspace=0, hspace=0) - axs = inner_grid.subplots() # Create all subplots for the inner grid. - for (c, d), ax in np.ndenumerate(axs): - ax.plot(*squiggle_xy(a + 1, b + 1, c + 1, d + 1)) - ax.set(xticks=[], yticks=[]) - -# show only the outside spines -for ax in fig11.get_axes(): - ss = ax.get_subplotspec() - ax.spines.top.set_visible(ss.is_first_row()) - ax.spines.bottom.set_visible(ss.is_last_row()) - ax.spines.left.set_visible(ss.is_first_col()) - ax.spines.right.set_visible(ss.is_last_col()) - -plt.show() - -############################################################################# -# -# .. admonition:: References -# -# The use of the following functions, methods, classes and modules is shown -# in this example: -# -# - `matplotlib.pyplot.subplots` -# - `matplotlib.figure.Figure.add_gridspec` -# - `matplotlib.figure.Figure.add_subplot` -# - `matplotlib.gridspec.GridSpec` -# - `matplotlib.gridspec.SubplotSpec.subgridspec` -# - `matplotlib.gridspec.GridSpecFromSubplotSpec` diff --git a/tutorials/intermediate/tight_layout_guide.py b/tutorials/intermediate/tight_layout_guide.py index 4bc92b49c122..976fd511a516 100644 --- a/tutorials/intermediate/tight_layout_guide.py +++ b/tutorials/intermediate/tight_layout_guide.py @@ -117,7 +117,7 @@ def example_plot(ax, fontsize=12): ############################################################################### # It works with subplots created with # :func:`~matplotlib.pyplot.subplot2grid`. In general, subplots created -# from the gridspec (:doc:`/tutorials/intermediate/gridspec`) will work. +# from the gridspec (:doc:`/tutorials/intermediate/arranging_axes`) will work. plt.close('all') fig = plt.figure() diff --git a/tutorials/provisional/mosaic.py b/tutorials/provisional/mosaic.py index 120e80d97d5d..9aa06c9520e1 100644 --- a/tutorials/provisional/mosaic.py +++ b/tutorials/provisional/mosaic.py @@ -14,7 +14,7 @@ and verbose. For dense, even grids we have `.Figure.subplots` but for more complex layouts, such as Axes that span multiple columns / rows of the layout or leave some areas of the Figure blank, you can use -`.gridspec.GridSpec` (see :doc:`/tutorials/intermediate/gridspec`) or +`.gridspec.GridSpec` (see :doc:`/tutorials/intermediate/arranging_axes`) or manually place your axes. `.Figure.subplot_mosaic` aims to provide an interface to visually lay out your axes (as either ASCII art or nested lists) to streamline this process.