Siksha Sarovar

Siksha Sarovar (sikshasarovar.com) is a free educational web application that helps students in India learn programming and prepare for academic and competitive exams. The platform offers structured coding courses (C, C++, Python, Java, HTML, CSS, PHP, Power BI, AI, Machine Learning, Data Science), complete university curriculum notes for BCA/MCA students with previous year question papers, Class 10 and Class 12 CBSE/HBSE school notes, and dedicated preparation material for SSC, UPSC, Banking, Railway and other government exams. Browsing the site is completely free and requires no account. Users may optionally sign in with Google solely to save their learning progress, quiz scores and personal preferences across devices.

Privacy Policy | Terms of Service | Contact Siksha Sarovar | About Siksha Sarovar

v4.0.9 · PWA
Siksha Sarovar logo
Siksha Sarovar
Your Learning Universe

Siksha Sarovar is a free e-learning platform for coding courses, BCA university notes and competitive exam preparation. Optional Google sign-in saves your learning progress across devices.

Initializing knowledge base…
Compiling modules 0%

Unit 4 — Statistical Visualization with Seaborn

Lesson 41 of 46 in the free Introduction to Data Analytics notes on Siksha Sarovar, written by Rohit Jangra.

Seaborn

Seaborn is a statistical data visualization library built on Matplotlib. It provides beautiful defaults, works directly with Pandas DataFrames, and produces complex statistical plots in one line.

Seaborn vs Matplotlib

BasisMatplotlibSeaborn
LevelLow-level, general purposeHigh-level, statistics-focused
Data inputArrays/listsDataFrames (column names directly)
Default aestheticsBasicPolished out of the box
Statistical plotsManual constructionBuilt in (regression, distribution, categorical)
Grouping by a categoryManual loopshue, col, row parameters
CustomisationTotal controlBuilt on Matplotlib — full control still available
Code volumeHighLow

Setup and Themes

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

sns.set_theme(style="whitegrid", palette="deep", font_scale=1.05)
# styles:   darkgrid, whitegrid, dark, white, ticks
# palettes: deep, muted, pastel, bright, dark, colorblind, viridis, Set2

tips = sns.load_dataset("tips")
iris = sns.load_dataset("iris")
print(tips.head())

Categories of Seaborn Plots

1. Distribution Plots

fig, axes = plt.subplots(2, 2, figsize=(13, 8))

sns.histplot(data=tips, x="total_bill", bins=25, kde=True, ax=axes[0,0], color="#168B99")
axes[0,0].set_title("histplot — histogram + KDE")

sns.kdeplot(data=tips, x="total_bill", hue="time", fill=True, alpha=0.4, ax=axes[0,1])
axes[0,1].set_title("kdeplot — smooth density by group")

sns.ecdfplot(data=tips, x="total_bill", hue="sex", ax=axes[1,0])
axes[1,0].set_title("ecdfplot — cumulative distribution")

sns.histplot(data=tips, x="total_bill", y="tip", bins=20, cbar=True, ax=axes[1,1])
axes[1,1].set_title("histplot 2-D — bivariate density")

plt.tight_layout(); plt.show()

2. Categorical Plots

fig, axes = plt.subplots(2, 3, figsize=(17, 9))

sns.countplot(data=tips, x="day", ax=axes[0,0], palette="Set2")
axes[0,0].set_title("countplot — frequency of each category")

sns.barplot(data=tips, x="day", y="total_bill", ax=axes[0,1], palette="Set2",
            errorbar=("ci", 95))
axes[0,1].set_title("barplot — mean with 95% CI")

sns.boxplot(data=tips, x="day", y="total_bill", hue="sex", ax=axes[0,2], palette="Set1")
axes[0,2].set_title("boxplot — five-number summary")

sns.violinplot(data=tips, x="day", y="total_bill", ax=axes[1,0], palette="Set3",
               inner="quartile")
axes[1,0].set_title("violinplot — box plot + distribution shape")

sns.stripplot(data=tips, x="day", y="total_bill", ax=axes[1,1], alpha=0.5, jitter=0.25)
axes[1,1].set_title("stripplot — every raw point")

sns.pointplot(data=tips, x="day", y="total_bill", hue="time", ax=axes[1,2],
              dodge=True, errorbar="se")
axes[1,2].set_title("pointplot — means connected, shows interaction")

plt.tight_layout(); plt.show()
barplot shows the MEAN of a numeric variable per category; countplot shows the COUNT of rows per category. Confusing the two is a common mistake.

3. Relational Plots

fig, axes = plt.subplots(1, 3, figsize=(17, 4.8))

