Why ProPlot?¶
Matplotlib is an extremely powerful plotting package used by academics, engineers, and data scientists far and wide. However, matplotlib 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 matplotlib’s object-oriented interface. 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 matplotlib’s default interface.
Less typing, more plotting¶
Limitation
Matplotlib users often need to change lots of plot settings all at once. With the default interface, 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 to resolve this.
Think of this as an expanded and thoroughly documented version of the
matplotlib.artist.Artist.update
method.
format
can modify things like axis labels and titles and
update existing axes with new “rc” settings. It also integrates
with various constructor functions to help keep things
succinct. Further, subplot containers 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 pplt
fig, axs = pplt.subplots(ncols=2)
axs.format(linewidth=1, color='gray')
axs.format(xlim=(0, 100), xticks=10, xtickminor=True, xlabel='foo', ylabel='bar')
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('bar', 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('foo', color='gray')
Class constructor functions¶
Limitation
Matplotlib and cartopy define several classes with verbose names like
MultipleLocator
, FormatStrFormatter
,
and LambertAzimuthalEqualArea
. They also keep them out of
the top-level package namespace. 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 matplotlib’s interface 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 projections. 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 defining constructor functions and passing various keyword arguments through these functions. This may seem “unpythonic” but it is absolutely invaluable for writing plotting code.
The constructor functions also accept flexible inputs. For example, a scalar passed
to Locator
returns a MultipleLocator
, a
lists of strings passed to Formatter
returns a
FixedFormatter
, 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 plots, 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¶
Limitation
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’s tight layout algorithm helps to avoid tweaking 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 refwidth
, refheight
, and/or refaspect
to
Figure
. The default behavior is refaspect=1
and
refwidth=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 independently with the
figwidth
and figheight
parameters. If only one is specified, the other will be
adjusted to preserve subplot aspect ratios. You can select a figwidth
and/or
figheight
suitable for submission to various publications
using the journal
parameter.
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 now variable thanks to the
GridSpec
subclass. This is critical for putting colorbars and legends or axes panels outside of subplots without “stealing space” from the parent subplot.The “tight layout” algorithm is fairly simple because figures are restricted to have just one
GridSpec
. 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.
Working with multiple subplots¶
Limitation
For many matplotlib users, figures with one subplot are a rarity. These users need multiple subplots to communicate complex ideas. Unfortunately, the path of least resistance often leads to 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. It is also generally necessary to add “a-b-c” labels to figures with multiple subplots before submitting them to publications, but matplotlib has no built-in way of doing this.
Solution
ProPlot makes it easier to work with multiple subplots and create clear, concise figures.
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
proplot.figure.Figure.colorbar
andproplot.figure.Figure.legend
methods can be used to draw colorbars and legends intended to reference more than one subplot in arbitrary contiguous rows and columns. See the next section for details.A-b-c labels can be easily added to subplots using the
rc.abc
andrc[‘abc.style’]
settings – for example, usingaxs.format(abc=True, abcstyle='A.')
. This is possible becausesubplots
assigns a uniquenumber
to every axes.
Simpler colorbars and legends¶
Limitation
In matplotlib, it can be difficult to draw legend
s
along the outside of subplots. Generally, you need to position the legend
manually and tweak the spacing to make room for the legend.
Also, colorbar
s drawn along the outside of subplots
with e.g. fig.colorbar(..., ax=ax)
need to “steal” space from the parent subplot.
This can cause asymmetry in figures with more than one subplot. It is also generally
difficult to draw “inset” colorbars in matplotlib.
Solution
ProPlot includes a simple framework for drawing colorbars and legends that reference 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 alter subplot
spacing or add 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 useful new colorbar and legend features described in the user guide.
Improved plotting methods¶
Limitation
A few common plotting tasks take a lot of work using matplotlib alone. The seaborn, xarray, and pandas packages offer improvements, but it would be nice to have this functionality built right into matplotlib’s interface.
Solution
ProPlot adds various seaborn, xarray, and pandas features to the
Axes
plotting methods along with several additional features
designed to make your life easier.
The following features are relevant for “1D” plotting methods like
plot
and scatter
:
The new
parametric
method draws parametric lines, where the parametric coordinate is denoted with 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
, andplot
plots by passing keyword arguments to these functions. You do not have to work with theerrorbar
method separately.All 1D plotting methods accept a
cycle
keyword argument interpreted byCycle
andcolorbar
andlegend
keyword arguments for drawing colorbars and legends at the specified location.
The following features are relevant for “2D” plotting methods like
pcolor
and contour
:
The new
heatmap
method invokespcolormesh
and draws ticks at the center of each box. This is more convenient for things like covariance matrices.Wherever colormaps are used, they can be divided into :ref:` discrete levels <ug_discrete>` using keyword arguments like
N
andlevels
– similar tocontourf
. This is accomplished by applyingDiscreteNorm
as the new default colormap normalizer. This feature can be disabled by settingrc[‘image.discretenorm’]
toFalse
.The new
DivergingNorm
normalizer is perfect for data with a natural midpoint and offers both “fair” and “unfair” scaling, and the newLinearSegmentedNorm
normalizer can generate the uneven color gradations useful for unusually distributed data.The
contour
,contourf
,pcolormesh
, andpcolor
commands all accept alabels
keyword argument to draw contour labels and grid box labels on-the-fly. 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, then silently trims one row/column of your data. ProPlot changes this behavior so that your data is not trimmed.
ProPlot fixes an irritating issue with saved vector graphics where white lines appear between filled contours, pcolor patches, and colorbar patches.
All 2D plotting methods methods accept
cmap
andnorm
keyword arguments interpreted byColormap
andNorm
and acolorbar
keyword argument for drawing colorbars at the specified location.
Cartopy and basemap integration¶
Limitation
There are two widely-used engines for working with geophysical data in
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, “map projection” coordinates are the default coordinate system rather than longitude-latitude coordinates. This choice is confusing for many users, since the vast majority of geophysical data are stored with longitude-latitude (i.e., “Plate Carrée”) coordinates.
Solution
ProPlot lets you specify geographic projections by simply passing
the PROJ name to subplots
with
e.g. fig, ax = pplt.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. contourf
or pcolormesh
.
See the user guide for details.
Xarray and pandas integration¶
Limitation
Data intended for visualization are commonly stored in array-like containers
that include metadata – namely xarray.DataArray
, pandas.DataFrame
, and
pandas.Series
. When matplotlib receives these objects, it simply ignores
the associated metadata. To create plots that are labeled with the metadata,
you must use the 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
interface rather than the object-oriented interface.
These tools also include features that would be useful additions to matplotlib
in their own right, without requiring special containers and a separate interface.
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
feature can be disabled by setting rc.autoformat
to False
.
Also, as desribed above, ProPlot implements features
that were originally only available from the xarray.DataArray.plot
,
pandas.DataFrame.plot
, and pandas.Series.plot
commands – like grouped
bar plots, layered area plots, heatmap plots, and on-the-fly colorbars and
legends – directly within the Axes
plotting commands.
Aesthetic colors and fonts¶
Limitation
A common problem with scientific visualizations is the use of “misleading” colormaps
like 'jet'
. These colormaps tend to have jarring jumps in
hue, saturation, and luminance that can trick the human eye into seeing
non-existing patterns. It is important to use “perceptually uniform” colormaps
instead. Matplotlib comes packaged with some of its own, plus
colormaps from the ColorBrewer project, but several external projects
offer a larger variety of aesthetically pleasing “perceptually uniform” colormaps.
Matplotlib also “registers” the X11/CSS4 color names, but these are relatively
limited. The more intuitive and more numerous XKCD color survey names can
be accessed with the 'xkcd:'
prefix, but this is cumbersome, and external projects
like open color offer even more useful names.
Finally, matplotlib comes packaged with DejaVu Sans
as the default font.
This font is open source and include glyphs for a huge variety of characters,
but unfortunately (in our opinion) it is not very aesthetically pleasing. It
can also be difficult to change the default matplotlib font.
Solution
ProPlot adds new colormaps, new colors, and new fonts to help you make more aesthetically pleasing figures.
ProPlot adds colormaps from the seaborn, cmocean, SciVisColor, and Scientific Colour Maps projects. It also defines a few default perceptually uniform colormaps and includes a
PerceptuallyUniformColormap
class for generating new ones. A table of colormap and color cycles can be shown usingshow_cmaps
andshow_cycles
. Old colormaps like'jet'
can still be accessed, but this is discouraged.ProPlot adds colors from the open color project and adds XKCD color survey names without the
'xkcd:'
prefix after filtering them to exclude perceptually-similar colors and normalizing the naming pattern to make them more self-consistent. Old X11/CSS4 colors can still be accessed, but this is discouraged. A table of color names can be shown usingshow_colors
.ProPlot adds the entire TeX Gyre font family to matplotlib. These are open-source fonts designed to resemble more popular, commonly-used fonts like Helvetica and Century. They are used for the new default serif, sans-serif, monospace, cursive, and “fantasy” fonts, and they are consistent across all workstations. A table of font names can be shown using
show_fonts
.
For details on adding new colormaps, colors, and fonts, see the .proplot folder section.
Manipulating colormaps and cycles¶
Limitation
In matplotlib, colormaps are implemented with the
LinearSegmentedColormap
class, representing “smooth” color
gradations, and the ListedColormap
class, representing
“discrete” color sets. They are generally cumbersome to modify or create from scratch.
Meanwhile, property cycles used for individual plot elements are implemented
with the Cycler
class. They are also cumbersome to modify and they cannot be
“registered” by name like colormaps.
The seaborn package introduces “color palettes” to make working with colormaps and property cycles easier, but it would be nice to have similar features integrated more closely with the matplotlib interface.
Solution
In ProPlot, it is easy to manipulate colormaps and property cycles:
The
Cycle
constructor function can be used to make new property cycles and retrieve named property cycle colors. It can also make property cycles by splitting up the colors from registered or on-the-flyLinearSegmentedColormap
s. Property cycle names are “registered” by adding them asListedColormap
instances. Arbitrary cycles can be displayed usingshow_cycles
.The
Colormap
constructor function can be used to slice and merge existing colormaps or generate brand new colormaps. It can also return theListedColormap
s containing property cycle colors for use with commands likepcolor
(useful, e.g., for categorical data). It can also be used to create newPerceptuallyUniformColormap
s. Arbitrary colormaps can be displayed usingshow_cmaps
.
Importing ProPlot also makes all colormap and property cycle 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 colormap database.
Physical units engine¶
Limitation
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
, figwidth
, figheight
, refwidth
, refheight
,
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 the 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
, rc[‘title.size’]
, and
rc[‘title.pad’]
. See the user guide for details.
Flexible global settings¶
Limitation
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 pplt.rc.key = value
, pplt.rc[key] = value
,
pplt.rc.update(...)
, with the format
method, or with
the context
method. ProPlot also adds a bunch
of new settings for controlling proplot-specific features.
See the user guide for details.
Loading saved settings¶
Limitation
Matplotlib rcParams
settings can be changed using a
matplotlibrc
file in the same directory as your python script. But it
can be difficult to design and store your own colormaps and color cycles for
future use. It is also difficult to get matplotlib to use custom .ttf
and
.otf
font files, which may be desirable when you are working on
Linux servers with limited font selections.
Solution
ProPlot settings can be changed using a ~/.proplotrc
file in the home
directory or by adding files named .proplotrc
or proplotrc
to
either 1) the current directory or 2) any parent directories. This
can be useful when working in projects with lots of subfolders. See
the user guide for details.
ProPlot also automatically loads colormaps, color cycles, colors, and font files
saved in the ~/.proplot/cmaps
, ~/.proplot/cycles
, ~/.proplot/colors
,
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
, register_colors
,
or register_fonts
.