Matplotlib and Seaborn
Draw everything, beautifully, in code
The workhorses of Python charts: matplotlib's primitives and seaborn's statistical defaults. Master the palette, the axes object, and the faceting API once.
What you will learn
- Control matplotlib figures/axes directly
- Use seaborn's statistical plots and themes
- Build small multiples with facets
What is this?
matplotlib is the low-level engine (figures, axes, lines, bars); seaborn is the statistical layer built on it — nicer defaults for histograms, boxplots, regressions and faceted grids. A typical workflow: set a seaborn theme, then draw with either, styling roles deliberately: fig, ax = plt.subplots(...); ax.plot(...).
Why do you need it?
- Seaborn's defaults are publication-ready without hand-tuning ticks.
- Faceting (col=, row=, hue=) turns one statement into small multiples in a line.
- The axes object model makes fine control reproducible.
- One stacked skill covers exploration, reports and dashboards.
Mental model
A draftsman's instruments plus a talented apprentice
matplotlib + seaborn
matplotlib is the plain drawing instruments — precise but manual. Seaborn is the apprentice who pre-sets the drawing layout, line weights and statistical summaries so you point at the statement and it appears; you still hold the instruments for custom strokes.
How it works
- sns.set_theme(style/context/palette) sets global defaults once.
- plt.subplots() returns (fig, ax); all plotting targets ax (or seaborn accepts ax=).
- sns.histplot, sns.boxplot, sns.scatterplot, sns.regplot handle their statistical role.
- sns.catplot(col=..., row=..., hue=...) facets categorical comparisons.
- sns.relplot(col=...) facets relationships; displot facets distributions.
- Fine control: ax.set_*, ax.legend, plt.tight_layout, fig.savefig(dpi=...).
Setup → draw → refine → export.
Examples
A faceted pair in one call
import numpy as np, pandas as pdimport matplotlib; matplotlib.use("Agg")import matplotlib.pyplot as pltimport seaborn as snsrng = np.random.default_rng(4)df = pd.DataFrame({"plan": np.random.choice(["basic","pro"], 120), "month": np.random.choice(["Jun","Jul"], 120), "spend": np.round(rng.lognormal(2, .8, 120), 2)})sns.set_theme(style="whitegrid")g = sns.catplot(data=df, x="plan", y="spend", col="month", kind="box")g.savefig("facet.png")print("facet.png: one grid, two months")catplot facets the same box comparison by month, and seaborn handles the panel scaffolding.
In production
- A growth report's small-multiples grid reuses sns.catplot with fixed col order.
- A research handoff exports all charts at consistent dpi and theme from one helper.
- A DA dashboard derives its inline sparklines from matplotlib axes hooks.
Best practices
- Set a theme/palette once at the top of the script.
- Target ax for fine control instead of pyplot globals.
- Prefer seaborn functions that accept data= and hue=/col= for facets.
- tight_layout + savefig(dpi=150) before printing any report.
Common mistakes
- Mixing pyplot globals and axes objects so styling leaks across figures.
- Overplotting scatter of 50k points — use alpha, sample or hexbin.
- Facet plots with too many panels becoming unreadable.
- Forgetting bbox_inches='tight' and clipping labels on export.
Debugging tips
- A blank export → matplotlib.use('Agg') before plotting in headless contexts.
- A facet missing a level → the data lacks that group; reorder via order=.
- Legend clipping → loc/bbox_to_anchor and tight_layout.
- When a seaborn layer and matplotlib layers collide, pass share axes explicitly.
Interview checkpoint
Hands-on lab
- Configure a global theme and canvas
- Draw a faceted comparison
- Export publication-quality output
Ship a Themed, Faceted Chart Panel
30 min · intermediate
Quick quiz
Which API facets by row/col in one call?
Seaborn runs independently of matplotlib objects underneath.