Why ProPlot?

ProPlot’s core mission is to improve upon the parts of matplotlib that tend to be cumbersome or repetitive for power users. This page enumerates the stickiest of these limitations and describes how ProPlot addresses them.

No more boilerplate


Power users often need to change lots of plot settings all at once. In matplotlib, this requires a bunch of one-liner setters and getters, like set_title.

This workflow is verbose and often confusing. It can be unclear whether settings can be changed from a Figure setter, an Axes setter, an XAxis or YAxis setter, or a miscellaneous bulk function like tick_params.


ProPlot introduces the format method for changing arbitrary settings in bulk. Think of this as an improved version of the Artist update method. This massively reduces the amount of code needed to create highly customized figures. For example, it is trivial to see that

import proplot as plot
f, ax = plot.subplots()
ax.format(linewidth=1, color='gray')
ax.format(xticks=20, xtickminor=True, xlabel='x axis', ylabel='y axis')

…is much more succinct than

import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
from matplotlib import rcParams
rcParams['axes.linewidth'] = 1
rcParams['axes.color'] = 'gray'
fig, ax = plt.subplots()
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')
ax.set_ylabel('y axis', color='gray')

Constructor functions


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 all of these extra class names and import statements can be a major drag.

Other parts of the matplotlib API were designed with this in mind. Native axes projections, axis scale classes, box style classes, arrow style classes, arc style classes, and backends are referenced with “registered” string names, as are basemap projection types. If these are already “registered”, why not “register” everything else?


In ProPlot, tick locators, tick formatters, axis scales, cartopy projections, colormaps, and property cyclers are all “registered”. This is done by creating several constructor functions and passing various keyword argument through the constructor functions. This may seem “unpythonic” but it is absolutely invaluable when making plots.

The constructor functions also accept other types of input: 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 all kinds of input. And if an instance of the corresponding class is passed to any constructor function, it is simply returned. See X and Y axis settings, Colormaps, and Color cycles for details.

The below table lists the constructor functions and the keyword arguments that use them.



Interpreted by

Keyword argument(s)


Axis Locator

format and colorbar

locator=, xlocator=, ylocator=, minorlocator=, xminorlocator=, yminorlocator=, ticks=, xticks=, yticks=, minorticks=, xminorticks=, yminorticks=


Axis Formatter

format and colorbar

formatter=, xformatter=, yformatter=, ticklabels=, xticklabels=, yticklabels=


Axis ScaleBase


xscale=, yscale=


Colormap instance

Various plotting methods



Property Cycler

Various plotting methods



Normalize instance

Various plotting methods



Projection or Basemap



Note that set_xscale and set_yscale now accept instances of ScaleBase thanks to a monkey patch applied by ProPlot.

Automatic dimensions and spacing


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, while the dimensions of the individual subplots are more important:

  1. The subplot aspect ratio is usually more relevant than the figure aspect ratio, e.g. for map projections.

  2. The subplot width and height control the evident thickness of text and other content plotted inside the axes.

Matplotlib has a tight layout algorithm to keep you from having to “tweak” the spacing, but the algorithm cannot apply different amounts of spacing between different subplot row and column boundaries. This is a silly limitation that often results in unnecessary whitespace, and can be a major problem when you want to put e.g. a legend on the outside of a subplot.


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 existing aspect will be used instead of the input aspect. Figure dimensions are calculated to accommodate subplot geometry and tight layout spacing as follows:

  • When you specify axwidth or axheight, the figure width and height are calculated automatically.

  • When you specify width, the figure height is calculated automatically.

  • When you specify height, the figure width is calculated automatically.

  • When you specify width and height or figsize, the figure dimensions are fixed.

