pythonforbio.
[WASM idle]
Library Overview05/06

Seaborn

Starting sandbox…
Beginnerlesson

Seaborn

In one line: statistical plots from tidy dataframes in one call, drawn by matplotlib underneath.

Version at time of writing: 0.13.2 · import seaborn as sns

The mental model

Seaborn is a dataset-oriented interface to Matplotlib. You do not tell it what to draw; you tell it which columns map to which visual channels, and it works out the rest — including the statistical transformation, the legend, the palette, and the axis labels.

sns.scatterplot(data=df, x="log2fc", y="neglog10p", hue="significant", size="baseMean")

Three things happened there that would have been 15 lines of matplotlib: the hue column was mapped to colours and a legend was built, the size column was mapped to marker areas with its own legend, and both axes were labelled from the column names.

The price of that convenience: your data must be tidy (long form). Seaborn's contract is "one row per observation, one column per variable". If your data is wide, melt it first.

The critical distinction

Seaborn has two kinds of function and confusing them causes most seaborn frustration:

Axes-level Figure-level
Examples scatterplot, boxplot, histplot, lineplot, heatmap relplot, catplot, displot, lmplot, pairplot, jointplot, clustermap
Draws into an existing Axes you pass via ax= a Figure it creates and owns
Returns the Axes a FacetGrid / JointGrid / ClusterGrid
Accepts ax= yes no — passing it is an error
Sizing figsize on your plt.subplots height= and aspect=
Can facet no yes, via col= / row=

Rule of thumb: axes-level when the plot is one panel in a figure you are composing; figure-level when you want faceting. See Figure-level vs Axes-level.

Why bioinformatics cares

The plots you make constantly — grouped boxplots of expression by condition, distributions split by batch, correlation heatmaps, faceted small multiples across tissues, clustermaps of expression — are one-liners in seaborn and half-page functions in raw matplotlib. clustermap in particular does hierarchical clustering, dendrograms and the heatmap in a single call.

Seaborn also does the statistical work you would otherwise get subtly wrong: bootstrap confidence intervals on aggregated lines, kernel density estimates with sensible bandwidth, and regression fits with CI bands.

Core notes

Figure-level vs Axes-level · Statistical Estimation in seaborn · Faceting · seaborn Themes and Palettes · seaborn objects Interface

Because seaborn is matplotlib, these matter too: Figure and Axes · Colormaps and Color · Saving Figures · Axes Styling and Annotation

Applied

Applied - Heatmaps and Clustermaps · Applied - Differential Expression Volcano Plot · Applied - Quality Control Plots

Install and check

pip install "seaborn>=0.13,<0.14"
python -c "import seaborn as sns; print(sns.__version__)"

Seaborn releases slowly — 0.13.2 dates from January 2024 and is still current in 2026. That is stability, not abandonment, but it does mean it occasionally lags pandas/matplotlib changes.

The 20% that gets 80% of the work done

import seaborn as sns
sns.set_theme(style="whitegrid", context="paper", palette="colorblind")

sns.boxplot(data=long, x="condition", y="expression", hue="tissue")
sns.histplot(data=df, x="af", bins=50, log_scale=(True, False))
sns.scatterplot(data=df, x="pc1", y="pc2", hue="batch", style="sex")
sns.heatmap(corr, cmap="vlag", center=0, square=True)
sns.clustermap(mat, z_score=0, cmap="vlag", figsize=(8, 10))

g = sns.relplot(data=long, x="dose", y="response", col="gene", col_wrap=4, kind="line")
g.set_axis_labels("Dose (µM)", "Response")
g.savefig("facets.pdf")

sns.set_theme() once at the top of a script is the cheapest way to make everything — including your raw matplotlib plots — look consistent.

Reaching through to matplotlib

This is the workflow that makes seaborn genuinely powerful:

fig, ax = plt.subplots(figsize=(5, 4))
sns.scatterplot(data=df, x="log2fc", y="neglog10p", ax=ax)
ax.axvline(0, color="grey", lw=0.8)              # matplotlib
for _, r in top_hits.iterrows():                  # matplotlib
    ax.annotate(r["gene"], (r["log2fc"], r["neglog10p"]))
fig.savefig("volcano.pdf", bbox_inches="tight")

For figure-level functions the handles are g.figure, g.axes (an ndarray), and g.axes_dict (keyed by facet value).

Gotchas that bite newcomers

  • Passing ax= to a figure-level function → TypeError. Use the axes-level twin (relplotscatterplot/lineplot).
  • Passing wide data and getting a nonsense plot. Melt first.
  • Not realising the error bars are bootstrap 95% CIs by default, not SD or SEM. Set errorbar= explicitly in anything you publish. See Statistical Estimation in seaborn.
  • sns.heatmap does not cluster; sns.clustermap does.
  • KDE plots on bounded data (proportions, allele frequencies) bleed past 0 and 1. Use histplot or set clip=.

See also

Matplotlib · Tidy Data · Pandas · Ecosystem Map

scratch

No output yet — run the code to populate this drawer.