The Two APIs: pyplot vs. Object-Oriented

Why Matplotlib Still Dominates Scientific Plotting

Despite newer libraries like Plotly and Bokeh, Matplotlib remains the backbone of Python scientific visualization. Every journal-quality figure pipeline, every automated reporting script, and most textbook plots are built with Matplotlib. Understanding its two APIs β€” the MATLAB-like pyplot interface and the object-oriented (OO) interface β€” is the key to writing both quick exploratory code and production-quality figure scripts.

Definition:

Figure and Axes Objects

In Matplotlib's object model:

  • A Figure (matplotlib.figure.Figure) is the top-level container representing the entire image. It holds one or more Axes.
  • An Axes (matplotlib.axes.Axes) is the rectangular region where data is plotted. It contains axis lines, tick labels, title, and all plotted elements (artists).
import matplotlib.pyplot as plt

fig, ax = plt.subplots()       # one Figure, one Axes
fig, axes = plt.subplots(2, 3) # one Figure, 6 Axes in a 2x3 grid

An Axes is not the plural of "axis." In Matplotlib, ax.xaxis and ax.yaxis are the two Axis objects living inside an Axes object.

Definition:

Artist β€” The Base Class of All Visual Elements

Every visual element in a Matplotlib figure is an Artist: lines, text, patches, images, tick labels, and the axes frame itself. Artists form a tree with the Figure at the root:

Figureβ†’Axesβ†’{Line2D,Text,Patch,…}\texttt{Figure} \to \texttt{Axes} \to \{\texttt{Line2D}, \texttt{Text}, \texttt{Patch}, \ldots\}

You rarely create Artists directly. Instead, Axes methods like ax.plot(), ax.scatter(), and ax.set_title() create and add them.

Definition:

pyplot (Stateful) vs. Object-Oriented (Explicit) API

pyplot API β€” a stateful MATLAB-like interface using plt.* functions. It maintains a "current figure" and "current axes" implicitly:

plt.plot(x, y)          # operates on current axes
plt.xlabel("Time (s)")  # modifies current axes
plt.title("Signal")
plt.show()

OO API β€” you create Figure and Axes objects explicitly and call methods on them:

fig, ax = plt.subplots()
ax.plot(x, y)
ax.set_xlabel("Time (s)")
ax.set_title("Signal")
fig.savefig("signal.pdf")

The OO API is always preferred for scripts, functions, and multi-panel figures. Use pyplot only in throwaway REPL explorations.

Theorem: Composability of the OO API

Any plotting function that accepts an ax parameter can be composed with any layout. If f1(ax)f_1(\texttt{ax}) draws a BER curve and f2(ax)f_2(\texttt{ax}) draws a constellation diagram, then:

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4))
f_1(ax1)
f_2(ax2)

produces a two-panel figure with no global state conflicts.

The OO API avoids the "which figure am I modifying?" ambiguity that plagues the stateful pyplot interface in complex scripts.

Theorem: Backend Architecture Theorem

Matplotlib separates frontend (Artist tree construction) from backend (rendering). The same Python code produces identical output regardless of backend:

  • Interactive backends (Qt5Agg, TkAgg, nbAgg) render to a GUI window.
  • Non-interactive backends (Agg, PDF, SVG, PGF) render to files.

Switching is done before any import:

import matplotlib
matplotlib.use("Agg")   # file-only backend, no GUI needed

This separation is why Matplotlib works on headless servers, in Docker containers, and in Jupyter notebooks equally well.

Example: The Same Plot in Both APIs

Plot y=sin⁑(2Ο€β‹…3t)y = \sin(2\pi \cdot 3t) for t∈[0,1]t \in [0, 1] using both the pyplot and OO APIs.

Example: Writing a Reusable Plotting Function

Write a function plot_ber(snr_db, ber, ax=None) that plots BER vs. SNR on a log scale and works both standalone and embedded in multi-panel figures.

Example: Temporary Style Changes with Context Managers

Apply the seaborn-v0_8-whitegrid style for one figure without affecting the rest of your script.

pyplot vs. OO API Explorer

Compare how changing frequency and amplitude affects a sinusoid. Both APIs produce identical output β€” the difference is purely in code style.

Parameters

Common Mistake: pyplot State Leaks Between Cells

Mistake:

Using plt.plot() in Jupyter without creating a new figure per cell. Previous plots "leak" into the current cell's output.

Correction:

Always start a cell with fig, ax = plt.subplots() or use plt.figure() to ensure a fresh canvas. Better yet, use the OO API exclusively.

Common Mistake: Calling plt.show() Before plt.savefig()

Mistake:

Calling plt.show() before plt.savefig() results in a blank saved file because show() clears the figure in non-interactive backends.

Correction:

Always call fig.savefig(...) before plt.show(), or better, use the OO API where fig.savefig() operates on a specific figure object and is not affected by show().

Quick Check

In Matplotlib, what is an 'Axes' object?

The x and y axis lines

The rectangular region where data is plotted

The entire window

A synonym for 'subplot'

Figure

The top-level Matplotlib container that holds all Axes and Artists.

Related: Axes, Artist

Axes

A rectangular region within a Figure where data is plotted, containing axis lines, labels, title, and data artists.

Related: Figure

Artist

The base class for all visual elements in Matplotlib. Everything you see in a figure is an Artist.

Backend

The rendering engine that converts the Artist tree to pixels or vector graphics. Interactive backends show windows; non-interactive backends write files.

Historical Note: Matplotlib's Origin Story

2003

John Hunter created Matplotlib in 2003 as a free alternative to MATLAB's plotting. The pyplot API was intentionally designed to mirror MATLAB syntax so that scientists could switch without relearning. After Hunter's passing in 2012, the community maintained and modernized the library, adding the OO API as the recommended approach.

Historical Note: The MATLAB Connection

2003-2010

Matplotlib's pyplot commands (plot, xlabel, title, subplot) are named identically to MATLAB functions. This was a deliberate design choice β€” Hunter wanted neuroimaging researchers at his lab to migrate from MATLAB without friction. The pylab module (now deprecated) went further, importing NumPy and pyplot into a single namespace to emulate MATLAB's workspace.