Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Matplotlib and Seaborn

🎯 Goal

After reading this chapter:

  • With Matplotlib you’ll be able to control your charts at the figure, axes level
  • With Seaborn you’ll create statistical charts in 1-2 lines
  • You know all the basic chart types needed in ML projects
  • You can prepare beautiful visualizations for EDA (Exploratory Data Analysis) reports

What to learn

Matplotlib

  • Figure and Axes architecture
  • pyplot interface (simple) vs Object-oriented API (control)
  • Main chart types: plot, scatter, bar, hist, boxplot
  • Subplots: subplots(), GridSpec
  • Customization: title, labels, legend, ticks, colors
  • Saving: savefig (PNG, SVG, PDF)

Seaborn

  • Themes and styling (set_theme, set_palette)
  • Categorical plots: countplot, barplot, boxplot, violinplot
  • Distribution plots: histplot, kdeplot, displot
  • Relationship plots: scatterplot, lineplot, regplot
  • Matrix plots: heatmap, clustermap
  • Multi-plot grids: FacetGrid, PairGrid, pairplot

Libraries

pip install matplotlib seaborn

Plotly alternative (for interactive charts):

pip install plotly

Important topics

Matplotlib architecture

Each plot in Matplotlib consists of 3 layers:

  1. Figure — the entire “canvas” (image file)
  2. Axes — a single chart area (subplot)
  3. Plot elements — line, point, bar, label, etc.
import matplotlib.pyplot as plt

# Two interfaces exist:

# 1. Pyplot API (simple, but global state)
plt.plot([1, 2, 3], [4, 5, 6])
plt.title("Quick")
plt.show()

# 2. Object-oriented API (RECOMMENDED — for larger projects)
fig, ax = plt.subplots(figsize=(8, 5))
ax.plot([1, 2, 3], [4, 5, 6])
ax.set_title("Better")
ax.set_xlabel("X")
ax.set_ylabel("Y")
fig.savefig("plot.png", dpi=150, bbox_inches="tight")

When Matplotlib, when Seaborn?

  • Matplotlib — when full control is needed, custom layout
  • Seaborn — statistical charts, direct DataFrame work, “good-looking defaults”

In real work, usually both together:

fig, ax = plt.subplots(figsize=(10, 6))
sns.heatmap(corr_matrix, annot=True, ax=ax)
ax.set_title("My Correlation Matrix")

Code examples

Main chart types

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)

fig, ax = plt.subplots(figsize=(10, 5))
ax.plot(x, y1, label="sin(x)", color="blue", linewidth=2)
ax.plot(x, y2, label="cos(x)", color="red", linestyle="--")
ax.set_title("Trigonometric Functions")
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.legend()
ax.grid(True, alpha=0.3)
plt.show()

Subplots

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

# Histogram
data = np.random.normal(0, 1, 1000)
axes[0, 0].hist(data, bins=30, color="steelblue", edgecolor="black")
axes[0, 0].set_title("Histogram")

# Scatter
x = np.random.rand(100)
y = x + np.random.normal(0, 0.1, 100)
axes[0, 1].scatter(x, y, alpha=0.6)
axes[0, 1].set_title("Scatter")

# Bar
categories = ["A", "B", "C", "D"]
values = [23, 45, 56, 78]
axes[1, 0].bar(categories, values, color=["red", "green", "blue", "orange"])
axes[1, 0].set_title("Bar")

# Box plot
data_groups = [np.random.normal(i, 1, 100) for i in range(3)]
axes[1, 1].boxplot(data_groups, labels=["Group 1", "Group 2", "Group 3"])
axes[1, 1].set_title("Box Plot")

plt.tight_layout()
plt.show()

Statistical charts in Seaborn

import seaborn as sns
import pandas as pd

# Load Titanic dataset
df = sns.load_dataset("titanic")

# Set theme
sns.set_theme(style="whitegrid", palette="muted")

# Categorical plot
fig, ax = plt.subplots(figsize=(8, 5))
sns.countplot(data=df, x="class", hue="survived", ax=ax)
ax.set_title("Survival by Class")
plt.show()

# Distribution
sns.histplot(data=df, x="age", hue="survived", multiple="stack", bins=30)
plt.title("Age distribution by survival")
plt.show()

# Pairplot — relationships among all features
sns.pairplot(df[["age", "fare", "pclass", "survived"]].dropna(), hue="survived")
plt.show()