sns.scatterplot(data=tips, x="total_bill", y="tip", hue="time",
                style="smoker", size="size", sizes=(30, 250), alpha=0.7, ax=axes[0])
axes[0].set_title("scatterplot — 5 variables at once")

flights = sns.load_dataset("flights")
sns.lineplot(data=flights, x="year", y="passengers", ax=axes[1],
             errorbar=("ci", 95), marker="o")
axes[1].set_title("lineplot — trend with confidence band")

sns.lineplot(data=flights, x="month", y="passengers", hue="year",
             palette="viridis", legend=False, ax=axes[2])
axes[2].set_title("lineplot — seasonality by year")
axes[2].tick_params(axis="x", rotation=45)

plt.tight_layout(); plt.show()

4. Regression Plots

fig, axes = plt.subplots(1, 3, figsize=(17, 4.8))

sns.regplot(data=tips, x="total_bill", y="tip", ax=axes[0],
            scatter_kws={"alpha": 0.5}, line_kws={"color": "#ef4444"})
axes[0].set_title("regplot — linear fit with 95% CI")

sns.regplot(data=tips, x="total_bill", y="tip", order=2, ax=axes[1],
            scatter_kws={"alpha": 0.4}, line_kws={"color": "#10b981"})
axes[1].set_title("regplot order=2 — polynomial fit")

sns.residplot(data=tips, x="total_bill", y="tip", ax=axes[2], color="#6366f1")
axes[2].set_title("residplot — diagnostics (want a random cloud)")

plt.tight_layout(); plt.show()

# lmplot = regplot + FacetGrid (creates its own figure)
sns.lmplot(data=tips, x="total_bill", y="tip", col="time", hue="smoker",
           height=4, aspect=1.1)
plt.show()

5. Matrix Plots

fig, axes = plt.subplots(1, 2, figsize=(14, 5.5))

corr = tips[["total_bill", "tip", "size"]].corr()
sns.heatmap(corr, annot=True, fmt=".3f", cmap="coolwarm", center=0,
            square=True, linewidths=1, cbar_kws={"shrink": 0.8}, ax=axes[0])
axes[0].set_title("heatmap — correlation matrix")

pivot = flights.pivot(index="month", columns="year", values="passengers")
sns.heatmap(pivot, cmap="YlOrRd", ax=axes[1], cbar_kws={"label": "Passengers"})
axes[1].set_title("heatmap — time x category matrix")

plt.tight_layout(); plt.show()

# clustermap — heatmap with hierarchical clustering (Unit 3) applied to rows/columns
sns.clustermap(iris.drop(columns="species").corr(), annot=True,
               cmap="coolwarm", center=0, figsize=(6.5, 6.5))
plt.show()
# Masking the upper triangle — the standard correlation heatmap presentation
numeric = iris.select_dtypes(include=np.number)
corr = numeric.corr()
mask = np.triu(np.ones_like(corr, dtype=bool))

plt.figure(figsize=(7.5, 6))
sns.heatmap(corr, mask=mask, annot=True, fmt=".2f", cmap="coolwarm",
            center=0, square=True, linewidths=1, vmin=-1, vmax=1)
plt.title("Iris — Correlation (lower triangle only)", fontweight="bold")
plt.tight_layout(); plt.show()

6. Multi-Plot Grids

# pairplot — every numeric pair, coloured by a category
sns.pairplot(iris, hue="species", diag_kind="kde", height=2.2,
             plot_kws={"alpha": 0.7, "s": 35}, corner=False)
plt.suptitle("Iris — Pair Plot", y=1.01, fontweight="bold")
plt.show()

# jointplot — a bivariate plot with marginal distributions
sns.jointplot(data=tips, x="total_bill", y="tip", kind="reg", height=6)
plt.show()
sns.jointplot(data=tips, x="total_bill", y="tip", kind="hex", height=6)
plt.show()
sns.jointplot(data=tips, x="total_bill", y="tip", hue="time", kind="kde", height=6)
plt.show()
# FacetGrid — the same plot repeated across subsets of the data
g = sns.FacetGrid(tips, col="time", row="smoker", height=3.4, aspect=1.25,
                  margin_titles=True)
g.map_dataframe(sns.scatterplot, x="total_bill", y="tip", alpha=0.7)
g.set_axis_labels("Total Bill", "Tip")
g.figure.suptitle("Tip vs Bill across Time and Smoker Status", y=1.03, fontweight="bold")
plt.show()

# The figure-level equivalents do this automatically
sns.catplot(data=tips, x="day", y="total_bill", hue="sex",
            col="time", kind="box", height=4, aspect=1.1)
plt.show()

