Семестр 3 · Модуль 3 · Неделя 7
Matplotlib и Seaborn: честные графики
Объектная модель Figure/Axes · виды графиков · subplots · подписи ·
сохранение · Seaborn: heatmap/pairplot · честная визуализация
2 ч теории. Презентация — 30 мин мини-лекция, далее live-coding
и задачи семинара. Данные — titanic.csv (модуль 2).
Прежде чем начать: откройте Jupyter и загрузите
titanic.csv через pd.read_csv — сегодня мы впервые
увидим свои данные.
График — язык отчётов и научных статей
- Визуализация — требование всех downstream-дисциплин:
Анализ данных, мат. статистика, ML, NLP.
- Ваш EDA из модуля 2 станет видимым: «посчитали
groupby —
теперь покажите это глазами».
- В компаниях любой отчёт начинается с графиков; в науке статья без
рисунков не проходит ревью.
- Seaborn и Matplotlib — стандарт де-факто научного стека Python.
Какие данные вы считали в модуле 2? Как бы вы показали
«средний возраст по классу каюты» человеку, который не читает таблицы?
Два API Matplotlib: pyplot и объектный
import matplotlib.pyplot as plt
plt.plot(x, y) # «быстрый» стиль
plt.title('...')
plt.show()
fig, ax = plt.subplots() # объектный
ax.plot(x, y)
ax.set_title('...')
fig.savefig('plot.png')
- pyplot (
plt.plot) — «последняя активная ось»;
годится для интерактива и консоли.
- Объектный API (
fig, ax) — полный контроль:
несколько осей, подписи, сохранение. Предпочтителен для задач модуля.
Почему plt.xlabel(...) после
fig, ax = plt.subplots() — смешение стилей и источник ошибок?
Анатомия рисунка: Figure и Axes
- Figure — «холст» целиком (весь рисунок, можно несколько осей).
- Axes — один подграфик со своими осями, подписями, линиями.
fig, axes = plt.subplots(2, 2, figsize=(9.5, 7))
# axes — массив 2×2; figsize — размер холста в дюймах
axes[0, 0].set_title('левый верхний')
axes[1, 1].set_title('правый нижний')
Одна Figure — много Axes. Каждый Axes можно настраивать
независимо: title, xlabel, ylabel, legend, grid.
Сколько «графиков» в plt.subplots(2, 3) и
какая форма у axes?
plot и scatter: тренд и связь двух величин
fig, ax = plt.subplots()
ax.scatter(df['Age'], df['Fare'],
s=40, alpha=0.75, color='#2563eb')
ax.set_xlabel('возраст, лет')
ax.set_ylabel('цена билета, у.е.')
ax.set_title('Возраст и цена билета')
ax.grid(alpha=0.3)
plot — для временны́х рядов и трендов;
scatter — для связи двух количественных величин.
Какая закономерность видна на графике: возраст и цена
связаны? Почему нельзя утверждать «цена растёт с возрастом»?
bar: сравнение категорий — с осью Y от нуля
rate = df.groupby('Pclass')['Survived'].mean()
fig, ax = plt.subplots()
ax.bar([1, 2, 3], rate, color='#059669')
ax.set_ylim(0, 1) # честная ось с нуля
ax.set_xticks([1, 2, 3])
ax.set_xlabel('класс каюты')
ax.set_ylabel('доля выживших')
ax.set_title('Выживаемость по классу')
Правило: у bar-диаграммы ось Y начинается с нуля. Обрезанная
ось set_ylim(0.2, 0.5) «раздувает» разницу между столбцами.
Почему set_ylim(0.3, 0.5) на этом графике —
манипуляция? Что она скрывает?
hist и boxplot: распределение одной величины
fig, ax = plt.subplots()
ax.hist(df['Age'].dropna(), bins=12,
color='#d97706', edgecolor='white')
ax.set_xlabel('возраст, лет')
ax.set_ylabel('число пассажиров')
ax.set_title('Распределение возраста')
- hist — форма распределения, мода, асимметрия.
- boxplot — медиана, квартили, выбросы.
Удобен для сравнения групп (например, возраст по полу).
Сколько пассажиров младше ~20 лет? Как это понять из
гистограммы?
Subplots 2×2: сетка осей, часть 1
fig, axes = plt.subplots(2, 2, figsize=(9.5, 7))
axes[0,0].hist(df['Age'].dropna(), bins=10, color='#d97706', edgecolor='white')
axes[0,0].set_title('Распределение возраста')
axes[0,0].set_xlabel('возраст, лет'); axes[0,0].set_ylabel('число')
rate = df.groupby('Pclass')['Survived'].mean()
axes[0,1].bar([1, 2, 3], rate, color='#059669')
axes[0,1].set_ylim(0, 1); axes[0,1].set_xticks([1, 2, 3])
axes[0,1].set_title('Выживаемость по классу')
axes[0,1].set_xlabel('класс'); axes[0,1].set_ylabel('доля выживших')
Сетка создаётся одним вызовом; каждая ось настраивается
независимо по индексу axes[i, j]. Продолжение — следующий слайд.
Какой график вы разместите в axes[1, 0],
если нужно показать связь возраста и цены билета?
…и заполняем оставшиеся оси (продолжение)
axes[1,0].scatter(df['Age'], df['Fare'],
s=30, alpha=0.7, color='#2563eb')
axes[1,0].set_title('Возраст × цена билета')
axes[1,0].set_xlabel('возраст, лет'); axes[1,0].set_ylabel('цена, у.е.')
sns.boxplot(data=df, x='Sex', y='Age', ax=axes[1,1])
axes[1,1].set_title('Возраст по полу')
axes[1,1].set_xlabel('пол'); axes[1,1].set_ylabel('возраст, лет')
fig.tight_layout()
fig.savefig('figures/dashboard.png', dpi=110)
Итог — дашборд из четырёх подписанных графиков. Это артефакт
недели: так же вы соберёте EDA-ноутбук в проекте модуля.
Зачем нужен fig.tight_layout() перед
сохранением?
Подписи, легенда, сетка — обязательная часть
- Каждый график имеет
set_title,
set_xlabel, set_ylabel — с единицами:
«возраст, лет», «цена, у.е.».
- Легенда — только если серий больше одной; для неё нужен
label= в каждой серии + ax.legend().
ax.grid() — сетка для чтения значений; ax.set_xticks
/ set_xlim — контроль шкалы.
fig.suptitle — общий заголовок всего рисунка.
ax.legend()
ax.grid(alpha=0.3)
ax.set_xticks([1, 2, 3])
Чем ax.legend() без label=
отличается от легенды с заданными метками? Что увидит студент?
Сохранение: savefig — только до show
fig, ax = plt.subplots()
ax.plot(df.index, df['Fare'], color='#7c3aed')
ax.set_title('Цена билета по списку')
ax.set_xlabel('пассажир (номер)'); ax.set_ylabel('цена, у.е.')
fig.savefig('figures/fare.png', dpi=150, bbox_inches='tight') # ДО show!
fig.savefig('fare.png') # после plt.show()
# → пустой/белый файл: figure уже закрыт
- Порядок всегда: настроили → сохранили → показали.
dpi=150 — качество; bbox_inches='tight' — без
полей; pdf — векторный, для отчётов.
Как проверить, что файл сохранился, не открывая его
глазами?
Честная визуализация: масштаб не должен врать
- Оба графика построены на одних данных
(число выживших по классам).
- Слева
set_ylim(4, 9) — столбцы «почти равны», но
разрыв визуально утроен.
- Справа ось с нуля — честная картина: 1-й класс ≈ 5, 3-й ≈ 8.
Правила честности: бары с нуля; не обрезать ось без маркера
разрыва; не скрывать выбросы; подписывать единицы. Полный разбор — неделя 8.
Какую историю рассказывает обрезанный график, а какую —
честный? Кто из читателей поверит первому?
Seaborn: heatmap корреляций
import seaborn as sns
fig, ax = plt.subplots(figsize=(6.2, 4.6))
sns.heatmap(df[['Age', 'Fare', 'Pclass', 'Survived']].corr(),
annot=True, fmt='.2f', cmap='coolwarm', ax=ax)
ax.set_title('Корреляции признаков Titanic')
df.corr() — матрица парных корреляций числовых колонок.
annot=True — значения в ячейках; цвет — знак и сила связи.
- Вывод по нашим данным: Fare и Survived коррелируют слабо (r ≈ −0.22).
Почему в df.corr() не попали колонки Name и
Sex? Что с ними сделать, чтобы учесть?
Seaborn: boxplot и pairplot из DataFrame
sns.boxplot(data=df, x='Sex', y='Age')
sns.pairplot(df[['Age', 'Fare', 'Pclass', 'Survived']].dropna(),
hue='Survived', diag_kind='hist')
Seaborn — «статистическая надстройка» над Matplotlib: принимает
DataFrame напрямую, добавляет стиль по умолчанию и сразу рисует группировку
по hue.
Какой вопрос к данным можно быстро ответить, глядя на
pairplot с hue='Survived'?
Типичные ошибки недели 7
- Неподписанные графики — нет title/xlabel/ylabel. График
без подписей не считается сданным (чек-лист ревью).
- savefig после show — пустой/белый файл.
- Смешение стилей:
plt.xlabel после создания
axes.
- Легенда без меток —
label= не задан.
- Обрезанная ось Y у bar — «раздутая» разница между категориями.
df.corr().plot() и sns.heatmap(df.corr()) без
понимания, что корреляция — по числовым колонкам.
- График строится в цикле, но все линии рисуются в одни оси — нужны новые
subplot или цвет/метка каждой серии.
Студент показал график с заголовком, но без подписей
осей. Примете ли вы его как «сданный»? Что скажете?
Вопросы для проверки понимания
- Зачем нужен объект
Axes и чем ax.plot лучше
plt.plot?
- Как создать сетку 2×2 и подписать каждый график?
- Как сохранить график в PNG высокого качества (и почему именно до show)?
- Почему нельзя обрезать ось Y у bar-диаграммы?
- Какие колонки попадают в
df.corr() и что показывает heatmap?
Ответьте письменно за 3 минуты — это мини-самооценка перед
семинаром.
Переход: на семинаре вы соберёте дашборд 2×2, тепловую
карту и научитесь «чинить» манипулирующие графики.
Что дальше
- Семинар недели 7: три уровня задач — «Разбор интерфейса»
(базовый), «Тепловая карта + Seaborn» (стандартный), «Дашборд 2×2 +
Challenge» (продвинутый).
- ДЗ недели 7: визуализировать EDA своего датасета из
модуля 2 — 3+ графика и heatmap корреляций (autograder + чек-лист).
- К концу недели вы умеете строить подписанные честные
графики и сохранять их в файл.
Готовы? Откройте семинар-7.md, скопируйте titanic.csv в
data/ и выполните базовую задачу.