Matplotlib Application Interfaces (APIs)#

Matplotlib has two major application interfaces, or styles of using the library:

  • An explicit "Axes" interface that uses methods on a Figure or Axes object tocreate other Artists, and build a visualization step by step. This has alsobeen called an "object-oriented" interface.

  • An implicit "pyplot" interface that keeps track of the last Figure and Axescreated, and adds Artists to the object it thinks the user wants.

In addition, a number of downstream libraries (likepandas andxarray) offeraplot method implemented directly on their data classes so that users cancalldata.plot().

The difference between these interfaces can be a bit confusing, particularlygiven snippets on the web that use one or the other, or sometimes multipleinterfaces in the same example. Here we attempt to point out how the "pyplot"and downstream interfaces relate to the explicit "Axes" interface to help usersbetter navigate the library.

Native Matplotlib interfaces#

The explicit "Axes" interface#

The "Axes" interface is how Matplotlib is implemented, and many customizationsand fine-tuning end up being done at this level.

This interface works by instantiating an instance of aFigure class (fig below), using asubplots method (or similar) on that object to create one or moreAxes objects (ax below), and then calling drawingmethods on the Axes (plot in this example):

importmatplotlib.pyplotaspltfig=plt.figure()ax=fig.subplots()ax.plot([1,2,3,4],[0,0.5,1,0.2])

(Sourcecode,2x.png,png)

We call this an "explicit" interface because each object is explicitlyreferenced, and used to make the next object. Keeping references to the objectsis very flexible, and allows us to customize the objects after they are created,but before they are displayed.

The implicit "pyplot" interface#

Thepyplot module shadows most of theAxes plotting methods to give the equivalent ofthe above, where the creation of the Figure and Axes is done for the user:

importmatplotlib.pyplotaspltplt.plot([1,2,3,4],[0,0.5,1,0.2])

(Sourcecode,2x.png,png)

This can be convenient, particularly when doing interactive work or simplescripts. A reference to the current Figure can be retrieved usinggcf and to the current Axes bygca. Thepyplot moduleretains a list of Figures, and each Figure retains a list of Axes on the figurefor the user so that the following:

importmatplotlib.pyplotaspltplt.subplot(1,2,1)plt.plot([1,2,3],[0,0.5,0.2])plt.subplot(1,2,2)plt.plot([3,2,1],[0,0.5,0.2])

(Sourcecode,2x.png,png)

is equivalent to:

importmatplotlib.pyplotaspltplt.subplot(1,2,1)ax=plt.gca()ax.plot([1,2,3],[0,0.5,0.2])plt.subplot(1,2,2)ax=plt.gca()ax.plot([3,2,1],[0,0.5,0.2])

(Sourcecode,2x.png,png)

In the explicit interface, this would be:

importmatplotlib.pyplotaspltfig,axs=plt.subplots(1,2)axs[0].plot([1,2,3],[0,0.5,0.2])axs[1].plot([3,2,1],[0,0.5,0.2])

(Sourcecode,2x.png,png)

Translating between the Axes interface and the pyplot interface#

You may find either interface in existing code, and unfortunately sometimes evenmixtures. This section describes the patterns for specific operations in bothinterfaces and how to translate from one to the other.

  • Creating figures is the same for both interfaces: Use the respectivepyplotfunctionsplt.figure(),plt.subplots(),plt.subplot_mosaic().For the Axes interface, you typically store the created Figure (and possiblyAxes) in variables for later use. When using the pyplot interface, thesevalues are typically not stored. Example:

    • Axes:fig,ax=plt.subplots()

    • pyplot:plt.subplots()

  • "Plotting" functions, i.e. functions that add data, are named the same andhave identical parameters on the Axes and in pyplot. Example:

    • Axes:ax.plot(x,y)

    • pyplot:plt.plot(x,y)

  • Functions that retrieve properties are named like the property in pyplotand are prefixed withget_ on the Axes. Example:

    • Axes:label=ax.get_xlabel()

    • pyplot:label=plt.xlabel()

  • Functions that set properties like the property in pyplot and are prefixed withset_ on the Axes. Example:

    • Axes:ax.set_xlabel("time")

    • pyplot:plt.xlabel("time")

Here is a short summary of the examples again as a side-by-side comparison:

Operation

Axes interface

pyplot interface

Creating figures

fig,ax=plt.subplots()

plt.subplots()

Plotting data

ax.plot(x,y)

plt.plot(x,y)

Getting properties

label=ax.get_xlabel()

label=plt.xlabel()

Setting properties

ax.set_xlabel("time")

plt.xlabel("time")

Why be explicit?#

