Subplots features

Automatic figure sizing

By default, ProPlot automatically determines the suitable figure size given the geometry of your subplot grid. The figure size is constrained by the physical size of a reference subplot. This algorithm is controlled by a variety of subplots keyword arguments:

  • ref sets the reference subplot number (default is 1, i.e. the subplot in the upper left corner).

  • aspect sets the reference subplot aspect ratio (default is 1). You can also use matplotlib’s set_aspect.

  • axwidth and axheight set the physical dimensions of the reference subplot (default is axwidth=2). If one is specified, the other is calculated to satisfy aspect. If both are specified, aspect is ignored. The physical dimensions of the figure are determined automatically.

  • width and height set the physical dimensions of the figure. If one is specified, the other is calculated to satisfy aspect and the subplot spacing. If both are specified (or if the matplotlib figsize parameter is specified), aspect is ignored.

This algorithm also includes the following notable features:

  • For very simple subplot grids (e.g. plot.subplots(ncols=2, nrows=3)), aspect, axwidth, and axheight apply to every subplot in the figure – not just the reference subplot.

  • When the reference subplot aspect ratio has been manually overridden (e.g. with ax.set_aspect(1)) or is set to 'equal' (as with map projections and imshow images), the aspect parameter is ignored.

  • When colorbars and panels are present in the figure, their physical widths are preserved during figure resizing. ProPlot specifies their widths in physical units to help avoid colorbars that look “too skinny” or “too fat”.

The below examples demonstrate the default behavior of the automatic figure sizing algorithm, and how it can be controlled with subplots keyword arguments.

[1]:
import proplot as plot
import numpy as np
# Cartopy projections
f, axs = plot.subplots(ncols=2, nrows=3, proj='robin')
axs.format(
    land=True, landcolor='k',
    suptitle='Auto figure sizing with grid of cartopy projections'
)

# Images
state = np.random.RandomState(51423)
f, axs = plot.subplots(ncols=2, nrows=3)
colors = state.rand(10, 20, 3).cumsum(axis=2)
colors /= colors.max()
axs.imshow(colors)
axs.format(
    land=True, landcolor='k',
    suptitle='Auto figure sizing with grid of images'
)
/home/docs/checkouts/readthedocs.org/user_builds/proplot/conda/v0.5.0/lib/python3.7/site-packages/proplot/utils.py:105: ProPlotWarning: Rebuilding font cache.
_images/subplots_3_1.svg
_images/subplots_3_2.svg
[2]:
import proplot as plot
# Loop through different axes widths
suptitle = 'Effect of subplot properties on figure size'
for axwidth in ('4cm', '6cm'):
    f, axs = plot.subplots(ncols=2, axwidth=axwidth,)
    axs[0].format(
        suptitle=suptitle,
        title=f'axwidth = {axwidth}', titleweight='bold',
        titleloc='uc', titlecolor='red9',
    )

# Loop through different aspect ratios
for aspect in (1, (3,2)):
    f, axs = plot.subplots(ncols=2, nrows=2, axwidth=1.6, aspect=aspect)
    axs[0].format(
        suptitle=suptitle,
        title=f'aspect = {aspect}', titleweight='bold',
        titleloc='uc', titlecolor='red9',
    )
_images/subplots_4_0.svg
_images/subplots_4_1.svg
_images/subplots_4_2.svg
_images/subplots_4_3.svg
[3]:
import proplot as plot
# Changing the reference subplot in the presence of unequal width/height ratios
suptitle = 'Effect of reference subplot on figure size'
for ref in (1, 2):
    f, axs = plot.subplots(
        ref=ref, nrows=3, ncols=3, wratios=(3, 2, 2),
        axwidth=1.1,
    )
    axs[ref-1].format(
        suptitle=suptitle,
        title='reference axes', titleweight='bold',
        titleloc='uc', titlecolor='red9'
    )

# Changing the reference subplot in a complex grid
for ref in (3, 2):
    f, axs = plot.subplots(
        [[1, 1, 2], [3, 4, 4]],
        hratios=(1, 1.5), wratios=(3, 2, 2),
        ref=ref, axwidth=1.1, span=False
    )
    axs[ref-1].format(
        suptitle=suptitle,
        title='reference axes', titleweight='bold',
        titleloc='uc', titlecolor='red9'
    )
_images/subplots_5_0.svg
_images/subplots_5_1.svg
_images/subplots_5_2.svg
_images/subplots_5_3.svg

Automatic subplot spacing

By default, ProPlot applies a tight layout algorithm to every figure. This algorithm automatically adjusts the space between subplot rows and columns and the figure edge to accomadate labels. It can be disabled by passing tight=False to subplots.

While matplotlib has its own tight layout algorithm, ProPlot’s algorithm permits variable spacing between subsequent subplot rows and columns (see the new GridSpec class) and may change the figure size (depending on the keyword arguments passed to subplots).

ProPlot’s tight layout algorithm can also be easily overridden. When you pass a spacing argument like left, right, top, bottom, wspace, or hspace to subplots, that value is always respected:

  • With left='2em', the left margin is fixed but the right, bottom, and top margins are calculated automatically.

  • With wspace=('3em', None) (and ncols=3), the space between the first two columns is fixed, while the space between the second two columns is calculated automatically.

The below examples demonstrate how the tight layout algorithm permits variable spacing between subplot rows and columns.

