# Subplots¶

This section documents a variety of features related to proplot subplots, including a-b-c subplot labels, axis sharing between subplots, automatic spacing between subplots, and a unique feature where the figure size is automatically determined from a reference subplot and the subplot geometry.

Important

Proplot does not officially support “nested” matplotlib structures like SubFigure and GridSpecFromSubplotSpec. Also, proplot subplots are only allowed to use one gridspec per figure (see this section). These restrictions considerably simplify the algorithm used to calculate figure sizes and subplot spaces and makes the default a-b-c label assignment and axis sharing between subplots less ambiguous. Some features associated with “nested” matplotlib structures can be reproduced with proplot, including different spaces between different subplot rows and columns and different formatting for groups of subplots. “Highlighting” behind groups of subplots (analogous to different subfigure background colors) will also be supported in the future.

## A-b-c labels¶

Proplot can quickly add “a-b-c” labels to subplots. This is possible because we assign a number to each subplot.

If you add subplots one-by-one with add_subplot, you can manually specify the number with the number keyword. By default, the subplot number is incremented by 1 each time you call add_subplot. If you draw all of your subplots at once with add_subplots, the numbers depend on the input arguments. If you passed an array, the subplot numbers correspond to the numbers in the array. But if you used the ncols and nrows keyword arguments, the number order is row-major by default and can be switched to column-major by passing order='F' (note the number order also determines the list order in the SubplotGrid returned by add_subplots).

To turn on “a-b-c” labels, set rc.abc to True or pass abc=True to format (see the format command for details). To change the label style, set rc.abc to e.g. 'A.' or pass e.g. abc='A.' to format. You can also modify the “a-b-c” label location, weight, and size with the rc['abc.loc'], rc['abc.weight'], and rc['abc.size'] settings. Also note that if the an “a-b-c” label and title are in the same position, they are automatically offset away from each other.

Note

“Inner” a-b-c labels and titles are surrounded with a white border when rc['abc.border'] and rc['title.border'] are True (the default). White boxes can be used instead by setting rc['abc.bbox'] and rc['title.bbox'] to True. These options help labels stand out against plotted content. Any text can be given “borders” or “boxes” by passing border=True or bbox=True to proplot.axes.Axes.text.

[1]:

import proplot as pplt
fig = pplt.figure(space=0, refwidth='10em')
axs = fig.subplots(nrows=3, ncols=3)
axs.format(
abc='A.', abcloc='ul',
xticks='null', yticks='null', facecolor='gray5',
xlabel='x axis', ylabel='y axis',
suptitle='A-b-c label offsetting, borders, and boxes',
)
axs[:3].format(abcloc='l', titleloc='l', title='Title')
axs[-3:].format(abcbbox=True)  # also disables abcborder
# axs[:-3].format(abcborder=True)  # this is already the default

[2]:

import proplot as pplt
fig = pplt.figure(space=0, refwidth=0.7)
axs = fig.subplots(nrows=8, ncols=8)
axs.format(
abc=True, abcloc='ur',
xlabel='x axis', ylabel='y axis', xticks=[], yticks=[],
suptitle='A-b-c label stress test'
)


## Figure sizes¶

Depending on the keyword arguments passed to Figure, proplot figure sizes may be flexible. By default, the figure size is calculated automatically from the gridspec geometry and the physical size of a “reference” subplot. This subplot has a number matching the Figure keyword refnum (the default value 1 usually corresponds to the subplot in the upper-left corner – see this section for more on subplot numbers). Alternatively, the figure height (width) may be calculated automatically from the gridspec geometry and a user-input figure width (height).

The figure size ultimately depends on the following Figure keyword arguments:

• refwidth and refheight set the physical dimensions of the reference subplot (default is rc['subplots.refwidth'] = 2.5). If one is specified, the other is calculated to satisfy the reference subplot aspect ratio refaspect (default is 1). If both are specified, refaspect is ignored.

• figwidth and figheight set the physical dimensions of the figure. If one is specified, the other is calculated to satisfy refaspect. If both are specified, or if the figsize argument is specified, the figure size is fixed and refaspect is ignored.

• journal sets the physical dimensions of the figure to meet requirements for submission to an academic journal. For example, journal='nat1' results in a width suitable for single-column Nature figures. See this table for the currently available journal specifications.

The below examples show how these keyword arguments affect the figure size.

Important

Proplot’s figure size algorithm has the following important properties:

[3]:

import proplot as pplt
import numpy as np

# Grid of images (note the square pixels)
state = np.random.RandomState(51423)
colors = np.tile(state.rand(8, 12, 1), (1, 1, 3))
fig, axs = pplt.subplots(ncols=3, nrows=2, refwidth=1.7)
fig.format(suptitle='Auto figure size for grid of images')
for ax in axs:
ax.imshow(colors)

