matplotlib
matplotlib is Python's foundational plotting library. Control figures, axes, and every artist for publication-quality static charts.
Recipe
Quick-reference recipe card - copy-paste ready.
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(8, 4))
ax.plot([1, 2, 3], [4, 1, 3], label="Series A")
ax.set(xlabel="Month", ylabel="Revenue", title="Q1 Performance")
ax.legend()
fig.savefig("q1.png", dpi=150, bbox_inches="tight")
plt.close(fig)When to reach for this:
- Fine-grained control over layout, fonts, and annotations
- Static PNG/PDF/SVG for reports and papers
- Embedding charts in batch Python jobs without a browser
- Base layer under seaborn, pandas
.plot, or custom viz
Working Example
import matplotlib.pyplot as plt
import numpy as np
months = np.arange(1, 13)
east = np.array([120, 130, 125, 140, 150, 155, 160, 158, 162, 170, 175, 180])
west = np.array([100, 105, 110, 108, 115, 120, 118, 122, 125, 130, 128, 135])
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4), sharey=True)
ax1.plot(months, east, marker="o", color="#2c7bb6", label="East")
ax1.plot(months, west, marker="s", color="#fdae61", label="West")
ax1.set(xlabel="Month", ylabel="Revenue ($k)", title="Regional Trends")
ax1.legend()
ax1.grid(True, alpha=0.3)
width = 0.35
ax2.bar(months - width/2, east, width, label="East", color="#2c7bb6")
ax2.bar(months + width/2, west, width, label="West", color="#fdae61")
ax2.set(xlabel="Month", title="Side-by-Side Comparison")
ax2.legend()
fig.suptitle("2025 Revenue Dashboard", fontsize=14)
fig.tight_layout()
fig.savefig("dashboard.png", dpi=200, bbox_inches="tight")
plt.close(fig)What this demonstrates:
- Object-oriented API with explicit
figandax - Subplots with
shareyfor comparable scales - Mixed line and grouped bar charts in one figure
suptitle,tight_layout, and high-dpi export
Deep Dive
How It Works
Figureholds artists;Axesprovides coordinate system and plotting methods.- pyplot state machine (
plt.plot) is fine in notebooks; prefer OO in modules. - Artists (lines, patches, text) update on
draw- savefig renders off-screen. - Styles live in
matplotlib.rcParamsor style sheets.
Common Axes Methods
| Method | Purpose |
|---|---|
plot | Lines and markers |
bar / barh | Categorical comparisons |
hist | Distributions |
scatter | Two numeric variables |
annotate | Callouts |
Python Notes
import matplotlib.pyplot as plt
# Non-interactive backend for servers
import matplotlib
matplotlib.use("Agg")
# Date formatting on x-axis
import matplotlib.dates as mdates
ax.xaxis.set_major_formatter(mdates.DateFormatter("%Y-%m"))Gotchas
- pyplot state leaks across cells - previous figures bleed into new plots. Fix: always
fig, ax = plt.subplots()andplt.close(fig). - Forgotten
tight_layout- axis labels clip in saved PNGs. Fix:fig.tight_layout()orconstrained_layout=Trueat creation. - Huge marker sizes in scatter - obscures density. Fix: lower
sand raisealpha. - Loop without close - thousands of figures exhaust RAM. Fix:
plt.close(fig)each iteration. - Wrong dpi for print - 72 dpi looks fuzzy on paper. Fix: 300 dpi PNG or vector PDF.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| seaborn | Statistical defaults and facets | You need low-level artist control |
| Plotly | Interactive hover/zoom | Static LaTeX papers |
| Altair | Declarative grammar specs | Highly custom annotations |
| ggplot (plotnine) | R ggplot familiarity | Team standardized on matplotlib |
FAQs
Figure vs Axes vs pyplot?
Figureis the container;Axesis one plot area.pyplotis a stateful shortcut - convenient but brittle in apps.
How do I set global font size?
import matplotlib.pyplot as plt
plt.rcParams.update({"font.size": 12})How do I rotate x tick labels?
ax.tick_params(axis="x", rotation=45)How do I add a secondary y-axis?
ax2 = ax.twinx()
ax2.plot(x, other_series, color="tab:orange")Can matplotlib do subplots in a grid?
fig, axes = plt.subplots(2, 2, figsize=(8, 8))
axes[0, 1].plot(x, y)How do I color by category?
- Loop categories and plot separate
ax.plotcalls with labels. - Or use seaborn/pandas for automatic hue handling.
Why is my chart empty in CI?
- Missing
Aggbackend on headless servers. - Forgot to call
savefigbeforeclose.
How do I embed in Jupyter?
%matplotlib inlinefor static; avoid huge dpi in notebooks.- Still close figures when generating many in a loop.
How do I set log scale?
ax.set_yscale("log")Does pandas.plot use matplotlib?
- Yes - it returns Axes objects you can further tweak.
Related
- seaborn - statistical charts on matplotlib
- Visualization Basics - chart selection
- Plotting Large Data - downsampling
- Visualization Best Practices - honest axes
Stack versions: This page was written for Python 3.14.0 (stable 3.14, maintenance 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, and uv 0.6+.