We often want to write a function which plots something. In order to be useful in a large variety of scenarios, a good library plotting function should be extremely flexible and support a large variety of options. We probably want to support basically the same set of options for virtually every plotting function we write. In order to avoid writing redundant code, we present the @plotter decorator, which is found in lib5c.util.plotting.

Design principles

We expect a generic plotting function plot_fn() should take in some data and plot it to the default pyplot axis. We expect to be able to override some details about the plot, including what axis should be plotted on, the plot title, whether or not to add a legend, etc. Ideally, the function should return the axis it plotted on, which would allow the caller to make further modifications to the plot before saving to disk. However, many clients will probably want to just save the figure to disk as fast as possible, so an “automatic save” option should exist as well.


We consider the plotting function plot_fn():

import matplotlib.pyplot as plt

from lib5c.util.plotting import plotter

def plot_fn(x, y, **kwargs):
    plt.scatter(x, y)

Importantly, the @plotter API requires client functions to accept arbitrary **kwargs in their signature.

A client may call

ax = plot_fn(x, y)

where the return value of plot_fn() will simply be the axis that was plotted on.

To plot to a specific axis called ax, the client may call

ax = plot_fn(x, y, ax=ax)

To automatically save the figure to disk, the client may call

plot_fn(x, y, outfile='plot.png')

Plots will be automatically saved at 300 dpi, but this can be overriden by calling

plot_fn(x, y, outfile='plot.png', dpi=800)

Plots will be styled with seaborn, using the 'ticks' axis style. To use a different seaborn style, the client can call

plot_fn(x, y, style='darkgrid')

The @plotter decorator will always automatically remove the seaborn styles after the call completes, allowing future plotting calls to look “normal”.

To avoid using the seaborn styles and stick with matplotlib defaults, the client can call

plot_fn(x, y, style=None)

The plot will be despined with sns.despine() by default. To disable this, the client can call

plot_fn(x, y, despine=False)

The decorated plot_fn() will accept a kwarg called legend. By default, this kwarg is None, which leaves all decisions about the legend in the hands of plot_fn(). If plot_fn() adds a legend to the plot, but you wish to remove it, call

plot_fn(x, y, legend=False)

If plot_fn() does not add a legend, but you wish to have one, call

plot_fn(x, y, legend=True)

If the legend is really large and you wish it to be added outside the plot area, you can call

plot_fn(x, y, legend='outside')

Other minor adjustments can be made to the plot with the kwargs illustrated in the following example call:

plot_fn(x, y, xlim=(0, 10), ylim=(-1, 1), xlabel='number of cows',
        ylabel='relative change in grass', xticks=11, yticks=[-1, 0, 1],
        title='cows vs grass')

Here we note that xticks and yticks can be passed as either an integer (in which case the specified axis will have that many evenly-spaced ticks) or as anything accepted by plt.xticks() (in our example, a list of tick locations).