# Grid of cartopy projections
fig, axs = pplt.subplots(ncols=2, nrows=3, proj='robin')
axs.format(land=True, landcolor='k')
fig.format(suptitle='Auto figure size for grid of cartopy projections')

[4]:

import proplot as pplt
pplt.rc.update(grid=False, titleloc='uc', titleweight='bold', titlecolor='red9')

# Change the reference subplot width
suptitle = 'Effect of subplot width on figure size'
for refwidth in ('3cm', '5cm'):
fig, axs = pplt.subplots(ncols=2, refwidth=refwidth,)
axs[0].format(title=f'refwidth = {refwidth}', suptitle=suptitle)

# Change the reference subplot aspect ratio
suptitle = 'Effect of subplot aspect ratio on figure size'
for refaspect in (1, 2):
fig, axs = pplt.subplots(ncols=2, refwidth=1.6, refaspect=refaspect)
axs[0].format(title=f'refaspect = {refaspect}', suptitle=suptitle)

# Change the reference subplot
suptitle = 'Effect of reference subplot on figure size'
for ref in (1, 2):  # with different width ratios
fig, axs = pplt.subplots(ncols=3, wratios=(3, 2, 2), ref=ref, refwidth=1.1)
axs[ref - 1].format(title='reference', suptitle=suptitle)
for ref in (1, 2):  # with complex subplot grid
fig, axs = pplt.subplots([[1, 2], [1, 3]], refnum=ref, refwidth=1.8)
axs[ref - 1].format(title='reference', suptitle=suptitle)

pplt.rc.reset()


## Subplot spaces¶

Depending on the keyword arguments passed to Figure and GridSpec, the spaces between proplot subplots may be flexible. By default, the spaces are calculated automatically to accomadate text labels using a custom “tight layout” algorithm. This algorithm can be disabled by passing tight=False to Figure or by setting rc['subplots.tight'] to False. In contrast to matplotlib’s tight layout algorithm, proplot’s algorithm may change the figure size and permits variable spacing between each subplot row and column (see proplot.gridspec.GridSpec for details).

The tight layout algorithm can also be completely or partly overridden. When you pass any of the spacing arguments left, right, top, bottom, wspace, or hspace to Figure or GridSpec, that value is always respected. For example:

• left=2 fixes the left margin at 2 em-widths, while the right, bottom, and top margin widths are determined by the tight layout algorithm.

• wspace=1 fixes the spaces between subplot columns at 1 em-width, while the spaces between subplot rows are determined by the tight layout algorithm.

• wspace=(3, None) fixes the space between the first two columns of a three-column plot at 3 em-widths, while the space between the second two columns is determined by the tight layout algorithm.

Alternatively, the padding used by the tight layout algorithm (rather than the absolute spaces between subplot edges) can be changed by passing outerpad, innerpad, or panelpad to Figure or GridSpec. This padding can be set locally by passing an array of values to wpad and hpad (analogous to wspace and hspace), or by passing the pad keyword when creating panel axes or outer colorbars or legends (analogous to space). Finally, passing wequal=True, hequal=True, or equal=True constrains the tight layout algorithm to produce equal spacing between main subplot rows or columns (note that equal spacing is the default behavior when tight layout is disabled).

All the spacing arguments described above can be specified with a unit string interpreted by units. The default unit assumed for numeric arguments is an “em-width” (i.e., a rc['font.size'] width – see the units table for details).

[5]:

import proplot as pplt

# Stress test of the tight layout algorithm
# Add large labels along the edge of one subplot
for equal, descrip in enumerate(('variable', 'equal')):
fig, axs = pplt.subplots(
nrows=3, ncols=3, refwidth=1.1, share=False, equal=bool(equal)
)
axs[1].format(
xlabel='xlabel\nxlabel',
ylabel='ylabel\nylabel\nylabel\nylabel'
)
axs.format(
grid=False,
toplabels=('Column 1', 'Column 2', 'Column 3'),
leftlabels=('Row 1', 'Row 2', 'Row 3'),
suptitle=f'Tight layout with {descrip} row-column spacing',
)

[6]:

import proplot as pplt

# Stress test of the tight layout algorithm
# This time override the algorithm between selected subplot rows/columns
fig, axs = pplt.subplots(
ncols=4, nrows=3, refwidth=1.1, span=False,
bottom='5em', right='5em',  # margin spacing overrides
wspace=(0, 0, None), hspace=(0, None),  # column and row spacing overrides
)
axs.format(
grid=False,
xlocator=1, ylocator=1, tickdir='inout',
xlim=(-1.5, 1.5), ylim=(-1.5, 1.5),
suptitle='Tight layout with user overrides',
toplabels=('Column 1', 'Column 2', 'Column 3', 'Column 4'),
leftlabels=('Row 1', 'Row 2', 'Row 3'),
)
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(xlabel='xlabel', title='Title\nTitle\nTitle')
axs[:, 0].format(ylabel='ylabel')


