The basics¶
Creating figures¶
Proplot works by subclassing
three fundamental matplotlib classes: proplot.figure.Figure
replaces
matplotlib.figure.Figure
, proplot.axes.Axes
replaces matplotlib.axes.Axes
,
and proplot.gridspec.GridSpec
replaces matplotlib.gridspec.GridSpec
(see this tutorial
for more on gridspecs).
To make plots with these classes, you must start with the top-level commands
figure
, subplot
, or subplots
. These are
modeled after the pyplot
commands of the same name. As in
pyplot
, subplot
creates a figure and a single
subplot, subplots
creates a figure and a grid of subplots, and
figure
creates an empty figure that can be subsequently filled
with subplots. A minimal example with just one subplot is shown below.
Note
Proplot changes the default rc['figure.facecolor']
so that the figure backgrounds shown by the matplotlib backend are light gray
(the rc['savefig.facecolor']
applied to saved figures is still white).
Proplot also controls the appearance of figures in Jupyter notebooks
using the new rc.inlineformat
setting, which is passed to
config_inline_backend
on import. This
imposes a higher-quality default “inline” format
and disables the backend-specific settings InlineBackend.rc
and
InlineBackend.print_figure_kwargs
, ensuring that the figures you save
look like the figures displayed by the backend.
Proplot also changes the default rc['savefig.format']
from PNG to PDF for the following reasons:
Vector graphic formats are infinitely scalable.
Vector graphic formats are preferred by academic journals.
Nearly all academic journals accept figures in the PDF format alongside the EPS format.
The EPS format is outdated and does not support transparent graphic elements.
In case you do need a raster format like PNG, proplot increases the
default rc['savefig.dpi']
to 1000 dots per inch, which is
recommended by most journals
as the minimum resolution for figures containing lines and text. See the
configuration section for how to change these settings.
[1]:
# Simple subplot
import numpy as np
import proplot as pplt
state = np.random.RandomState(51423)
data = 2 * (state.rand(100, 5) - 0.5).cumsum(axis=0)
fig, ax = pplt.subplot(suptitle='Single subplot', xlabel='x axis', ylabel='y axis')
# fig = pplt.figure(suptitle='Single subplot') # equivalent to above
# ax = fig.subplot(xlabel='x axis', ylabel='y axis')
ax.plot(data, lw=2)
[1]:
<a list of 5 Line2D objects>
Creating subplots¶
Similar to matplotlib, subplots can be added to figures one-by-one
or all at once. Each subplot will be an instance of
proplot.axes.Axes
. To add subplots all at once, use
proplot.figure.Figure.add_subplots
(or its shorthand,
proplot.figure.Figure.subplots
). Note that under the hood, the top-level
proplot command subplots
simply calls figure
followed by proplot.figure.Figure.add_subplots
.
With no arguments,
add_subplots
returns a subplot generated from a 1-row, 1-columnGridSpec
.With
ncols
ornrows
,add_subplots
returns a simple grid of subplots from aGridSpec
with matching geometry in either row-major or column-majororder
.With
array
,add_subplots
returns an arbitrarily complex grid of subplots from aGridSpec
with matching geometry. Herearray
is a 2D array representing a “picture” of the subplot layout, where each unique integer indicates aGridSpec
slot occupied by the corresponding subplot and0
indicates an empty space. The returned subplots are contained in aSubplotGrid
(see below for details).
To add subplots one-by-one, use the proplot.figure.Figure.add_subplot
command (or its shorthand proplot.figure.Figure.subplot
).
With no arguments,
add_subplot
returns a subplot generated from a 1-row, 1-columnGridSpec
.With integer arguments,
add_subplot
returns a subplot matching the correspondingGridSpec
geometry, as in matplotlib. Note that unlike matplotlib, the geometry must be compatible with the geometry implied by previousadd_subplot
calls.With a
SubplotSpec
generated by indexing aproplot.gridspec.GridSpec
,add_subplot
returns a subplot at the corresponding location. Note that unlike matplotlib, only onegridspec
can be used with each figure.
As in matplotlib, to save figures, use savefig
(or its
shorthand proplot.figure.Figure.save
). User paths in the filename are expanded
with os.path.expanduser
. In the following examples, we add subplots to figures
with a variety of methods and then save the results to the home directory.
Warning
Proplot employs automatic axis sharing by default. This lets
subplots in the same row or column share the same axis limits, scales, ticks,
and labels. This is often convenient, but may be annoying for some users. To
keep this feature turned off, simply change the default settings
with e.g. pplt.rc.update('subplots', share=False, span=False)
. See the
axis sharing section for details.
[2]:
# Simple subplot grid
import numpy as np
import proplot as pplt
state = np.random.RandomState(51423)
data = 2 * (state.rand(100, 5) - 0.5).cumsum(axis=0)
fig = pplt.figure()
ax = fig.subplot(121)
ax.plot(data, lw=2)
ax = fig.subplot(122)
fig.format(
suptitle='Simple subplot grid', title='Title',
xlabel='x axis', ylabel='y axis'
)
# fig.save('~/example1.png') # save the figure
# fig.savefig('~/example1.png') # alternative
[3]:
# Complex grid
import numpy as np
import proplot as pplt
state = np.random.RandomState(51423)
data = 2 * (state.rand(100, 5) - 0.5).cumsum(axis=0)
array = [ # the "picture" (0 == nothing, 1 == subplot A, 2 == subplot B, etc.)
[1, 1, 2, 2],
[0, 3, 3, 0],
]
fig = pplt.figure(refwidth=1.8)
axs = fig.subplots(array)
axs.format(
abc=True, abcloc='ul', suptitle='Complex subplot grid',
xlabel='xlabel', ylabel='ylabel'
)
axs[2].plot(data, lw=2)
# fig.save('~/example2.png') # save the figure
# fig.savefig('~/example2.png') # alternative
[3]:
<a list of 5 Line2D objects>
[4]:
# Really complex grid
import numpy as np
import proplot as pplt
state = np.random.RandomState(51423)
data = 2 * (state.rand(100, 5) - 0.5).cumsum(axis=0)
array = [ # the "picture" (1 == subplot A, 2 == subplot B, etc.)
[1, 1, 2],
[1, 1, 6],
[3, 4, 4],
[3, 5, 5],
]
fig, axs = pplt.subplots(array, figwidth=5, span=False)
axs.format(
suptitle='Really complex subplot grid',
xlabel='xlabel', ylabel='ylabel', abc=True
)
axs[0].plot(data, lw=2)
# fig.save('~/example3.png') # save the figure
# fig.savefig('~/example3.png') # alternative
[4]:
<a list of 5 Line2D objects>
[5]:
# Using a GridSpec
import numpy as np
import proplot as pplt
state = np.random.RandomState(51423)
data = 2 * (state.rand(100, 5) - 0.5).cumsum(axis=0)
gs = pplt.GridSpec(nrows=2, ncols=2, pad=1)
fig = pplt.figure(span=False, refwidth=2)
ax = fig.subplot(gs[:, 0])
ax.plot(data, lw=2)
ax = fig.subplot(gs[0, 1])
ax = fig.subplot(gs[1, 1])
fig.format(
suptitle='Subplot grid with a GridSpec',
xlabel='xlabel', ylabel='ylabel', abc=True
)
# fig.save('~/example4.png') # save the figure
# fig.savefig('~/example4.png') # alternative
Multiple subplots¶
If you create subplots all-at-once with e.g. subplots
,
proplot returns a SubplotGrid
of subplots. This list-like,
array-like object provides some useful features and unifies the behavior of the
three possible return types used by matplotlib.pyplot.subplots
:
SubplotGrid
behaves like a scalar when it is singleton. In other words, if you make a single subplot withfig, axs = pplt.subplots()
, thenaxs[0].method(...)
is equivalent toaxs.method(...)
.SubplotGrid
permits list-like 1D indexing, e.g.axs[1]
to return the second subplot. The subplots in the grid are sorted bynumber
(see this page for details on changing thenumber
order).SubplotGrid
permits array-like 2D indexing, e.g.axs[1, 0]
to return the subplot in the second row, first column, oraxs[:, 0]
to return aSubplotGrid
of every subplot in the first column. The 2D indexing is powered by the underlyinggridspec
.
SubplotGrid
includes methods for working
simultaneously with different subplots. Currently, this includes
the commands format
,
panel_axes
,
inset_axes
,
altx
, and alty
.
In the below example, we use proplot.gridspec.SubplotGrid.format
on the grid
returned by subplots
to format different subgroups of subplots
(see below for more on the format command).
Note
If you create subplots one-by-one with subplot
or
add_subplot
, a SubplotGrid
containing the numbered subplots is available via the
proplot.figure.Figure.subplotgrid
property. As with subplots made
all-at-once, the subplots in the grid are sorted by number
.
[6]:
import proplot as pplt
import numpy as np
state = np.random.RandomState(51423)
# Selected subplots in a simple grid
fig, axs = pplt.subplots(ncols=4, nrows=4, refwidth=1.2, span=True)
axs.format(xlabel='xlabel', ylabel='ylabel', suptitle='Simple SubplotGrid')
axs.format(grid=False, xlim=(0, 50), ylim=(-4, 4))
axs[:, 0].format(facecolor='blush', edgecolor='gray7', linewidth=1) # eauivalent
axs[:, 0].format(fc='blush', ec='gray7', lw=1)
axs[0, :].format(fc='sky blue', ec='gray7', lw=1)
axs[0].format(ec='black', fc='gray5', lw=1.4)
axs[1:, 1:].format(fc='gray1')
for ax in axs[1:, 1:]:
ax.plot((state.rand(50, 5) - 0.5).cumsum(axis=0), cycle='Grays', lw=2)
# Selected subplots in a complex grid
fig = pplt.figure(refwidth=1, refnum=5, span=False)
axs = fig.subplots([[1, 1, 2], [3, 4, 2], [3, 4, 5]], hratios=[2.2, 1, 1])
axs.format(xlabel='xlabel', ylabel='ylabel', suptitle='Complex SubplotGrid')
axs[0].format(ec='black', fc='gray1', lw=1.4)
axs[1, 1:].format(fc='blush')
axs[1, :1].format(fc='sky blue')
axs[-1, -1].format(fc='gray4', grid=False)
axs[0].plot((state.rand(50, 10) - 0.5).cumsum(axis=0), cycle='Grays_r', lw=2)
[6]:
<a list of 10 Line2D objects>
Plotting stuff¶
Matplotlib includes two different interfaces for plotting stuff:
a python-style object-oriented interface with axes-level commands
like matplotlib.axes.Axes.plot
, and a MATLAB-style pyplot
interface
with global commands like matplotlib.pyplot.plot
that track the “current” axes.
Proplot builds upon the python-style interface using the proplot.axes.PlotAxes
class. Since every axes used by proplot is a child of PlotAxes
, we
are able to add features directly to the axes-level commands rather than relying
on a separate library of commands (note that while some of these features may be
accessible via pyplot
commands, this is not officially supported).
For the most part, the features added by PlotAxes
represent
a superset of matplotlib. If you are not interested, you can use the plotting
commands just like you would in matplotlib. Some of the core added features include
more flexible treatment of data arguments, recognition of
xarray and pandas data structures, integration with
proplot’s colormap and color cycle
tools, and on-the-fly legend and colorbar generation.
In the below example, we create a 4-panel figure with the
familiar “1D” plotting commands plot
and
scatter
, along with the “2D” plotting commands
pcolormesh
and contourf
.
See the 1D plotting and 2D plotting
sections for details on the features added by proplot.
[7]:
import proplot as pplt
import numpy as np
# Sample data
N = 20
state = np.random.RandomState(51423)
data = N + (state.rand(N, N) - 0.55).cumsum(axis=0).cumsum(axis=1)
# Example plots
cycle = pplt.Cycle('greys', left=0.2, N=5)
fig, axs = pplt.subplots(ncols=2, nrows=2, figwidth=5, share=False)
axs[0].plot(data[:, :5], linewidth=2, linestyle='--', cycle=cycle)
axs[1].scatter(data[:, :5], marker='x', cycle=cycle)
axs[2].pcolormesh(data, cmap='greys')
m = axs[3].contourf(data, cmap='greys')
axs.format(
abc='a.', titleloc='l', title='Title',
xlabel='xlabel', ylabel='ylabel', suptitle='Quick plotting demo'
)
fig.colorbar(m, loc='b', label='label')
[7]:
<matplotlib.colorbar.Colorbar at 0x7f2d26ec57c0>
Formatting stuff¶
Matplotlib includes two different interfaces for formatting stuff:
a “python-style” object-oriented interface with instance-level commands
like matplotlib.axes.Axes.set_title
, and a “MATLAB-style” interface
that tracks current axes and provides global commands like
matplotlib.pyplot.title
.
Proplot provides the format
command as an
alternative “python-style” command for formatting a variety of plot elements.
While matplotlib’s one-liner commands still work, format
only needs to be
called once and tends to cut down on boilerplate code. You can call
format
manually or pass format
parameters to axes-creation commands
like subplots
, add_subplot
,
inset_axes
, panel_axes
, and
altx
or alty
. The
keyword arguments accepted by format
can be grouped as follows:
Figure settings. These are related to row labels, column labels, and figure “super” titles – for example,
fig.format(suptitle='Super title')
. Seeproplot.figure.Figure.format
for details.General axes settings. These are related to background patches, a-b-c labels, and axes titles – for example,
ax.format(title='Title')
Seeproplot.axes.Axes.format
for details.Cartesian axes settings (valid only for
CartesianAxes
). These are related to x and y axis ticks, spines, bounds, and labels – for example,ax.format(xlim=(0, 5))
changes the x axis bounds. Seeproplot.axes.CartesianAxes.format
and this section for details.Polar axes settings (valid only for
PolarAxes
). These are related to azimuthal and radial grid lines, bounds, and labels – for example,ax.format(rlim=(0, 10))
changes the radial bounds. Seeproplot.axes.PolarAxes.format
and this section for details.Geographic axes settings (valid only for
GeoAxes
). These are related to map bounds, meridian and parallel lines and labels, and geographic features – for example,ax.format(latlim=(0, 90))
changes the meridional bounds. Seeproplot.axes.GeoAxes.format
and this section for details.rc
settings. Any keyword matching the name of an rc setting is locally applied to the figure and axes. If the name has “dots”, you can pass it as a keyword argument with the “dots” omitted, or pass it torc_kw
in a dictionary. For example, the default a-b-c label location is controlled byrc['abc.loc']
. To change this for an entire figure, you can usefig.format(abcloc='right')
orfig.format(rc_kw={'abc.loc': 'right'})
. See this section for more on rc settings.
A format
command is available on every figure and axes.
proplot.figure.Figure.format
accepts both figure and axes
settings (applying them to each numbered subplot by default).
Similarly, proplot.axes.Axes.format
accepts both axes and figure
settings. There is also a proplot.gridspec.SubplotGrid.format
command that can be used to change settings for a subset of
subplots – for example, axs[:2].format(xtickminor=True)
turns on minor ticks for the first two subplots (see
this section for more on subplot grids).
The below example shows the many keyword arguments accepted
by format
, and demonstrates how format
can be
used to succinctly and efficiently customize plots.
[8]:
import proplot as pplt
import numpy as np
fig, axs = pplt.subplots(ncols=2, nrows=2, refwidth=2, share=False)
state = np.random.RandomState(51423)
N = 60
x = np.linspace(1, 10, N)
y = (state.rand(N, 5) - 0.5).cumsum(axis=0)
axs[0].plot(x, y, linewidth=1.5)
axs.format(
suptitle='Format command demo',
abc='A.', abcloc='ul',
title='Main', ltitle='Left', rtitle='Right', # different titles
ultitle='Title 1', urtitle='Title 2', lltitle='Title 3', lrtitle='Title 4',
toplabels=('Column 1', 'Column 2'),
leftlabels=('Row 1', 'Row 2'),
xlabel='xaxis', ylabel='yaxis',
xscale='log',
xlim=(1, 10), xticks=1,
ylim=(-3, 3), yticks=pplt.arange(-3, 3),
yticklabels=('a', 'bb', 'c', 'dd', 'e', 'ff', 'g'),
ytickloc='both', yticklabelloc='both',
xtickdir='inout', xtickminor=False, ygridminor=True,
)
Settings and styles¶
A dictionary-like object named rc
is created when you import
proplot. rc
is similar to the matplotlib rcParams
dictionary, but can be used to change both matplotlib settings and
proplot settings. The matplotlib-specific settings are
stored in rc_matplotlib
(our name for matplotlib.rcParams
) and
the proplot-specific settings are stored in rc_proplot
.
Proplot also includes a rc.style
setting that can be used to
switch between matplotlib stylesheets.
See the configuration section for details.
To modify a setting for just one subplot or figure, you can pass it to
proplot.axes.Axes.format
or proplot.figure.Figure.format
. To temporarily
modify setting(s) for a block of code, use context
.
To modify setting(s) for the entire python session, just assign it to the
rc
dictionary or use update
.
To reset everything to the default state, use reset
.
See the below example.
[9]:
import proplot as pplt
import numpy as np
# Update global settings in several different ways
pplt.rc.metacolor = 'gray6'
pplt.rc.update({'fontname': 'Source Sans Pro', 'fontsize': 11})
pplt.rc['figure.facecolor'] = 'gray3'
pplt.rc.axesfacecolor = 'gray4'
# pplt.rc.save() # save the current settings to ~/.proplotrc
# Apply settings to figure with context()
with pplt.rc.context({'suptitle.size': 13}, toplabelcolor='gray6', metawidth=1.5):
fig = pplt.figure(figwidth=6, sharey='limits', span=False)
axs = fig.subplots(ncols=2)
# Plot lines with a custom cycler
N, M = 100, 7
state = np.random.RandomState(51423)
values = np.arange(1, M + 1)
cycle = pplt.get_colors('grays', M - 1) + ['red']
for i, ax in enumerate(axs):
data = np.cumsum(state.rand(N, M) - 0.5, axis=0)
lines = ax.plot(data, linewidth=3, cycle=cycle)
# Apply settings to axes with format()
axs.format(
grid=False, xlabel='xlabel', ylabel='ylabel',
toplabels=('Column 1', 'Column 2'),
suptitle='Rc settings demo',
suptitlecolor='gray7',
abc='[A]', abcloc='l',
title='Title', titleloc='r', titlecolor='gray7'
)
# Reset persistent modifications from head of cell
pplt.rc.reset()
[10]:
import proplot as pplt
import numpy as np
# pplt.rc.style = 'style' # set the style everywhere
# Sample data
state = np.random.RandomState(51423)
data = state.rand(10, 5)
# Set up figure
fig, axs = pplt.subplots(ncols=2, nrows=2, span=False, share=False)
axs.format(suptitle='Stylesheets demo')
styles = ('ggplot', 'seaborn', '538', 'bmh')
# Apply different styles to different axes with format()
for ax, style in zip(axs, styles):
ax.format(style=style, xlabel='xlabel', ylabel='ylabel', title=style)
ax.plot(data, linewidth=3)