Insets and panels

Panel axes

It is often useful to have narrow “panels” along the edge of a larger subplot for plotting secondary 1-dimensional datasets or summary statistics. In proplot, you can generate panels using the panel_axes command (or its shorthand, panel). The panel location is specified with a string, e.g. ax.panel('r') or ax.panel('right') for a right-hand side panel, and the resulting panels are instances of CartesianAxes. By default, the panel shares its axis limits, axis labels, tick positions, and tick labels with the main subplot, but this can be disabled by passing share=False. To generate “stacked” panels, call panel_axes more than once. To generate several panels at once, call panel_axes on the SubplotGrid returned by subplots.

In the first example below, the distances are automatically adjusted by the tight layout algorithm according to the pad keyword (the default is rc['subplots.panelpad'] – this can be changed for an entire figure by passing panelpad to Figure). In the second example, the tight layout algorithm is overriden by manually setting the space to 0. Panel widths are specified in physical units, with the default controlled by rc['subplots.panelwidth']. This helps preserve the look of the figure if the figure size changes. Note that by default, panels are excluded when centering spanning axis labels and super titles – to include the panels, pass includepanels=True to Figure.

Important

Proplot adds panel axes by allocating new rows and columns in the GridSpec rather than “stealing” space from the parent subplot (note that subsequently indexing the GridSpec will ignore the slots allocated for panels). This approach means that panels do not affect subplot aspect ratios and do not affect subplot spacing, which lets proplot avoid relying on complicated “constrained layout” algorithms and tends to improve the appearance of figures with even the most complex arrangements of subplots and panels.

[1]:
import proplot as pplt

# Demonstrate that complex arrangements preserve
# spacing, aspect ratios, and axis sharing
gs = pplt.GridSpec(nrows=2, ncols=2)
fig = pplt.figure(refwidth=1.5, share=False)
for ss, side in zip(gs, 'tlbr'):
    ax = fig.add_subplot(ss)
    px = ax.panel_axes(side, width='3em')
fig.format(
    xlim=(0, 1), ylim=(0, 1),
    xlabel='xlabel', ylabel='ylabel',
    xticks=0.2, yticks=0.2,
    title='Title', suptitle='Complex arrangement of panels',
    toplabels=('Column 1', 'Column 2'),
    abc=True, abcloc='ul', titleloc='uc', titleabove=False,
)
_images/insets_panels_2_0.svg
[2]:
import proplot as pplt
import numpy as np
state = np.random.RandomState(51423)
data = (state.rand(20, 20) - 0.48).cumsum(axis=1).cumsum(axis=0)
data = 10 * (data - data.min()) / (data.max() - data.min())

# Stacked panels with outer colorbars
for cbarloc, ploc in ('rb', 'br'):
    # Create figure
    fig, axs = pplt.subplots(
        nrows=1, ncols=2, refwidth=1.8, panelpad=0.8,
        share=False, includepanels=True
    )
    axs.format(
        xlabel='xlabel', ylabel='ylabel', title='Title',
        suptitle='Using panels for summary statistics',
    )

    # Plot 2D dataset
    for ax in axs:
        ax.contourf(
            data, cmap='glacial', extend='both',
            colorbar=cbarloc, colorbar_kw={'label': 'colorbar'},
        )

    # Get summary statistics and settings
    axis = int(ploc == 'r')  # dimension along which stats are taken
    x1 = x2 = np.arange(20)
    y1 = data.mean(axis=axis)
    y2 = data.std(axis=axis)
    titleloc = 'upper center'
    if ploc == 'r':
        titleloc = 'center'
        x1, x2, y1, y2 = y1, y2, x1, x2

    # Panels for plotting the mean. Note SubplotGrid.panel() returns a SubplotGrid
    # of panel axes. We use this to call format() for all the panels at once.
    space = 0
    width = '4em'
    kwargs = {'titleloc': titleloc, 'xreverse': False, 'yreverse': False}
    pxs = axs.panel(ploc, space=space, width=width)
    pxs.format(title='Mean', **kwargs)
    for px in pxs:
        px.plot(x1, y1, color='gray7')

    # Panels for plotting the standard deviation
    pxs = axs.panel(ploc, space=space, width=width)
    pxs.format(title='Stdev', **kwargs)
    for px in pxs:
        px.plot(x2, y2, color='gray7', ls='--')
_images/insets_panels_3_0.svg
_images/insets_panels_3_1.svg

Inset axes

Inset axes can be generated with the inset_axes command (or its shorthand, inset). To generate several insets at once, call inset_axes on the SubplotGrid returned by subplots. By default, inset axes have the same projection as the parent axes, but you can also request a different projection (e.g., ax.inset_axes(bounds, proj='polar')). When the axes are both CartesianAxes, you can pass zoom=True to inset_axes to quickly add a “zoom indication” box and lines (this uses indicate_inset_zoom internally). The box and line positions automatically follow the axis limits of the inset axes and parent axes. To modify the zoom line properties, you can pass a dictionary to zoom_kw.

[3]:
import proplot as pplt
import numpy as np

# Sample data
N = 20
state = np.random.RandomState(51423)
x, y = np.arange(10), np.arange(10)
data = state.rand(10, 10).cumsum(axis=0)
data = np.flip(data, (0, 1))

# Plot data in the main axes
fig, ax = pplt.subplots(refwidth=3)
m = ax.pcolormesh(data, cmap='Grays', levels=N)
ax.colorbar(m, loc='b', label='label')
ax.format(
    xlabel='xlabel', ylabel='ylabel',
    suptitle='"Zooming in" with an inset axes'
)

# Create an inset axes representing a "zoom-in"
# See the 1D plotting section for more on the "inbounds" keyword
ix = ax.inset(
    [5, 5, 4, 4], transform='data', zoom=True,
    zoom_kw={'ec': 'blush', 'ls': '--', 'lw': 2}
)
ix.format(
    xlim=(2, 4), ylim=(2, 4), color='red8',
    linewidth=1.5, ticklabelweight='bold'
)
ix.pcolormesh(data, cmap='Grays', levels=N, inbounds=False)
[3]:
<matplotlib.collections.QuadMesh at 0x7fd17a847340>
_images/insets_panels_5_1.svg