## Axis sharing¶

Figures with lots of subplots often have redundant labels. To help address this, the matplotlib command matplotlib.pyplot.subplots includes sharex and sharey keywords that permit sharing axis limits and ticks between like rows and columns of subplots. Proplot builds on this feature by…

1. Automatically sharing axes between subplots and panels occupying the same rows or columns of the GridSpec. This works for aribtrarily complex subplot grids. It also works if subplots were generated one-by-one with add_subplot rather than subplots. It is controlled by the sharex and sharey Figure keywords (default is rc['subplots.share'] = True). You can use the share keyword as a shorthand to set both sharex and sharey.

2. Automatically sharing labels across subplots and panels with edges against the same row or column of the GridSpec. This also works for complex grids and subplots generated one-by-one. It is controlled by the spanx and spany Figure keywords (default is rc['subplots.span'] = True). Use the span keyword as a shorthand to set both spanx and spany. Note that unlike supxlabel and supylabel, these labels are aligned between gridspec edges rather than figure edges.

3. Supporting five sharing “levels”. These values can be passed to sharex, sharey, or share, or assigned to rc['subplots.share']. The levels are defined as follows:

• False or 0: Axis sharing is disabled.

• 'labels', 'labs', or 1: Axis labels are shared, but nothing else. Labels will appear on the leftmost and bottommost subplots.

• 'limits', 'lims', or 2: Same as 1, but axis limits, axis scales, and major and minor tick locations and formatting are also shared.

• True or 3 (default): Same as 2, but axis tick labels are also shared. Tick labels will appear on the leftmost and bottommost subplots.

• 'all' or 4: Same as 3, but axis limits, axis scales, and axis ticks are shared even between subplots not in the same row or column.

The below examples demonstrate the effect of various axis and label sharing settings on the appearance of several subplot grids.

[7]:

import proplot as pplt
import numpy as np
N = 50
M = 40
state = np.random.RandomState(51423)
cycle = pplt.Cycle('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)

# Plots with different sharing and spanning settings
# Note that span=True and share=True are the defaults
spans = (False, False, True, True)
shares = (False, 'labels', 'limits', True)
for i, (span, share) in enumerate(zip(spans, shares)):
fig = pplt.figure(refaspect=1, refwidth=1.06, spanx=span, sharey=share)
axs = fig.subplots(ncols=4)
for ax, data in zip(axs, datas):
on = ('off', 'on')[int(span)]
ax.plot(data, cycle=cycle)
ax.format(
grid=False, xlabel='spanning axis', ylabel='shared axis',
suptitle=f'Sharing mode {share!r} (level {i}) with spanning labels {on}'
)

[8]:

import proplot as pplt
import numpy as np
state = np.random.RandomState(51423)

# Plots with minimum and maximum sharing settings
# Note that all x and y axis limits and ticks are identical
spans = (False, True)
shares = (False, 'all')
titles = ('Minimum sharing', 'Maximum sharing')
for span, share, title in zip(spans, shares, titles):
fig = pplt.figure(refwidth=1, span=span, share=share)
axs = fig.subplots(nrows=4, ncols=4)
for ax in axs:
data = (state.rand(100, 20) - 0.4).cumsum(axis=0)
ax.plot(data, cycle='Set3')
axs.format(
abc=True, abcloc='ul', suptitle=title,
xlabel='xlabel', ylabel='ylabel',
grid=False, xticks=25, yticks=5
)


## Physical units¶

Proplot supports arbitrary physical units for controlling the figure figwidth and figheight; the reference subplot refwidth and refheight; the gridspec spacing and tight layout padding keywords left, right, bottom, top, wspace, hspace, outerpad, innerpad, panelpad, wpad, and hpad; the colorbar and panel widths; various legend spacing and padding arguments; various format font size and padding arguments; the line width and marker size arguments passed to PlotAxes commands; and all applicable rc settings. This feature is powered by the units function.

A table of acceptable physical units is found here. They include centimeters, millimeters, pixels, em-heights, en-heights, and points. The default physical unit (assumed when an argument is numeric) depends on the context. For subplot and figure sizes, it is inches. For gridspec and legend spaces, it is em-widths. For font sizes, text padding, and line widths, it is points. See the relevant documentation in the API reference for details.

[9]:

import proplot as pplt
import numpy as np
with pplt.rc.context(fontsize='12px'):  # depends on rc['figure.dpi']
fig, axs = pplt.subplots(
ncols=3, figwidth='15cm', figheight='3in',
wspace=('10pt', '20pt'), right='10mm',
)
cb = fig.colorbar(
'Mono', loc='b', extend='both', label='colorbar',
width='2em', extendsize='3em', shrink=0.8,
)
pax = axs[2].panel_axes('r', width='5en')
axs.format(
suptitle='Arguments with arbitrary units',
xlabel='x axis', ylabel='y axis',
)