# Heatmap — correlation matrix
numeric_df = df.select_dtypes(include="number")
corr = numeric_df.corr()
fig, ax = plt.subplots(figsize=(10, 8))
sns.heatmap(corr, annot=True, cmap="coolwarm", center=0, fmt=".2f", ax=ax)
ax.set_title("Correlation Matrix")
plt.show()

Production-ready style

# Custom theme
plt.style.use("seaborn-v0_8-darkgrid")  # or "ggplot", "fivethirtyeight"

# Or fully custom
plt.rcParams.update({
    "font.size": 11,
    "axes.titlesize": 14,
    "axes.titleweight": "bold",
    "figure.dpi": 100,
    "savefig.dpi": 200,
    "savefig.bbox": "tight",
})

Backend integration

1. Chart endpoint in FastAPI (return PNG)

from fastapi import FastAPI
from fastapi.responses import StreamingResponse
import matplotlib
matplotlib.use("Agg")  # IMPORTANT: no GUI for backend
import matplotlib.pyplot as plt
import io

app = FastAPI()

@app.get("/chart/sales.png")
async def sales_chart():
    df = pd.read_sql("SELECT date, sales FROM daily_sales ORDER BY date", engine)
    
    fig, ax = plt.subplots(figsize=(12, 5))
    ax.plot(df["date"], df["sales"], color="navy", linewidth=2)
    ax.fill_between(df["date"], df["sales"], alpha=0.3, color="navy")
    ax.set_title("Daily Sales")
    ax.set_xlabel("Date")
    ax.set_ylabel("Sales (USD)")
    
    buf = io.BytesIO()
    fig.savefig(buf, format="png", dpi=150, bbox_inches="tight")
    plt.close(fig)  # IMPORTANT: prevent memory leak
    buf.seek(0)
    
    return StreamingResponse(buf, media_type="image/png")

2. Background report generation

@celery_app.task
def generate_monthly_report(month: str):
    df = load_data(month)
    
    fig, axes = plt.subplots(2, 2, figsize=(15, 10))
    
    # Revenue trend
    df.set_index("date")["revenue"].plot(ax=axes[0, 0], title="Revenue")
    
    # Top categories
    df.groupby("category")["revenue"].sum().nlargest(10).plot.barh(ax=axes[0, 1])
    
    # User growth
    df.groupby("date")["new_users"].sum().plot(ax=axes[1, 0])
    
    # Correlation
    sns.heatmap(df.corr(), ax=axes[1, 1], annot=True, fmt=".2f")
    
    plt.tight_layout()
    fig.savefig(f"/reports/{month}.pdf", format="pdf")
    plt.close(fig)
    
    send_email_with_attachment(f"/reports/{month}.pdf")

Important note for server-side rendering

When using matplotlib in backend:

  1. Do matplotlib.use("Agg") — to avoid loading GUI backend
  2. **Call plt.close(fig)**— to prevent memory leak
  3. Thread safety — matplotlib is not thread-safe. You can use Gunicorn workers, but in async context render in a separate thread (asyncio.to_thread)

Resources

🏋️ Exercises

🟢 Easy

  1. Create 1000 random numbers with NumPy and plot their histogram (matplotlib).
  2. In Seaborn, load iris dataset and do pairplot.
  3. Create a 2x2 subplot with a different chart type in each.

🟡 Medium

  1. Plot Titanic dataset’s correlation matrix as a heatmap with annot=True and custom colormap.
  2. Create a custom theme: fonts, colors, grid style — save it in a mlflow_style.py module and import into other projects.
  3. Create a chart with 2 y-axes in one Figure (twinx) — e.g., daily users and daily revenue on the same x-axis.

🔴 Hard

  1. FastAPI Dashboard: create the endpoint /api/charts/{chart_type}.png. User sends query parameters chart_type=line|bar|hist|scatter, data_source=..., title=... and receives a nice PNG. Add caching (with Redis).
  2. PDF report: create a 10-page multi-page PDF report (using matplotlib PdfPages): cover page, analytics per section, final summary.

Capstone

notebooks/month-01/03_visualization.ipynb:

  • Load COVID-19 or any public time-series dataset
  • Create an EDA report with 6 different chart types (line, bar, hist, box, scatter, heatmap)
  • All in one Figure, layout using GridSpec
  • Save in PDF format

✅ Checklist

  • I know the difference between pyplot API and OO API
  • I understand the relationship of Figure and Axes
  • I can create subplots and manage layout
  • I know how to use heatmap, pairplot, distplot in Seaborn
  • I can create a custom style/theme
  • I know I use Agg and plt.close when using matplotlib in backend
  • I can save a chart in PNG, SVG, PDF formats

EDA Capstone project — now on to the real work.