Matplotlib и Seaborn
🎯 Цель
После прочтения этой главы:
- Сможете управлять своими графиками на уровне
figure,axesчерез Matplotlib - Сможете создавать статистические графики в 1-2 строки через Seaborn
- Знаете все основные типы chart, нужные для ML-проектов
- Сможете готовить красивую визуализацию для отчёта EDA (Exploratory Data Analysis)
Что нужно изучить
Matplotlib
- Архитектура
FigureиAxes pyplotinterface (простой) vs Object-oriented API (контроль)- Основные типы chart:
plot,scatter,bar,hist,boxplot - Subplots:
subplots(),GridSpec - Customization: title, labels, legend, ticks, colors
- Сохранение:
savefig(PNG, SVG, PDF)
Seaborn
- Themes и 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
Библиотеки
pip install matplotlib seaborn
Альтернатива Plotly (для интерактивных графиков):
pip install plotly
Важные темы
Архитектура Matplotlib
Каждый график в Matplotlib состоит из 3 слоёв:
- Figure — весь “холст” (файл изображения)
- Axes — область одного chart (subplot)
- Элементы plot — линия, точка, bar, label и т.д.
import matplotlib.pyplot as plt
# Есть 2 interface:
# 1. Pyplot API (простой, но global state)
plt.plot([1, 2, 3], [4, 5, 6])
plt.title("Quick")
plt.show()
# 2. Object-oriented API (РЕКОМЕНДУЕТСЯ — для крупных проектов)
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")
Когда Matplotlib, когда Seaborn?
- Matplotlib — когда нужен полный контроль, custom layout
- Seaborn — статистические chart, прямая работа с DataFrame, “хорошо выглядящие default’ы”
В реальной работе обычно используют оба вместе:
fig, ax = plt.subplots(figsize=(10, 6))
sns.heatmap(corr_matrix, annot=True, ax=ax)
ax.set_title("My Correlation Matrix")
Примеры кода
Основные типы chart
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()
Статистические chart в Seaborn
import seaborn as sns
import pandas as pd
# Загрузка Titanic dataset
df = sns.load_dataset("titanic")
# Установка темы
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 — связь между всеми 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()
Красивый style для production
# Custom theme
plt.style.use("seaborn-v0_8-darkgrid") # или "ggplot", "fivethirtyeight"
# Или полностью custom
plt.rcParams.update({
"font.size": 11,
"axes.titlesize": 14,
"axes.titleweight": "bold",
"figure.dpi": 100,
"savefig.dpi": 200,
"savefig.bbox": "tight",
})
Интеграция с backend
1. Chart endpoint в FastAPI (возврат PNG)
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
import matplotlib
matplotlib.use("Agg") # ВАЖНО: для backend нет GUI
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) # ВАЖНО: предотвращение 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")
Важное замечание для server-side rendering
При использовании matplotlib в backend:
- Делайте
matplotlib.use("Agg")— чтобы не загружать GUI backend - **Вызывайте
plt.close(fig)**— чтобы предотвратить memory leak - Thread safety — matplotlib не thread-safe. Можно использовать Gunicorn workers, но в async-контексте выносите в отдельный поток (
asyncio.to_thread)
Ресурсы
- Matplotlib official tutorials — matplotlib.org/stable/tutorials/
- Seaborn gallery — seaborn.pydata.org/examples/
- “Python Data Science Handbook” — Jake VanderPlas (бесплатно online: jakevdp.github.io)
- “Storytelling with Data” — Cole Nussbaumer Knaflic (chart design)
- Plotly Express — для интерактивных chart: plotly.com/python/plotly-express/
🏋️ Упражнения
🟢 Easy
- С NumPy создайте 1000 случайных чисел и нарисуйте их histogram (matplotlib).
- В Seaborn загрузите
irisdataset и сделайтеpairplot. - Создайте 2x2 subplot, в каждом — свой тип chart.
🟡 Medium
- Нарисуйте correlation matrix Titanic dataset через heatmap с
annot=Trueи custom colormap. - Создайте custom theme: шрифт, цвета, стиль grid — сохраните его в модуле
mlflow_style.pyи импортируйте в других проектах. - Создайте chart с 2 y-axis в одном
Figure(twinx) — например, daily users и daily revenue на одном x-axis.
🔴 Hard
- FastAPI Dashboard: создайте endpoint
/api/charts/{chart_type}.png. Пользователь передаёт query-параметрыchart_type=line|bar|hist|scatter,data_source=...,title=...и получает красивый PNG. Добавьте caching (через Redis). - PDF report: создайте multi-page PDF report из 10 страниц (используя matplotlib
PdfPages): обложка, аналитика по разделам, итоговая summary.
Capstone
notebooks/month-01/03_visualization.ipynb:
- Загрузите COVID-19 или любой public time-series dataset
- Создайте EDA report с 6 разными типами chart (line, bar, hist, box, scatter, heatmap)
- Всё в одном
Figure, сделайте layout черезGridSpec - Сохраните в PDF формате
✅ Чек-лист
- Знаю разницу между API
pyplotи OO API - Понимаю взаимосвязь
FigureиAxes - Умею создавать subplots и управлять layout
- Знаю, как использовать heatmap, pairplot, distplot в Seaborn
- Умею создавать custom style/theme
- Знаю, что в backend для matplotlib использую
Aggиplt.close - Могу сохранить chart в PNG, SVG, PDF форматах
EDA Capstone проект — переходим к настоящей работе.