seaborn
seaborn builds statistical graphics on matplotlib with tidy-data APIs - ideal for fast exploratory analysis and polished defaults.
Recipe
Quick-reference recipe card - copy-paste ready.
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
sns.set_theme(style="whitegrid", context="notebook")
df = pd.read_csv("sales.csv", parse_dates=["ordered_at"])
g = sns.relplot(data=df, x="ordered_at", y="revenue", hue="region", kind="line")
g.set_axis_labels("Date", "Revenue ($)")
plt.close()When to reach for this:
- Quick EDA on pandas DataFrames
- Group comparisons with confidence intervals
- Faceted small multiples (
col,row) - KDE/hist/box/violin for distributions
Working Example
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
rng = np.random.default_rng(42)
df = pd.DataFrame(
{
"region": np.repeat(["East", "West"], 200),
"segment": np.tile(np.repeat(["SMB", "Enterprise"], 100), 2),
"revenue": rng.normal(150, 40, 400).clip(10),
}
)
sns.set_theme(style="ticks")
fig, axes = plt.subplots(1, 3, figsize=(12, 4))
sns.histplot(data=df, x="revenue", hue="region", kde=True, ax=axes[0])
axes[0].set_title("Distribution")
sns.boxplot(data=df, x="segment", y="revenue", hue="region", ax=axes[1])
axes[1].set_title("By segment")
sns.barplot(
data=df,
x="region",
y="revenue",
estimator="median",
errorbar=("pi", 50),
ax=axes[2],
)
axes[2].set_title("Median with interval")
sns.despine()
fig.tight_layout()
fig.savefig("eda.png", dpi=150, bbox_inches="tight")
plt.close(fig)What this demonstrates:
- Tidy
data=API across three plot types histplotwith optional KDE overlayboxplotwith hue for second categorical- Modern
errorbartuple instead of deprecatedci
Deep Dive
How It Works
- seaborn maps DataFrame columns to aesthetic roles (x, y, hue, size, style).
- Statistical functions aggregate internally (mean, median, bootstrap CI).
- Figure-level functions (
relplot,catplot,displot) returnFacetGrid. - Axes-level functions (
scatterplot,barplot) acceptax=for subplots.
Plot Picker
| Question | Function |
|---|---|
| Trend over time | lineplot / relplot(kind="line") |
| Category compare | barplot, boxplot, violinplot |
| Distribution | histplot, kdeplot, ecdfplot |
| Relationship | scatterplot, regplot |
Python Notes
import seaborn as sns
# Load sample dataset for teaching
tips = sns.load_dataset("tips") # bundled CSV cache
# Palette accessible to colorblind readers
sns.set_palette("colorblind")Gotchas
- Wide vs tidy data - seaborn expects one row per observation. Fix:
meltor fix upstream schema. - Deprecated
ci=parameter - removed in seaborn 0.13+. Fix: useerrorbar=("ci", 95)orNone. - Forgotten
plt.closeon relplot - figure-level APIs create new figures silently. Fix: capture return value andplt.close(). - Bar plot of means on skewed data - mean hides outliers. Fix: pair with box/violin or show median.
- Huge categorical hue - unreadable legends. Fix: top-N filter or facet instead of hue.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
pandas .plot | Minimal quick charts | Need statistical CI defaults |
| Plotly express | Interactive exploration | Static print pipeline |
| Altair | Declarative Vega-Lite export | Need matplotlib pixel tweaks |
| matplotlib raw | Full artist control | Repetitive statistical boilerplate |
FAQs
What is tidy data for seaborn?
- Each variable is a column; each observation is a row.
- No separate columns per region unless using wide-form melt.
How do I facet by two variables?
sns.relplot(data=df, x="x", y="y", col="region", row="segment", kind="scatter")How do I turn off confidence bands?
sns.lineplot(data=df, x="t", y="v", errorbar=None)Can I pass matplotlib ax?
- Axes-level functions accept
ax=. - Figure-level functions manage their own figure.
How do I save relplot output?
g = sns.relplot(...)
g.savefig("out.png", dpi=150)What palette should I use?
colorblindorSet2for categories.- Sequential colormaps (
viridis) for continuous values.
How do I show regression?
sns.regplot(data=df, x="ad_spend", y="revenue")Does seaborn work with Polars?
- Convert with
.to_pandas()for plotting unless using alt/plotly native paths.
How do I order categories?
order = df.groupby("region")["revenue"].median().sort_values().index
sns.barplot(data=df, x="region", y="revenue", order=order)Why different counts in barplot?
- Bar height is estimator (mean/median), not raw row count.
- Use
countplotfor row counts.
Related
- matplotlib - axes-level customization
- Visualization Basics - chart selection
- Plotly - interactive follow-ups
- Visualization Best Practices - honest scales
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+.