Matplotlib is Python’s de‑facto 2D/3D plotting library.
This guide covers everything from quick pyplot
sketches to the full object‑oriented (OO) API,
including new features up to v3.10.1 (released 27 Feb 2025) .
Figure
/Axes
objectsgridspec
, twin axesNote: Every snippet runs on Python 3.9+ and Matplotlib ≥3.3; optional features need ≥3.7.
# stable release (pip)
python -m pip install matplotlib
# or from conda‑forge
conda install -c conda-forge matplotlib
pip install matplotlib[qt]
– Qt interactive back‑endpip install mplcairo
– high‑quality vector rasterizerpip install ipympl
– Jupyter interactive widgets
import matplotlib.pyplot as plt # pyplot helper state‑machine
from matplotlib import cm # colour‑maps
import numpy as np
The OO approach is explicit: you create a Figure
then add one or more
Axes
(the real plot containers).
fig = plt.figure(figsize=(6,4), dpi=100)
ax = fig.add_subplot(1, 1, 1) # rows, cols, index
ax.plot([0, 1], [0, 1], label="linear")
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_title("Basic OO plot")
fig.tight_layout()
plt.show()
Axes.plot()
– generic line/markerAxes.scatter()
– 2‑D pointsAxes.bar()
– categorical barsAxes.imshow()
– images & heatmapsAxes.set_*()
family – labels, limits, scales
plt.figure()
plt.plot([0, 1, 2], [0, 1, 0], marker="o")
plt.title("Pyplot quick‑start")
plt.xlabel("x‑axis")
plt.ylabel("y‑axis")
plt.show()
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8,3), sharey=True)
ax1.plot(np.sin(np.linspace(0, 2*np.pi, 100)))
ax1.set_title("sin")
ax2.plot(np.cos(np.linspace(0, 2*np.pi, 100)), color="orange")
ax2.set_title("cos")
Tip: plt.subplots()
returns both figure and axes – a
convenient bridge between state‑machine and OO style.
ax.plot(x, y,
linestyle="--", linewidth=2,
marker="s", markersize=6, markerfacecolor="white")
scatter = ax.scatter(x, y, c=y, s=(y*20)**2, cmap="viridis", alpha=.8)
fig.colorbar(scatter, ax=ax, label="value")
ax.hist(data, bins=30, density=True, histtype="stepfilled", alpha=.6)
ax.bar(categories, counts, yerr=errors, capsize=4)
im = ax.imshow(matrix, cmap="plasma", origin="lower")
fig.colorbar(im, ax=ax)
import matplotlib as mpl
mpl.rcParams["font.family"] = "DejaVu Sans"
mpl.rcParams.update({"figure.facecolor": "#f9f9f9"})
plt.style.use("seaborn-v0_8") # improved seaborn‑compat style
# available styles: plt.style.available
with plt.rc_context({"axes.prop_cycle": cycler(color=["#ff6f69", "#96ceb4"])}):
ax.plot(...)
Store custom styles as ~/.config/matplotlib/stylelib/your_style.mplstyle
and load them with plt.style.use("your_style")
.
ax.set_xlim(0, 10); ax.set_ylim(-1, 1)
ax.set_xticks(range(0, 11, 2))
ax.grid(True, which="both", linestyle=":")
ax.legend(title="Signal", loc="upper right", frameon=False)
ax.annotate("peak", xy=(np.pi/2, 1), xytext=(2, .6),
arrowprops={"arrowstyle":"->"})
fig, axes = plt.subplots(2, 3, figsize=(8,4), sharex="col", sharey="row")
for i, ax in enumerate(axes.flat):
ax.text(.5, .5, f"ax {i}", ha="center", va="center")
fig.tight_layout()
import matplotlib.gridspec as gs
fig = plt.figure(figsize=(6,6))
spec = gs.GridSpec(3, 3, figure=fig)
ax0 = fig.add_subplot(spec[0, :])
ax1 = fig.add_subplot(spec[1:, :2])
ax2 = fig.add_subplot(spec[1:, 2])
ax2 = ax.twinx()
ax2.plot(x, z, color="green", label="second y")
Matplotlib ships perceptually uniform maps like viridis
,
plasma
and cividis
.
norm = plt.Normalize(vmin=data.min(), vmax=data.max())
im = ax.imshow(data, cmap="magma", norm=norm)
fig.colorbar(im, ax=ax, orientation="horizontal")
from matplotlib.colors import ListedColormap
cmap = ListedColormap(["#00274d", "#0057b8", "#ffd200"])
plt.register_cmap("brand", cmap)
from mpl_toolkits.mplot3d import Axes3D # implicit import
ax = fig.add_subplot(111, projection="3d")
ax.plot_surface(X, Y, Z, cmap="viridis", edgecolor="none")
from matplotlib.animation import FuncAnimation
fig, ax = plt.subplots()
line, = ax.plot([], [], lw=2)
def init():
ax.set_xlim(0, 2*np.pi); ax.set_ylim(-1, 1)
return line,
def update(frame):
x = np.linspace(0, frame, 100)
line.set_data(x, np.sin(x))
return line,
ani = FuncAnimation(fig, update, frames=np.linspace(0, 2*np.pi, 120),
init_func=init, blit=True, interval=30)
ani.save("sine.gif", writer="pillow")
mpl.use("QtAgg") # high‑performance native
mpl.use("module://ipympl")# Jupyter ipywidgets
from matplotlib.widgets import Slider
fig, ax = plt.subplots()
plt.subplots_adjust(bottom=.25)
t = np.linspace(0, 2*np.pi, 1000)
line, = ax.plot(t, np.sin(t))
axamp = plt.axes([0.25, 0.1, 0.65, 0.03])
slider = Slider(axamp, "Freq", 0.1, 10.0, valinit=1)
def on_change(val):
line.set_ydata(np.sin(val*t))
fig.canvas.draw_idle()
slider.on_changed(on_change)
fig.savefig("plot.png", dpi=300, bbox_inches="tight")
fig.savefig("plot.svg") # vector
fig.savefig("plot.pdf") # print‑quality
Use bbox_inches="tight"
and pad_inches
to trim
whitespace. PNGs above 300 dpi suit publication; SVG/PDF best for print/vector.
Line2D.set()
or rc context managers.blit=True
in FuncAnimation
.Agg
back‑end for headless rendering on servers.matplotlib.pyplot.decimate
) or render via
LineCollection
/PathCollection
.blocking=False
to avoid GUI freeze..webp
output via pillow ≥10.plt.subplots()
for responsive mosaics.Full release‑notes: Matplotlib 3.10.1 docs .
fig, ax = plt.subplots()
– canonical layoutax.plot
/ ax.scatter
/ ax.bar
ax.set_title
, ax.set_xlabel
, ax.set_ylabel
ax.legend()
, ax.grid()
fig.tight_layout()
, fig.savefig()
Add images to the grid below (horizontal scroll enabled):