- Why ProPlot?
- Less typing, more plotting
- Class constructor functions
- Automatic dimensions and spacing
- Eliminating redundancies
- Outer colorbars and legends
- Enhanced plotting methods
- Xarray and pandas integration
- Cartopy and basemap integration
- Colormaps and property cycles
- The subplot container class
- Quick global settings
- Physical units engine
- The .proplot folder
Why ProPlot?
Matplotlib is an extremely powerful plotting package used by academics, engineers, and data scientists far and wide. However, the default matplotlib API can be cumbersome or repetitive for users who…
…make very complex figures with multiple subplots.
…want to finely tune their figure annotations and aesthetics.
…need to make new figures nearly every day.
ProPlot’s core mission is to provide a smoother plotting experience for matplotlib’s heaviest users. We accomplish this by expanding upon the object-oriented matplotlib API. ProPlot makes changes that would be hard to justify or difficult to incorporate into matplotlib itself, owing to differing design choices and backwards compatibility considerations.
This page enumerates these changes and explains how they address the limitations of the matplotlib API.
Less typing, more plotting
Problem
Matplotlib users often need to change lots of plot settings all at once. With the default API, this requires calling a series of one-liner setter methods.
This workflow is quite verbose – it tends to require “boilerplate code” that gets copied and pasted a hundred times. It can also be confusing – it is often unclear whether properties are applied from an Axes
setter (e.g. set_xlabel
and set_xticks
), an XAxis
or YAxis
setter (e.g. set_major_locator
and set_major_formatter
), a Spine
setter (e.g. set_bounds
), or a “bulk” property setter (e.g. tick_params
), or whether one must dig into the figure architecture and apply settings to several different objects. While this is in the spirit of object-oriented design, it seems like there should be a more unified, straightforward way to change settings for day-to-day matplotlib usage.
Solution
ProPlot introduces the format
method for changing arbitrary settings all at once. Think of this as an expanded and thoroughly documented version of the matplotlib.artist.Artist.update
method. It can also be used to update so-called quick settings and various other rc settings for a particular subplot, and to concisely work with verbose classes using the constructor functions. Further, the subplots container class can be used to invoke format
on several subplots at once.
Together, these features significantly reduce the amount of code needed to create highly customized figures. As an example, it is trivial to see that
import proplot as plot
fig, axs = plot.subplots(ncols=2)
axs.format(linewidth=1, color='gray')
axs.format(xlim=(0, 100), xticks=10, xtickminor=True, xlabel='x axis', ylabel='y axis')
…is much more succinct than
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
import matplotlib as mpl
with mpl.rc_context(rc={'axes.linewidth': 1, 'axes.color': 'gray'}):
fig, axs = plt.subplots(ncols=2, sharey=True)
axs[0].set_ylabel('y axis', color='gray')
for ax in axs:
ax.set_xlim(0, 100)
ax.xaxis.set_major_locator(mticker.MultipleLocator(10))
ax.tick_params(width=1, color='gray', labelcolor='gray')
ax.tick_params(axis='x', which='minor', bottom=True)
ax.set_xlabel('x axis', color='gray')
Class constructor functions
Problem
Matplotlib and cartopy introduce a bunch of classes with verbose names like MultipleLocator
, FormatStrFormatter
, and LambertAzimuthalEqualArea
. Since plotting code has a half life of about 30 seconds, typing out these extra class names and import statements can be a major drag.
Parts of the matplotlib API were actually designed with this in mind. Backend classes, native axes projections, axis scales, box styles, arrow styles, and arc styles are referenced with “registered” string names, as are basemap projection types. So, why not “register” everything else?
Solution
In ProPlot, tick locators, tick formatters, axis scales, cartopy projections, colormaps, and property cyclers are all “registered”. ProPlot does this by introducing several constructor functions and passing various keyword argument through the constructor functions. This may seem “unpythonic” but it is absolutely invaluable when writing plotting code.
The constructor functions also accept other input types for your convenience. For example, scalar numbers passed to Locator
returns a MultipleLocator
instance, lists of strings passed to Formatter
returns a FixedFormatter
instance, and Colormap
and Cycle
accept colormap names, individual colors, and lists of colors. Passing the relevant class instance to a constructor function simply returns the instance.
See the user guide sections on Cartesian axis settings, colormaps, and color cycles for details. The below table lists the constructor functions and the keyword arguments that use them.
Function | Return type | Used by | Keyword argument(s) |
---|---|---|---|
| |||
| |||
| |||
| |||
| |||
| |||
|
Note that set_xscale
and set_yscale
now accept instances of ScaleBase
thanks to a monkey patch applied by ProPlot.
Automatic dimensions and spacing
Problem
Matplotlib plots tend to require lots of “tweaking” when you have more than one subplot in the figure. This is partly because you must specify the physical dimensions of the figure, despite the fact that…
…the subplot aspect ratio is generally more relevant than the figure aspect ratio. An aspect ratio of
1
is desirable for most plots, and the aspect ratio must be held fixed for geographic and polar projections and mostimshow
plots.…the physical width and height of the subplot controls the “evident” thickness of text, lines, and other content plotted inside the subplot. The effect of the figure size on this “evident” thickness depends on the number of subplot tiles in the figure.
Also, while matplotlib has a tight layout algorithm to keep you from having to “tweak” the spacing, the algorithm cannot apply different amounts of spacing between different subplot row and column boundaries.
Solution
In ProPlot, you can specify the physical dimensions of a reference subplot instead of the figure by passing axwidth
, axheight
, and/or aspect
to Figure
. The default behavior is aspect=1
and axwidth=2
(inches). If the aspect ratio mode for the reference subplot is set to 'equal'
, as with geographic and polar plots and imshow
plots, the imposed aspect ratio will be used instead. The width or height of the figure can also be constrained with the width
and height
parameters, and the journal
parameter lets you create figures with suitable size for various publications.
ProPlot also uses its own “tight layout” algorithm to automatically determine the left
, right
, bottom
, top
, wspace
, and hspace
GridSpec
parameters. This algorithm has the following advantages:
Spacing between rows and columns is variable thanks to the new
GridSpec
class. This is critical for putting colorbars and legends outside of subplots without “stealing space” from the parent subplot.The “tight layout” is calculated quickly and simply because figures are restricted to have only one
GridSpec
per figure. This is done by requiring users to draw all of their subplots at once withsubplots
(although in a future version, there will be aproplot.figure
function that allows users to add subplots one-by-one while retaining the single-gridspec restriction).
See the user guide for details.
Eliminating redundancies
Problem
For many academics, figures with just one subplot are a rarity. We tend to need multiple subplots for comparing different datasets and illustrating complex concepts. Unfortunately, it is easy to end up with redundant figure elements when drawing multiple subplots, namely…
…repeated axis tick labels.
…repeated axis labels.
…repeated colorbars.
…repeated legends.
These sorts of redundancies are very common even in publications, where they waste valuable page space. They arise because this is often the path of least resistance – removing redundancies tends to require extra work.
Solution
ProPlot seeks to eliminate redundant elements to help you make clear, concise figures. We tackle this issue using shared and spanning axis labels and figure-spanning colorbars and legends.
Axis tick labels and axis labels are shared between subplots in the same row or column by default. This is controlled by the
sharex
,sharey
,spanx
, andspany
subplots
keyword args.The new
Figure
colorbar
andlegend
methods make it easy to draw colorbars and legends intended to reference more than one subplot. For details, see the next section.
Outer colorbars and legends
Problem
In matplotlib, it can be difficult to draw legend
s along the outside of subplots. Generally, you need to position the legend manually and adjust various GridSpec
spacing properties to make room for the legend. And while colorbars can be drawn along the outside of subplots with fig.colorbar(..., ax=ax)
, this can cause asymmetry in plots with more than one subplot, since the space allocated for the colorbar is “stolen” from the parent axes.
Solution
ProPlot introduces a new framework for drawing colorbars and legends referencing individual subplots and multiple contiguous subplots.
To draw a colorbar or legend on the outside of a specific subplot, pass an “outer” location (e.g.
loc='l'
orloc='left'
) toproplot.axes.Axes.colorbar
orproplot.axes.Axes.legend
.To draw a colorbar or legend on the inside of a specific subplot, pass an “inner” location (e.g.
loc='ur'
orloc='upper right'
) toproplot.axes.Axes.colorbar
orproplot.axes.Axes.legend
.To draw a colorbar or legend along the edge of the figure, use
proplot.figure.Figure.colorbar
andproplot.figure.Figure.legend
. Thecol
,row
, andspan
keyword args control whichGridSpec
rows and columns are spanned by the colorbar or legend.
Since GridSpec
permits variable spacing between subplot rows and columns, “outer” colorbars and legends do not mess up subplot spacing or add extra whitespace. This is critical e.g. if you have a colorbar between columns 1 and 2 but nothing between columns 2 and 3. Also, Figure
and Axes
colorbar widths are now specified in physical units rather than relative units, which makes colorbar thickness independent of subplot size and easier to get just right.
There are also several new colorbar and legend features described in the user guide.
Enhanced plotting methods
Problem
Certain common plotting tasks take a lot of work when using the default matplotlib API. The seaborn
, xarray
, and pandas
packages offer improvements, but it would be nice to have this functionality build right into matplotlib.
Solution
ProPlot adds various seaborn
, xarray
, and pandas
features to the Axes
plotting methods along with several brand new features designed to make your life easier.
The new
heatmap
method invokespcolormesh
and draws ticks at the center of each box. This is more convenient for things like covariance matrices.The new
parametric
method draws parametric line plots, where the parametric coordinate is denoted with a colorbar and colormap colors rather than text annotations.The
bar
andbarh
methods accept 2D arrays and can stack or group successive columns. Similarly, the newarea
andareax
methods (aliases forfill_between
andfill_betweenx
) also accept 2D arrays and can stack or overlay successive columns.The
bar
,barh
,vlines
,hlines
,area
, andareax
commands all accept anegpos
keyword argument that can be used to assign “negative” and “positive” colors to different regions.You can now add error bars or error shading to
bar
,barh
,plot
, andscatter
plots by passing special keyword arguments to these functions. You no longer have to work with theerrorbar
method directly.All 1D plotting methods accept a “cycle” keyword argument interpreted by
Cycle
and optional “colorbar” and “legend” keyword arguments for populating legends and colorbars at the specified location with the result of the plotting command.All 2D plotting methods methods accept “cmap” and “norm” keyword arguments interpreted by
Colormap
andNorm
, along with an optional “colorbar” keyword argument for drawing on-the-fly colorbars. They also accept a “labels” keyword argument used to draw contour labels or grid box labels on-the-fly, and labels are colored black or white according to the luminance of the underlying filled contour or grid box color.Matplotlib requires coordinate centers for contour plots and edges for pcolor plots. If you pass centers to pcolor, matplotlib treats them as edges and silently trims one row/column of your data. ProPlot changes this behavior so that your data is no longer trimmed.
ProPlot fixes an irritating issue with saved vector graphics where white lines appear between filled contours, pcolor patches, and colorbar patches.
Xarray and pandas integration
Problem
When you pass the array-like xarray.DataArray
, pandas.DataFrame
, and pandas.Series
containers to matplotlib plotting commands, the metadata is ignored. To create plots that are automatically labeled with this metadata, you must use the dedicated xarray.DataArray.plot
, pandas.DataFrame.plot
, and pandas.Series.plot
tools instead.
This approach is fine for quick plots, but not ideal for complex ones. It requires learning a different syntax from matplotlib, and tends to encourage using the pyplot
API rather than the object-oriented API. These tools also include features that would be useful additions to matplotlib in their own right, without requiring special data containers and an entirely separate API.
Solution
ProPlot reproduces many of the xarray.DataArray.plot
, pandas.DataFrame.plot
, and pandas.Series.plot
features on the Axes
plotting methods themselves. Passing a DataArray
, DataFrame
, or Series
through any plotting method automatically updates the axis tick labels, axis labels, subplot titles, and colorbar and legend labels from the metadata. This can be disabled by passing autoformat=False
to the plotting method or to subplots
.
Also, as described in the section on plotting methods, ProPlot implements certain features like grouped bar plots, layered area plots, heatmap plots, and on-the-fly colorbars and legends from the xarray
and pandas
APIs directly on the Axes
class.
Cartopy and basemap integration
Problem
There are two widely-used engines for plotting geophysical data with matplotlib: cartopy
and basemap
. Using cartopy tends to be verbose and involve boilerplate code, while using basemap requires you to use plotting commands on a separate Basemap
object rather than an axes object. They both require separate import statements and extra lines of code to configure the projection.
Furthermore, when you use cartopy
and basemap
plotting commands, the assumed coordinate system is map projection coordinates rather than longitude-latitude coordinates. This choice is confusing for many users, since the vast majority of geophysical data are stored in longitude-latitude or “Plate Carrée” coordinates.
Solution
ProPlot lets you specify geographic projections by simply passing the PROJ name to subplots
with e.g. fig, ax = plot.subplots(proj='pcarree')
. Alternatively, the Proj
constructor function can be used to quickly generate cartopy.crs.Projection
and Basemap
instances.
ProPlot also gives you access to various cartopy
and basemap
features via the proplot.axes.GeoAxes.format
method. This lets you quickly modify geographic plot settings like latitude and longitude gridlines, gridline labels, continents, coastlines, and political boundaries.
Finally, GeoAxes
makes longitude-latitude coordinates the “default” coordinate system by passing transform=ccrs.PlateCarree()
to CartopyAxes
plotting methods and latlon=True
to BasemapAxes
plotting methods. And to enforce global coverage over the poles and across longitude seams, you can pass globe=True
to any 2D plotting command, e.g. pcolormesh
and contourf
.
See the user guide for details.
Colormaps and property cycles
Problem
In matplotlib, colormaps are implemented with the ListedColormap
and LinearSegmentedColormap
classes. They are generally cumbersome to edit or create from scratch. The seaborn
package introduces “color palettes” to make this easier, but it would be nice to have similar features built right into the matplotlib API.
Solution
In ProPlot, it is easy to manipulate colormaps and property cycles:
The
Colormap
constructor function can be used to slice and merge existing colormaps and/or generate brand new ones.The
Cycle
constructor function can be used to make property cycles from colormaps! Property cycles can be applied to plots in a variety of ways – see the user guide for details.The new
ListedColormap
andLinearSegmentedColormap
classes include several convenient methods and have a much nicer REPL string representation.The
PerceptuallyUniformColormap
class is used to make perceptually uniform colormaps. These have smooth, aesthetically pleasing color transitions that represent your data accurately.
Importing ProPlot also makes all colormap names case-insensitive, and colormaps can be reversed or cyclically shifted by 180 degrees simply by appending '_r'
or '_s'
to the colormap name. This is powered by the ColormapDatabase
dictionary, which replaces matplotlib’s native database.
The subplot container class
Problem
In matplotlib, subplots
returns a 2D ndarray
for figures with more than one column and row, a 1D ndarray
for single-row or single-column figures, or just an Axes
instance for single-subplot figures.
Solution
In ProPlot, subplots
returns a SubplotsContainer
filled with Axes
instances. This container lets you call arbitrary methods on arbitrary subplots all at once, which can be useful when you want to style your subplots identically (e.g. axs.format(tickminor=False)
). The SubplotsContainer
class also unifies the behavior of the three possible matplotlib.pyplot.subplots
return values:
SubplotsContainer
permits 2D indexing, e.g.axs[1, 0]
. Sincesubplots
can generate figures with arbitrarily complex subplot geometry, this 2D indexing is useful only when the arrangement happens to be a clean 2D matrix.SubplotsContainer
permits 1D indexing, e.g.axs[0]
. The default order can be switched from row-major to column-major by passingorder='F'
tosubplots
.When it is singleton,
SubplotsContainer
behaves like a scalar. So when you make a single axes withfig, axs = plot.subplots()
,axs[0].method(...)
is equivalent toaxs.method(...)
.
See the user guide for details.
Quick global settings
Problem
In matplotlib, there are several rcParams
that you often want to set all at once, like the tick lengths and spine colors. It is also often desirable to change these settings for individual subplots rather than globally.
Solution
In ProPlot, you can use the rc
object to change lots of settings at once with convenient shorthands. This is meant to replace matplotlib’s rcParams
dictionary. Settings can be changed with plot.rc.key = value
, plot.rc[key] = value
, plot.rc.update(...)
, with the format
method, or with the context
method. See the user guide for details.
Physical units engine
Problem
Matplotlib uses figure-relative units for the margins left
, right
, bottom
, and top
, and axes-relative units for the column and row spacing wspace
and hspace
. Relative units tend to require “tinkering” with numbers until you find the right one. And since they are relative, if you decide to change your figure size or add a subplot, they will have to be readjusted.
Matplotlib also requires users to set the figure size figsize
in inches. This may be confusing for users outside of the United States.
Solution
ProPlot introduces the physical units engine units
for interpreting figsize
, width
, height
, axwidth
, axheight
, left
, right
, top
, bottom
, wspace
, hspace
, and arguments in a few other places. Acceptable units include inches, centimeters, millimeters, pixels, points), picas), and em-heights). Em-heights are particularly useful, as labels already present can be useful “rulers” for figuring out the amount of space needed.
units
is also used to convert settings passed to rc
from arbitrary physical units to points – for example [rc.ticklen](https://proplot.readthedocs.io/en/latest/configuration.html?highlight=ticklen#rc-proplot)
, [rc[‘title.size’]](https://proplot.readthedocs.io/en/latest/configuration.html?highlight=title.size#rc-proplot)
, and [rc[‘title.pad’]](https://proplot.readthedocs.io/en/latest/configuration.html?highlight=title.pad#rc-proplot)
. See the user guide for
The .proplot folder
Problem
In matplotlib, it can be difficult to design your own colormaps and color cycles, and there is no builtin way to save them for future use. It is also difficult to get matplotlib to use custom .ttc
, .ttf
, and .otf
font files, which may be desirable when you are working on Linux servers with limited font selections.
Solution
ProPlot automatically adds colormaps, color cycles, and font files saved in the .proplot/cmaps
, .proplot/cycles
, and .proplot/fonts
folders in your home directory. You can save colormaps and color cycles to these folders simply by passing save=True
to Colormap
and Cycle
. To manually load from these folders, e.g. if you have added files to these folders but you do not want to restart your ipython session, simply call register_cmaps
, register_cycles
, and register_fonts
.