By default, ProPlot also uses a custom tight layout algorithm that automatically determines the left, right, bottom, top, wspace, and hspace GridSepc parameters. This algorithm is simpler and more accurate because:

  1. The new GridSpec class permits variable spacing between rows and columns. It turns out this is critical for putting Colorbars and legends on the outside of subplots.

  2. Figures are restricted to have only one GridSpec per figure. This is done by requiring users to draw all of their subplots at once with subplots, and it considerably simplifies the algorithm (see GH#50 for details).

See Figure tight layout for details.

Outer colorbars and legends


In matplotlib, it is difficult to draw colorbars and legends on the outside of subplots. It is very easy to mess up the subplot aspect ratios and the colorbar widths. It is even more difficult to draw colorbars and legends that reference more than one subplot:

  • Matplotlib has no capacity for drawing colorbar axes that span multiple plots – you have to create the axes yourself. This requires so much tinkering that most users just add identical colorbars to every single subplot!

  • Legends that span multiple plots tend to require manual positioning and tinkering with the GridSpec spacing, just like legends placed outside of individual subplots.


ProPlot introduces a brand new engine for drawing colorbars and legends along the outside of individual subplots and along contiguous subplots on the edge of the figure:

  • Passing loc='l', loc='r', loc='b', or loc='t' to Axes colorbar or Axes legend draws the colorbar or legend along the outside of the axes.

  • Passing loc='l', loc='r', loc='b', or loc='t' to Figure colorbar and legend draws the colorbar or legend along the edge of the figure, centered relative to the subplot grid rather than figure coordinates.

  • Outer colorbars and legends don’t mess up the subplot layout or subplot aspect ratios, since GridSpec permits variable spacing between subplot rows and columns. This is critical e.g. if you have a colorbar between columns 1 and 2 but nothing between columns 2 and 3.

  • Figure and Axes colorbar widths are specified in physical units rather than relative units. This makes colorbar thickness independent of figure size and easier to get just right.

The colorbar and legend commands also add several new features, like colorbars-from-lines and centered-row legends. And to make Axes colorbar consistent with Axes legend, you can also now draw inset colorbars. See Colorbars and legends for details.

The axes container class


In matplotlib, subplots returns a 2D ndarray, a 1D ndarray, or the axes itself. This inconsistent behavior can be confusing.


In ProPlot, subplots returns an axes_grid container filled with axes objects. 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 axes_grid container also unifies the behavior of the three possible matplotlib.pyplot.subplots return values:

  • axes_grid is a list subclass that behaves like a scalar when it contains just one element.

  • axes_grid supports row-major or column-major 1D indexing, e.g. axs[0]. The order can be changed by passing order='F' or order='C' to subplots.

  • axes_grid permits 2D indexing, e.g. axs[1,0]. Since subplots can generate figures with arbitrarily complex subplot geometry, this 2D indexing is useful only when the arrangement happens to be a clean 2D matrix.

See Creating figures for details.

Xarray and pandas integration


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 not ideal – plotting methods should be invoked on the Axes, not on the data container! It also requires learning a slightly different syntax, and tends to encourage using the pyplot API rather than the object-oriented API.


ProPlot reproduces most of the xarray.DataArray.plot, pandas.DataFrame.plot, and pandas.Series.plot features on the Axes methods themselves!

Axis tick labels, axis labels, subplot titles, and colorbar and legend labels are automatically applied when a DataArray, DataFrame, or Series is passed through a plotting method instead of a ndarray. This is accomplished by passing positional arguments through the standardize_1d and standardize_2d wrappers. See 1d plotting and 2d plotting for details.

Various plotting improvements


Certain plotting tasks are quite difficult to accomplish with 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.

Matplotlib also has some finicky plotting issues. For example, when you pass coordinate centers to pcolor and pcolormesh, they are interpreted as edges and the last column and row of your data matrix is ignored. Also, to add labels to contour and contourf, you need to call a dedicated clabel method instead of just using a keyword argument.


The ProPlot Axes class wraps various plotting methods to duplicate certain seaborn, xarray, and pandas features, and includes several brand new features.

Plotting method




Alias for fill_between.


Alias for fill_betweenx.


Draws parametric line plots, where the parametric coordinate is denoted with colormap colors.


Now accepts 2D arrays, stacks or groups successive columns. Soon will be able to use different colors for positive/negative data.


As with bar, but for horizontal bars.


Now accepts 2D arrays, stacks or overlays successive columns. Also can use different colors for positive/negative data.


As with fill_between, but for horizontal fills.


Invokes pcolormesh and puts ticks at the center of each box.


Add contour labels by passing labels=True, interpolate to global coverage for ProjAxes by passing globe=True.


As with contour. Labels are colored black or white according to the filled contour’s luminance.


Add gridbox labels by passing labels=True, interpolate to global coverage by passing globe=True. Labels are colored black or white according to the underlying box’s luminance.


As with pcolor.

There are also some features applied in bulk to various plotting methods:

See 1d plotting and 2d plotting for details.

Cartopy and basemap integration


There are two widely-used engines for plotting geophysical data with matplotlib: cartopy and basemap. Using cartopy tends to be quite verbose and involve lots of boilerplate code, while basemap is outdated and requires you to use plotting commands on a separate Basemap object.

Also, cartopy and basemap plotting commands assume map projection coordinates unless specified otherwise. For most of us, this choice is very frustrating, since geophysical data are usually stored in longitude-latitude or “Plate Carrée” coordinates.


ProPlot integrates various cartopy and basemap features into the ProjAxes format method. This lets you apply all kinds of geographic plot settings, like coastlines, continents, political boundaries, and meridian and parallel gridlines. ProjAxes also overrides various plotting methods:

  • transform=ccrs.PlateCarree() is the new default for all GeoAxes plotting methods.

  • latlon=True is the new default for all BasemapAxes plotting methods.

  • globe=True can be passed to any 2D plotting command to enforce global coverage over the poles and across the longitude boundaries.

See Geographic and polar plots for details.

Note that active development on basemap will halt after 2020. For now, cartopy is missing several features offered by basemap – namely, flexible meridian and parallel gridline labels, drawing physical map scales, and convenience features for adding background images like the “blue marble”. But once these are added to cartopy, ProPlot may remove the basemap integration features.

Colormaps and property cycles


In matplotlib, colormaps are implemented with the ListedColormap and LinearSegmentedColormap classes. They are hard to edit and hard to create from scratch.


In ProPlot, it is easy to manipulate colormaps and property cycles:

Importing ProPlot also makes colormap names case-insensitive and reviersible by appending '_r' tot he colormap name. This is powered by the CmapDict dictionary, which replaces matplotlib’s native colormap database.

Smarter colormap normalization


In matplotlib, when extend='min', extend='max', or extend='neither' is passed to colorbar , colormap colors reserved for “out-of-bounds” values are truncated.

The problem is that matplotlib discretizes colormaps by generating a low-resolution lookup table (see LinearSegmentedColormap for details). This approach cannot be fine-tuned and creates an unnecessary copy of the colormap.

It is clear that the task discretizing colormap colors should be left to the normalizer, not the colormap itself. Matplotlib provides BoundaryNorm for this purpose, but it is seldom used and its features are limited.


In ProPlot, all colormap visualizations are automatically discretized with the BinNorm class. This reads the extend property passed to your plotting command and chooses colormap indices so that your colorbar levels always traverse the full range of colormap colors.

BinNorm also applies arbitrary continuous normalizer requested by the user, e.g. Normalize or LogNorm, before discretization. Think of BinNorm as a “meta-normalizer” – other normalizers perform the continuous transformation step, while this performs the discretization step.

Bulk global settings


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 or individual blocks of code rather than globally.


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.

For details, see Configuring proplot. The most notable bulk settings are described below.





The color for axes bounds, ticks, and labels.

axes.edgecolor, geoaxes.edgecolor, axes.labelcolor, tick.labelcolor, hatch.color, xtick.color, ytick.color


The width of axes bounds and ticks.

axes.linewidth, geoaxes.linewidth, hatch.linewidth, xtick.major.width, ytick.major.width


Font size for “small” labels.

font.size, tick.labelsize, xtick.labelsize, ytick.labelsize, axes.labelsize, legend.fontsize, geogrid.labelsize


Font size for “large” labels.

abc.size, figure.titlesize, axes.titlesize, suptitle.size, title.size, leftlabel.size, toplabel.size, rightlabel.size, bottomlabel.size


Padding between ticks and labels.

xtick.major.pad, xtick.minor.pad, ytick.major.pad, ytick.minor.pad


Tick direction.

xtick.direction, ytick.direction


Tick length.

xtick.major.size, ytick.major.size, ytick.minor.size * tickratio, xtick.minor.size * tickratio


Ratio between major and minor tick lengths.

xtick.major.size, ytick.major.size, ytick.minor.size * tickratio, xtick.minor.size * tickratio


Margin width when limits not explicitly set.

axes.xmargin, axes.ymargin

Working with fonts


In matplotlib, the default font is DejaVu Sans. In this developer’s humble opinion, DejaVu Sans is fugly AF. It is also really tricky to work with custom fonts in matplotlib.


ProPlot comes packaged with several additional fonts. The new default font is Helvetica. Albeit somewhat overused, this is a tried and tested, aesthetically pleasing sans serif font.

ProPlot adds fonts to matplotlib by making use of a completely undocumented feature: the $TTFPATH environment variable (matplotlib adds .ttf and .otf font files from folders listed in $TTFPATH). You can also use your own font files by dropping them in ~/.proplot/fonts.

Physical units engine


Matplotlib requires users to use inches for the figure size figsize. This must be confusing for users outside of the U.S.

Matplotlib also 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 meaningless numbers until you find the right one… and then if your figure size changes, you have to adjust them again.


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, em-heights, and light years (because why not?). 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.linewidth, rc.ticklen, rc[‘axes.titlesize’], and rc[‘axes.titlepad’]. See Configuring proplot for details.