sns.displot(data=tips, x="total_bill", col="day", kde=True,
            col_wrap=2, height=3.2)
plt.show()

Axes-Level vs Figure-Level Functions

Axes-levelFigure-level
Examplesscatterplot, boxplot, histplot, regplotrelplot, catplot, displot, lmplot, pairplot, jointplot
Accepts ax=Yes — draws into an existing subplotNo — creates its own figure
Faceting (col/row)NoYes
Use whenBuilding a custom multi-panel figureYou want quick faceted plots

Palettes and Styling

# Built-in palettes
sns.color_palette("deep")          # default qualitative
sns.color_palette("viridis", 8)    # sequential (ordered data)
sns.color_palette("coolwarm", 8)   # diverging (has a meaningful midpoint)
sns.color_palette("colorblind")    # accessible — use this for public reports

# Custom palette
custom = ["#168B99", "#10b981", "#f59e0b", "#6366f1", "#ef4444"]
sns.set_palette(custom)

fig, ax = plt.subplots(figsize=(9, 5))
sns.barplot(data=tips, x="day", y="total_bill", hue="sex",
            palette=["#168B99", "#f59e0b"], ax=ax, errorbar=None)
ax.set_title("Average Bill by Day and Sex", fontsize=14, fontweight="bold")
ax.set_xlabel("Day of Week"); ax.set_ylabel("Average Total Bill (Rs)")
sns.despine()                       # remove the top and right spines
ax.legend(title="Sex", frameon=False)
plt.tight_layout(); plt.show()

A Complete EDA Figure

sns.set_theme(style="whitegrid")
fig = plt.figure(figsize=(16, 11))
gs = fig.add_gridspec(3, 3, hspace=0.4, wspace=0.3)

ax1 = fig.add_subplot(gs[0, 0])
sns.histplot(data=tips, x="total_bill", kde=True, ax=ax1, color="#168B99")
ax1.set_title("Bill Distribution")

ax2 = fig.add_subplot(gs[0, 1])
sns.boxplot(data=tips, x="day", y="total_bill", ax=ax2, palette="Set2")
ax2.set_title("Bill by Day")

ax3 = fig.add_subplot(gs[0, 2])
sns.countplot(data=tips, x="day", hue="time", ax=ax3, palette="Set1")
ax3.set_title("Orders by Day and Time")

ax4 = fig.add_subplot(gs[1, :2])
sns.scatterplot(data=tips, x="total_bill", y="tip", hue="time", size="size",
                sizes=(25, 220), alpha=0.7, ax=ax4)
sns.regplot(data=tips, x="total_bill", y="tip", scatter=False,
            line_kws={"color": "#ef4444", "linewidth": 2}, ax=ax4)
ax4.set_title("Tip vs Bill with Trend Line")

ax5 = fig.add_subplot(gs[1, 2])
sns.heatmap(tips[["total_bill","tip","size"]].corr(), annot=True, fmt=".2f",
            cmap="coolwarm", center=0, square=True, ax=ax5, cbar=False)
ax5.set_title("Correlations")

ax6 = fig.add_subplot(gs[2, 0])
sns.violinplot(data=tips, x="sex", y="tip", hue="smoker", split=True,
               ax=ax6, palette="Set2")
ax6.set_title("Tip by Sex and Smoker")

ax7 = fig.add_subplot(gs[2, 1])
tips_pct = tips.assign(tip_pct=lambda d: d["tip"] / d["total_bill"] * 100)
sns.barplot(data=tips_pct, x="size", y="tip_pct", ax=ax7, palette="viridis",
            errorbar=None)
ax7.set_title("Tip % by Party Size")

ax8 = fig.add_subplot(gs[2, 2])
sns.kdeplot(data=tips_pct, x="tip_pct", hue="time", fill=True, alpha=0.4, ax=ax8)
ax8.set_title("Tip % Distribution")

fig.suptitle("Restaurant Tips — Complete Exploratory Analysis",
             fontsize=17, fontweight="bold", y=0.995)
plt.show()

Quick Reference — Which Seaborn Function

GoalFunction
Distribution of one numeric variablehistplot, kdeplot, displot
Compare a numeric variable across groupsboxplot, violinplot, barplot
Count of categoriescountplot
Relationship between two numericsscatterplot, regplot, jointplot
Trend over timelineplot
Correlation matrixheatmap
Every pair of numeric variablespairplot
Same plot across data subsetsFacetGrid, catplot, relplot, displot
Hierarchical structure in a matrixclustermap

With NumPy, Pandas, Matplotlib and Seaborn, you can analyse any dataset that fits in memory. The remaining lessons cover what happens when it doesn't.