Data Visualization
Seaborn for Statistical Plots
"region": ["North", "South", "East", "West"] * 25,
Jr Codex Data Science Notes
Level: Intermediate Prerequisites: Chapter 2 Time to complete: ~25 minutes
Table of Contents
- Why Seaborn on Top of Matplotlib?
- Distribution Plots
- Categorical Plots
- Relationship Plots
- The Pair Plot
- The Heatmap, Properly Styled
- Seaborn Themes
- Summary & Next Steps
1. Why Seaborn on Top of Matplotlib?
Seaborn is built directly on matplotlib (Chapter 2) but is specifically designed for statistical visualization — it works natively with pandas DataFrames, handles grouping/coloring by category automatically, and applies more polished default styling out of the box.
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
df = pd.DataFrame({
"region": ["North", "South", "East", "West"] * 25,
"spend": [100 + i * 2 for i in range(100)],
})
# The same bar chart in matplotlib takes more manual setup than in seaborn
sns.barplot(data=df, x="region", y="spend", errorbar="sd") # includes error bars automatically
plt.title("Average Spend by Region (with std dev)")Rule of thumb: reach for seaborn first when the plot is fundamentally statistical (distributions, categorical comparisons, correlations); drop to raw matplotlib (Chapter 2) when you need fine, custom control over a specific visual element seaborn doesn't expose directly.
2. Distribution Plots
Seaborn's distribution tools extend Module 3's histogram/box plot habits with more statistical nuance built in.
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
data = np.random.normal(100, 15, 1000)
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
# Histogram with an overlaid density curve (KDE)
sns.histplot(data, kde=True, ax=axes[0])
axes[0].set_title("Histogram + KDE")
# KDE alone — a smoothed estimate of the distribution's shape
sns.kdeplot(data, fill=True, ax=axes[1])
axes[1].set_title("KDE Plot")
# Box plot, seaborn style
sns.boxplot(y=data, ax=axes[2])
axes[2].set_title("Box Plot")KDE (Kernel Density Estimate) — What It Adds
─────────────────────────────────────────
A histogram's shape depends on bin width choice (Module 3, Ch.2).
A KDE smooths this into a continuous curve, giving a cleaner
sense of the underlying distribution's shape without that
bin-width sensitivity.
─────────────────────────────────────────
3. Categorical Plots
Seaborn's categorical plots are the direct successor to Module 3, Chapter 3's grouped box plots — with more variety in how the comparison is shown.
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
df = pd.DataFrame({
"region": np.random.choice(["North", "South", "East", "West"], 500),
"spend": np.random.gamma(2, 100, 500), # naturally right-skewed, like real spend data
})
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
# Box plot — five-number summary per category (Module 1, Ch.1)
sns.boxplot(data=df, x="region", y="spend", ax=axes[0])
axes[0].set_title("Box Plot by Region")
# Violin plot — box plot + KDE combined, shows the FULL distribution shape per category
sns.violinplot(data=df, x="region", y="spend", ax=axes[1])
axes[1].set_title("Violin Plot by Region")
# Strip/swarm plot — shows EVERY individual data point, good for smaller datasets
sns.stripplot(data=df, x="region", y="spend", alpha=0.3, ax=axes[2])
axes[2].set_title("Strip Plot by Region")Box vs Violin vs Strip — When to Use Which
─────────────────────────────────────────
Box plot: compact, standard, good default for MOST comparisons
Violin plot: reveals BIMODAL or unusual shapes a box plot would hide
(directly connects to Module 3, Ch.2's shape-recognition skill)
Strip/swarm: best for SMALL datasets where seeing every raw point matters
(gets cluttered/unreadable past a few hundred points)
─────────────────────────────────────────
4. Relationship Plots
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
df = pd.DataFrame({
"age": np.random.randint(20, 65, 300),
"income": np.random.normal(60000, 20000, 300),
"region": np.random.choice(["North", "South"], 300),
})
df["income"] = df["income"] + df["age"] * 300 # add a real relationship
# Scatter plot with a fitted regression line and confidence band — one line of code
sns.regplot(data=df, x="age", y="income")
plt.title("Age vs Income, with Linear Fit")# scatterplot with color-coding by a THIRD variable — directly builds on
# Module 3, Chapter 3's multivariate analysis
sns.scatterplot(data=df, x="age", y="income", hue="region", style="region")
plt.title("Age vs Income, by Region")sns.regplot's shaded band around the fitted line is a confidence interval — a visual representation of the uncertainty in that fitted relationship, connecting directly back to Module 1's sampling and hypothesis testing chapters.
5. The Pair Plot
sns.pairplot generates a full grid of scatter plots (every numeric column against every other) plus histograms on the diagonal — an extremely efficient first look at a new dataset's relationships, extending Module 3, Chapter 1's "first contact" habit.
import seaborn as sns
import pandas as pd
import numpy as np
df = pd.DataFrame({
"age": np.random.randint(20, 65, 200),
"income": np.random.normal(60000, 20000, 200),
"spend": np.random.normal(300, 100, 200),
"region": np.random.choice(["North", "South"], 200),
})
sns.pairplot(df, hue="region", diag_kind="kde")Reading a Pair Plot
─────────────────────────────────────────
Diagonal: each variable's own distribution (KDE or histogram)
Off-diagonal: scatter plot of EVERY pair of numeric variables
hue="region": each point colored by category — reveals whether
relationships DIFFER by group (Module 3's
multivariate/Simpson's Paradox concern)
─────────────────────────────────────────
Practical tip: pairplot gets slow and visually cluttered past ~6-8 numeric columns — for larger datasets, select a focused subset of the most relevant columns first.
6. The Heatmap, Properly Styled
Extending Module 3, Chapter 3's correlation heatmap with better default styling:
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
np.random.seed(0)
df = pd.DataFrame(np.random.randn(200, 5), columns=["A", "B", "C", "D", "E"])
df["B"] = df["A"] * 0.8 + np.random.randn(200) * 0.3 # force a real correlation
corr = df.corr()
plt.figure(figsize=(7, 6))
sns.heatmap(
corr,
annot=True, # print the actual correlation VALUE in each cell
fmt=".2f", # 2 decimal places
cmap="coolwarm", # diverging color scale — blue (negative) to red (positive)
center=0, # ensures 0 correlation maps to the MIDDLE of the color scale
square=True,
linewidths=0.5,
)
plt.title("Correlation Heatmap")center=0 matters: without it, a diverging colormap can visually mislead by not centering the "neutral" color exactly on zero correlation — a subtle but important correctness detail directly tied to Chapter 1's "color should encode meaning accurately" principle.
7. Seaborn Themes
import seaborn as sns
sns.set_theme(style="whitegrid") # sets a consistent look for ALL subsequent plots in the session
# Available built-in styles: "darkgrid", "whitegrid", "dark", "white", "ticks"import seaborn as sns
import matplotlib.pyplot as plt
sns.set_theme(style="whitegrid", palette="deep", font_scale=1.1)
# Every plot from this point on in the script/notebook uses this consistent styling —
# a one-line way to apply Chapter 1's design principles across an entire report or notebook8. Summary & Next Steps
Key Takeaways
- Seaborn builds on matplotlib specifically for statistical visualization — it works directly with DataFrames and handles category-based grouping/coloring automatically.
histplot/kdeplotextend Module 3's distribution analysis;boxplot/violinplot/stripplotextend its categorical comparisons, each with different trade-offs for dataset size and shape visibility.regplotandscatterplotwithhue=extend Module 3's bivariate/multivariate analysis, adding fitted trend lines and confidence bands.pairplotis the fastest way to see every numeric-numeric relationship at once;heatmapwithcenter=0is the properly-styled version of a correlation matrix.
Concept Check
- When would a violin plot reveal something a standard box plot would hide?
- Why does
center=0matter when building a correlation heatmap with a diverging colormap? - What's a practical limitation of
sns.pairploton datasets with many numeric columns?
Next Chapter
→ Chapter 4: Interactive Visualization with Plotly
Jr Codex — 1-on-1 Personalized Coaching | Back to Module Index