Matplotlib
Matplotlib
Core Principle: Matplotlib is a hierarchical graphics rendering library in which every visual element is represented as an explicit Artist object within a Figure and Axes hierarchy.
Version at time of writing: 3.11.x · import matplotlib.pyplot as plt
Architectural Hierarchy
Three nested containers, and one rule.
graph TD
A["Figure (Canvas, DPI, Serialization)"] --> B["Axes (Data Area, Ticks, Labels)"]
B --> C["Artist (Line2D, Text, Patch, Collection)"]
The rule: everything you see is an Artist attached to an Axes, and every Artist has getters and setters. If you can see it, you can change it. That is why matplotlib is verbose and why it provides complete graphical control: every component can be programmatically adjusted to meet publication standards.
"Axes" means a panel, not an axis. A 2×2 grid has four Axes. The x-axis of one panel is ax.xaxis. This naming causes real confusion; see Figure and Axes.
Explicit Object-Oriented Interface
Use the object-oriented API, not the implicit plt. state machine:
fig, ax = plt.subplots(figsize=(6, 4))
ax.scatter(x, y)
ax.set_xlabel("log2 fold change")
fig.savefig("plot.pdf", bbox_inches="tight")
not
plt.scatter(x, y)
plt.xlabel("log2 fold change") # ← which Axes did this hit?
plt.savefig("plot.pdf")
The plt. form writes to whatever the "current" Axes happens to be, which breaks the moment you have subplots, a loop, or a seaborn call in between. See pyplot vs Object-Oriented API.
Computational Relevance in Biology
Journals want vector figures at specific column widths with editable text. Only matplotlib gives you that control: exact figure sizes in inches, fonts embedded as text rather than paths, colourblind-safe palettes, and per-Artist tweaking for the one gene label that overlaps another. Seaborn gets you 90% of the way in one line, and then you reach through to matplotlib for the last 10%.
Core Reference Modules
Structure Figure and Axes · pyplot vs Object-Oriented API · Matplotlib Artists · Subplots and Layout
Content Common Plot Types · Axes Styling and Annotation · Colormaps and Color
Output Saving Figures · rcParams and Style Sheets
Applied Workflows
Applied - Differential Expression Volcano Plot · Applied - Heatmaps and Clustermaps · Applied - Quality Control Plots
Installation and Verification
pip install "matplotlib>=3.10,<4"
python -c "import matplotlib; print(matplotlib.__version__, matplotlib.get_backend())"
If figures do not appear, the backend is usually the culprit. In a headless script or container use matplotlib.use("Agg") before importing pyplot and save to file rather than showing.
Essential API Patterns
fig, axes = plt.subplots(1, 2, figsize=(10, 4), sharey=True, layout="constrained")
axes[0].scatter(x, y, s=8, alpha=0.5, c=colours)
axes[0].axhline(0, ls="--", lw=1, color="grey")
axes[0].set(xlabel="log2FC", ylabel="-log10 p", title="Volcano")
axes[1].hist(y, bins=50)
axes[1].set_xlabel("-log10 p")
fig.savefig("figure.pdf", bbox_inches="tight")
plt.close(fig)
layout="constrained" (the modern replacement for tight_layout()) fixes overlapping labels automatically. plt.close(fig) in loops prevents the memory leak that produces the "More than 20 figures have been opened" warning.
Common Pitfalls & Behavioral Edge Cases
- Figures never close in a loop → memory grows until the process dies. Always
plt.close(fig). - Text saved as paths instead of glyphs, so editors cannot change it. Set
rcParams["pdf.fonttype"] = 42. See Saving Figures. - Jet / rainbow colormaps. They are perceptually non-uniform and invent structure that is not there. Use
viridis. See Colormaps and Color. plt.show()inside a script blocks; inside a notebook it is usually redundant.- Resizing after saving has no effect because figure serialization records the state at invocation time.
Related References
Seaborn · Ecosystem Map · Pandas