What happens if you have to backtrack, and operate on an old axes that is notreferenced byplt.gca()? One simple way is to callsubplot again withthe same arguments. However, that quickly becomes inelegant. You can alsoinspect the Figure object and get its list of Axes objects, however, that can bemisleading (colorbars are Axes too!). The best solution is probably to save ahandle to every Axes you create, but if you do that, why not simply create theall the Axes objects at the start?

The first approach is to callplt.subplot again:

importmatplotlib.pyplotaspltplt.subplot(1,2,1)plt.plot([1,2,3],[0,0.5,0.2])plt.subplot(1,2,2)plt.plot([3,2,1],[0,0.5,0.2])plt.suptitle('Implicit Interface: re-call subplot')foriinrange(1,3):plt.subplot(1,2,i)plt.xlabel('Boo')

(Sourcecode,2x.png,png)

The second is to save a handle:

importmatplotlib.pyplotaspltaxs=[]ax=plt.subplot(1,2,1)axs+=[ax]plt.plot([1,2,3],[0,0.5,0.2])ax=plt.subplot(1,2,2)axs+=[ax]plt.plot([3,2,1],[0,0.5,0.2])plt.suptitle('Implicit Interface: save handles')foriinrange(2):plt.sca(axs[i])plt.xlabel('Boo')

(Sourcecode,2x.png,png)

However, the recommended way would be to be explicit from the outset:

importmatplotlib.pyplotaspltfig,axs=plt.subplots(1,2)axs[0].plot([1,2,3],[0,0.5,0.2])axs[1].plot([3,2,1],[0,0.5,0.2])fig.suptitle('Explicit Interface')foriinrange(2):axs[i].set_xlabel('Boo')

(Sourcecode,2x.png,png)

Third-party library "Data-object" interfaces#

Some third party libraries have chosen to implement plotting for their dataobjects, e.g.data.plot(), is seen inpandas,xarray, and otherthird-party libraries. For illustrative purposes, a downstream library mayimplement a simple data container that hasx andy data stored together,and then implements aplot method:

importmatplotlib.pyplotasplt# supplied by downstream library:classDataContainer:def__init__(self,x,y):"""        Proper docstring here!        """self._x=xself._y=ydefplot(self,ax=None,**kwargs):ifaxisNone:ax=plt.gca()ax.plot(self._x,self._y,**kwargs)ax.set_title('Plotted from DataClass!')returnax# what the user usually calls:data=DataContainer([0,1,2,3],[0,0.2,0.5,0.3])data.plot()

(Sourcecode,2x.png,png)

So the library can hide all the nitty-gritty from the user, and can make avisualization appropriate to the data type, often with good labels, choices ofcolormaps, and other convenient features.

In the above, however, we may not have liked the title the library provided.Thankfully, they pass us back the Axes from theplot() method, andunderstanding the explicit Axes interface, we could call:ax.set_title('Mypreferredtitle') to customize the title.

Many libraries also allow theirplot methods to accept an optionalaxargument. This allows us to place the visualization in an Axes that we haveplaced and perhaps customized.

Summary#

Overall, it is useful to understand the explicit "Axes" interface since it isthe most flexible and underlies the other interfaces. A user can usuallyfigure out how to drop down to the explicit interface and operate on theunderlying objects. While the explicit interface can be a bit more verboseto setup, complicated plots will often end up simpler than trying to usethe implicit "pyplot" interface.

Note

It is sometimes confusing to people that we importpyplot for bothinterfaces. Currently, thepyplot module implements the "pyplot"interface, but it also provides top-level Figure and Axes creationmethods, and ultimately spins up the graphical user interface, if oneis being used. Sopyplot is still needed regardless of theinterface chosen.

Similarly, the declarative interfaces provided by partner libraries use theobjects accessible by the "Axes" interface, and often accept these as argumentsor pass them back from methods. It is usually essential to use the explicit"Axes" interface to perform any customization of the default visualization, orto unpack the data into NumPy arrays and pass directly to Matplotlib.

Appendix: "Axes" interface with data structures#

MostAxes methods allow yet another API addressing by passing adata object to the method and specifying the arguments as strings:

importmatplotlib.pyplotaspltdata={'xdat':[0,1,2,3],'ydat':[0,0.2,0.4,0.1]}fig,ax=plt.subplots(figsize=(2,2))ax.plot('xdat','ydat',data=data)

(Sourcecode,2x.png,png)

Appendix: "pylab" interface#

There is one further interface that is highly discouraged, and that is tobasically dofrommatplotlib.pylabimport*. This imports all thefunctions frommatplotlib.pyplot,numpy,numpy.fft,numpy.linalg, andnumpy.random, and some additional functions into the global namespace.

Such a pattern is considered bad practice in modern python, as it cluttersthe global namespace. Even more severely, in the case ofpylab, this willoverwrite some builtin functions (e.g. the builtinsum will be replaced bynumpy.sum), which can lead to unexpected behavior.