Seaborn for statistical plots

Seaborn is a high-level library built on Matplotlib. It takes a tidy pandas DataFrame, maps columns to x, y, colour and facets, and computes statistics (means, confidence intervals, density curves) for you. Because every seaborn plot draws on Matplotlib Axes, everything from the object-model lesson still applies.

Tidy data first

Seaborn expects long (tidy) data: one row per observation, one column per variable.

import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

rng = np.random.default_rng(1)
n = 600
df = pd.DataFrame({
    "city": rng.choice(["Bengaluru", "Ahmedabad", "Kolkata"], n),
    "channel": rng.choice(["App", "Store"], n, p=[0.6, 0.4]),
    "basket": rng.gamma(4, 180, n).round(),        # order value in INR
})
df["items"] = (df["basket"] / 150 + rng.normal(0, 1, n)).clip(1).round()

If your data is wide (one column per month), convert it with df.melt(...) before plotting.

Axes-level vs figure-level functions

Question Axes-level Figure-level
Distribution of one variable histplot, kdeplot displot
Numeric vs category boxplot, violinplot, barplot catplot
Two numeric variables scatterplot, regplot relplot, lmplot

Axes-level functions accept ax= so they fit into your own subplot grids. Figure-level functions create their own Figure and can split data into panels with col= and row=.

fig, axes = plt.subplots(1, 2, figsize=(11, 4))
sns.histplot(data=df, x="basket", hue="channel", element="step", ax=axes[0])
sns.boxplot(data=df, x="city", y="basket", ax=axes[1])
axes[1].set_ylabel("Basket value (INR)")
fig.tight_layout()

Statistics built in

sns.barplot plots the mean with a 95% confidence interval by default - not totals. Say so in your caption, or pass estimator="sum" and errorbar=None if you want totals.

sns.barplot(data=df, x="city", y="basket", hue="channel", errorbar=("ci", 95))

sns.regplot(data=df, x="items", y="basket") fits and draws a linear regression line with a confidence band - a quick visual check before formal modelling.

Facets for comparison

g = sns.displot(data=df, x="basket", col="city", kind="hist", height=3, aspect=1.2)
g.set_axis_labels("Basket value (INR)", "Orders")
g.savefig("basket_by_city.png", dpi=150)

Faceting shows each group on its own panel with shared axes, which is often clearer than overlapping colours.

Themes

sns.set_theme(style="whitegrid", context="talk") changes Matplotlib defaults globally - context scales fonts for paper, notebook, talk or poster. Call it once at the top of a notebook.

Common mistakes

  • Passing wide data and wondering why hue does nothing.
  • Reading barplot bars as totals when they are means.
  • Using a violin plot for tiny groups (under ~30 points); show the raw points with stripplot instead.
  • Trying ax= on a figure-level function like relplot - it is not supported.

Practice

Using the generated DataFrame, build a 1x3 figure: a KDE of basket value by channel, a violin plot of basket value by city, and a regplot of items versus basket. Then use catplot with col="channel" to compare cities, and write a one-line insight as the figure title.

B

Written by Bablu Kumar Jha

Founder, BeeProton AI · Mumbai, Maharashtra, India