[4]:
import proplot as plot
f, axs = plot.subplots(
    ref=ref, nrows=3, ncols=3, axwidth=1.1, share=0
)
axs[ref-1].format(
    title='reference axes', titleweight='bold',
    titleloc='uc', titlecolor='red9'
)
axs[4].format(
    title='title\ntitle\ntitle',
    suptitle='Tight layout with variable row-column spacing'
)
axs[1].format(ylabel='ylabel\nylabel\nylabel')
axs[:4:2].format(xlabel='xlabel\nxlabel\nxlabel')
axs.format(
    rowlabels=['Row 1', 'Row 2', 'Row 3'],
    collabels=['Column 1', 'Column 2', 'Column 3']
)
_images/subplots_8_0.svg
[5]:
import proplot as plot
f, axs = plot.subplots(
    ncols=4, nrows=3, wspace=(0, 0, None), hspace=(0, None),
    bottom='5em', right='5em', span=False,
    axwidth=1.1,
)
axs.format(
    suptitle='Tight layout with user overrides',
    rowlabels=['Row 1', 'Row 2', 'Row 3'],
    collabels=['Column 1', 'Column 2', 'Column 3', 'Column 4']
)
axs[0, :].format(xtickloc='top')
axs[2, :].format(xtickloc='both')
axs[:, 1].format(ytickloc='neither')
axs[:, 2].format(ytickloc='right')
axs[:, 3].format(ytickloc='both')
axs[-1, :].format(title='Title\nTitle\nTitle', xlabel='xlabel')
axs[:, 0].format(ylabel='ylabel\nylabel')
_images/subplots_9_0.svg

Arbitrary physical units

ProPlot supports arbitrary physical units for controlling the figure width and height; the reference subplot axwidth and axheight; the gridspec spacing values left, right, bottom, top, wspace, and hspace; and in a few other places, e.g. panel and colorbar widths.

If a sizing argument is numeric, the units are inches or points, and if string, the units are interpreted by units. A table of acceptable units is found in the units documentation. They include centimeters, millimeters, pixels, em-heights, and points.

[6]:
import proplot as plot
import numpy as np
with plot.rc.context(small='12px', large='15px', linewidth='0.5mm'):
    f, axs = plot.subplots(
        ncols=3, width='15cm', height='2.5in',
        wspace=('10pt', '20pt'), right='10mm'
    )
    panel = axs[2].panel_axes('r', width='2em')
axs.format(
    suptitle='Arguments with arbitrary units',
    xlabel='x axis', ylabel='y axis'
)
_images/subplots_12_0.svg

Subplot numbers and labels

ProPlot assigns unique numbers to subplots. The number order determines the order the subplots appear in the subplot_grid and the order of “a-b-c” labels generated by format. If you did not provide a 2D array, the number order is row-major by default but can be made column-major by passing order='F' to subplots. The “a-b-c” label position and style can be changed with format.

[7]:
import proplot as plot
f, axs = plot.subplots(nrows=8, ncols=8, axwidth=0.7, space=0)
axs.format(
    abc=True, abcloc='ur', xlabel='x axis', ylabel='y axis',
    xticks=[], yticks=[], suptitle='Subplot labels demo'
)
_images/subplots_15_0.svg

Shared and spanning labels

Matplotlib has an “axis sharing” feature that holds axis limits the same for axes within a grid of subplots. But this has no effect on the axis labels and tick labels, which can lead to lots of redundancies.

To help you eliminate these redundancies, ProPlot introduces four axis-sharing options and a new spanning label option, controlled by the share, sharex, sharey, span, spanx, and spany subplots keyword args. “Sharing level” 1 hides inner x and y axis labels. “Sharing level” 2 is the same as 1, but the x and y axis limits are locked. “Sharing level” 3 is the same as 2, but the x and y tick labels are hidden. “Spanning labels” are centered x and y axis labels used for subplots whose spines are on the same row or column. See the below example.

Note that the the “shared” and “spanning” axes are determined automatically based on the extent of each subplot in the GridSpec. Since ProPlot uses just one GridSpec per figure, this can be done with zero ambiguity.

[8]:
import proplot as plot
import numpy as np
N = 50
M = 40
state = np.random.RandomState(51423)
colors = plot.Colors('grays_r', M, left=0.1, right=0.8)
datas = []
for scale in (1, 3, 7, 0.2):
    data = scale * (state.rand(N, M) - 0.5).cumsum(axis=0)[N//2:, :]
    datas.append(data)
# Same plot with different sharing and spanning settings
for share in (0, 1, 2, 3):
    f, axs = plot.subplots(
        ncols=4, aspect=1, axwidth=1.2,
        sharey=share, spanx=share//2
    )
    for ax, data in zip(axs, datas):
        ax.plot(data, cycle=colors)
        ax.format(
            suptitle=f'Axis-sharing level: {share}, spanning labels {["off","on"][share//2]}',
            grid=False, xlabel='spanning', ylabel='shared'
        )
_images/subplots_18_0.svg
_images/subplots_18_1.svg
_images/subplots_18_2.svg
_images/subplots_18_3.svg
[9]:
import proplot as plot
import numpy as np
plot.rc.reset()
plot.rc.cycle = 'Set3'
state = np.random.RandomState(51423)
titles = ['With redundant labels', 'Without redundant labels']
# Same plot with and without default sharing settings
for mode in (0, 1):
    f, axs = plot.subplots(
        nrows=4, ncols=4, share=3*mode,
        span=1*mode, axwidth=1
    )
    for ax in axs:
        ax.plot((state.rand(100, 20) - 0.4).cumsum(axis=0))
    axs.format(
        xlabel='xlabel', ylabel='ylabel', suptitle=titles[mode],
        abc=True, abcloc='ul',
        grid=False, xticks=25, yticks=5
    )
_images/subplots_19_0.svg
_images/subplots_19_1.svg