mrkeyoor.com_
Wed 05 Aug 05:00 UTC
PyPIDataupdated 05 Aug 2026

matplotlib

Matplotlib is Python's foundational plotting library for static, animated, and interactive visualizations. It renders publication-quality figures to PNG, SVG, PDF, and interactive GUI windows, and runs anywhere Python does: scripts, IPython, Jupyter, web servers, and desktop toolkits. Nearly every chart you have seen from a Python codebase touches it somewhere, because pandas .plot(), seaborn, and many other tools are built directly on its Figure and Axes objects.

Verdict

The workhorse of Python plotting: unmatched control, output quality, and staying power, with an API that shows its age and a learning curve bent by two coexisting styles. Learn the object-oriented interface once and it will outlast every dashboard fad.

API stability5/5Twenty years of careful backward compatibility under an explicit EffVer versioning scheme; code from old tutorials usually still runs, with deprecations phased in over multiple releases.
Docs4/5matplotlib.org has extensive tutorials, a full API reference, and a large example gallery, but the sheer volume plus the two-API split makes finding the current recommended way harder than it should be.
Maintenance5/5NumFOCUS-sponsored with a large contributor base, CI across multiple platforms, and the repo pushed August 2026; 3.11.x patch releases continue steadily.
Ecosystem5/5About 58M weekly downloads and the render layer for pandas, seaborn, and much of scientific Python; third-party styles, colormaps, and backend integrations are abundant.

Use it if

  • You need publication-quality figures with control over every tick, label, font, and margin, exported to PNG, SVG, or PDF
  • You already use pandas or seaborn and want to customize the figures they produce, since both hand you matplotlib objects
  • You need plots generated headlessly on a server or in CI, where the Agg backend writes image files with no display attached
  • You want animations or embedded plots inside Tk, Qt, or other GUI applications
Skip it if

Setup reality

pip install matplotlib pulls numpy, pillow, fonttools, and friends, but prebuilt wheels cover the common platforms so compiles are rare. The real setup pain is backends: on a headless server the default may try to open a GUI and fail, so you set the Agg backend; in Jupyter you occasionally fight magics and inline renderers; on a bare Linux install, Tk may simply be missing. After that it is the two-API problem: examples online mix plt.plot state-machine calls with fig/ax object calls, and figures leak memory in long-running processes unless you plt.close() them. Current releases require Python 3.11 or newer.

Patterns

Basic line plot, object-oriented styleline-plot

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 2 * np.pi, 200)
fig, ax = plt.subplots()
ax.plot(x, np.sin(x), label="sin")
ax.plot(x, np.cos(x), label="cos")
ax.legend()
plt.show()

Prefer fig, ax = plt.subplots() over bare plt.plot from the start; every serious customization later needs the ax object anyway.

Save a figure to filesave-figure

fig, ax = plt.subplots(figsize=(8, 4.5))
ax.plot(x, y)
fig.savefig("chart.png", dpi=300, bbox_inches="tight")
fig.savefig("chart.svg", bbox_inches="tight")
plt.close(fig)

Call savefig before plt.show(), not after; show can clear the figure and you end up saving a blank image.

Multiple panels in one figuresubplots-grid

fig, axs = plt.subplots(2, 2, figsize=(10, 8), sharex=True)
axs[0, 0].plot(x, y1)
axs[0, 1].scatter(x, y2, s=8)
axs[1, 0].hist(y1, bins=30)
axs[1, 1].bar(["a", "b", "c"], [3, 1, 2])
fig.tight_layout()

axs is a 2D numpy array here; with a single row or column it is 1D, which changes the indexing and trips people up.

Label axes, title, and legendlabels-titles

fig, ax = plt.subplots()
ax.plot(x, y, label="revenue")
ax.set_xlabel("Month")
ax.set_ylabel("USD (thousands)")
ax.set_title("Monthly revenue")
ax.legend(loc="upper left", frameon=False)

On Axes objects the methods are set_xlabel and set_title; the shorter plt.xlabel names only exist on the pyplot state machine.

Scatter with color mapping and colorbarscatter-colormap

fig, ax = plt.subplots()
sc = ax.scatter(x, y, c=values, cmap="viridis", s=20)
fig.colorbar(sc, ax=ax, label="temperature")

The colorbar needs the mappable returned by scatter; calling fig.colorbar() without it is the classic error here.

Bar chart with readable labelsbar-chart

labels = ["alpha", "beta", "gamma", "delta"]
counts = [12, 31, 7, 24]
fig, ax = plt.subplots()
bars = ax.bar(labels, counts)
ax.bar_label(bars, padding=3)
ax.tick_params(axis="x", rotation=45)

ax.bar_label writes the value above each bar; for many categories consider ax.barh, which keeps labels horizontal and legible.

Histogram of a distributionhistogram

fig, ax = plt.subplots()
ax.hist(data, bins=40, edgecolor="white", alpha=0.8)
ax.axvline(data.mean(), color="red", linestyle="--", label="mean")
ax.legend()

The default of 10 bins hides structure in most real datasets; pass an integer, a sequence of edges, or bins="auto".

Plot a time series with readable datesdatetime-axis

import matplotlib.dates as mdates

fig, ax = plt.subplots()
ax.plot(dates, values)
ax.xaxis.set_major_locator(mdates.MonthLocator())
ax.xaxis.set_major_formatter(mdates.DateFormatter("%b %Y"))
fig.autofmt_xdate()

Pass real datetime or numpy datetime64 values, not strings; strings plot as unordered categories and the axis becomes nonsense.

Render on a server with no displayheadless-backend

import matplotlib
matplotlib.use("Agg")  # before importing pyplot
import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax.plot(range(10))
fig.savefig("/tmp/out.png")
plt.close(fig)

The backend must be set before pyplot is first imported; the MPLBACKEND=Agg environment variable does the same without code changes.

Apply a built-in style sheetapply-style

import matplotlib.pyplot as plt

plt.style.use("ggplot")
print(plt.style.available)  # list all styles

with plt.style.context("dark_background"):
    fig, ax = plt.subplots()
    ax.plot(x, y)

plt.style.use is global for the process; the context manager form scopes a style to one figure without leaking.

Two y-axes on one plottwin-axes

fig, ax1 = plt.subplots()
ax1.plot(x, revenue, color="tab:blue")
ax1.set_ylabel("revenue", color="tab:blue")

ax2 = ax1.twinx()
ax2.plot(x, users, color="tab:orange")
ax2.set_ylabel("users", color="tab:orange")

Legends do not merge across twins automatically; collect handles from both axes and call ax1.legend(h1 + h2, l1 + l2).

Annotate a specific data pointannotate-point

fig, ax = plt.subplots()
ax.plot(x, y)
ax.annotate(
    "launch day",
    xy=(x[42], y[42]),
    xytext=(x[42] + 1, y[42] + 10),
    arrowprops=dict(arrowstyle="->"),
)

xy is the point the arrow targets and xytext is where the label sits; in data coordinates by default, switchable via textcoords.

Alternatives

PackageRegistryPick it when
plotlyPyPIYou want interactive charts with tooltips and zoom that drop into a web page or Dash app.
seabornPyPIYou want attractive statistical plots from DataFrames with far less code, still on matplotlib underneath.
altairPyPIYou prefer a declarative grammar-of-graphics API and your data fits comfortably in memory.