Перейти к содержанию

252 уроков, 17 библиотек и челлендж «Что выведет код?» — бесплатно, код прямо в браузере

Начать обучение
Урок 12 из 20 Средний 30 мин 140 XP

heatmap в Seaborn: тепловая карта матрицы и корреляции

Матрица превращается в картинку: корреляции признаков и активность по дням-часам загораются цветом. annot, fmt, cmap, vmin и vmax — четыре ручки тепловой карты.

Редакция Питоники

Некоторые данные сами просятся в таблицу: посещения клуба по дням недели и часам, корреляции между признаками, расписание занятости. Семь строк на шесть колонок — сорок два числа, и глазу нужна минута, чтобы найти максимум. heatmap («тепловая карта») перекладывает эту работу на цвет: каждая ячейка таблицы получает оттенок пропорционально величине, и горячие зоны находятся за секунду.

Построим тепловую карту активности, разберём аннотации и формат чисел, а затем — второй классический сценарий: корреляционную матрицу df.corr() с дивергентной палитрой и фиксированной шкалой.

Активность по дням и часам

Сеть фитнес-клубов считает посещения: по строкам дни недели, по колонкам часы суток. Данные уже сведены в матрицу — DataFrame с индексом-днями и колонками-часами:

Посещения клуба: дни против часов
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

visits = pd.DataFrame(
    [[14, 8, 5, 9, 22, 30],
     [12, 7, 4, 8, 20, 28],
     [16, 9, 6, 10, 25, 33],
     [15, 8, 5, 9, 24, 31],
     [20, 12, 8, 14, 38, 55],
     [34, 28, 22, 30, 52, 61],
     [30, 25, 18, 26, 47, 58]],
    index=["Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"],
    columns=["9:00", "12:00", "15:00", "18:00", "21:00", "23:00"],
)

sns.set_theme()
ax = sns.heatmap(visits, annot=True, fmt="d", cmap="YlGnBu")
ax.set_title("Посещения клуба: тёплые зоны видны сразу")
plt.show()
Вывод
График отображается под редактором
Первый запуск загружает pandas и seaborn — это займёт до минуты

Выводы читаются с одного взгляда: выходные — два самых жарких ряда, вечер 21:00 — самый горячий столбец, а полдень вторника — ледяная ячейка. Тепловая карта делает незаметное видимым: сорок два числа таблицы превращаются в картинку, где горячие зоны светятся сами. Маркетинг клуба получит расписание акций без единого фильтра в Excel.

annot и fmt: числа в ячейках

Параметр annot=True подписывает каждую ячейку значением, а fmt задаёт формат числа: "d" — целое, ".1f" и ".2f" — один и два знака после запятой. Без annot карта остаётся чистым цветом — красиво, но нечитаемо точно; с annot график самодостаточен и в отчёте, и на печати.

Матрица корреляций: df.corr()

Второй классический сценарий — разведочный анализ: какие признаки вообще связаны. Возьмём неделю работы магазина: рекламный бюджет, посетители и выручка по дням. Метод df.corr() возвращает таблицу парных корреляций — чисел от -1 до 1, где 1 означает идеальную прямую связь, -1 — идеальную обратную, а 0 — отсутствие линейной связи:

Корреляции магазина: сначала числа
import pandas as pd

shop = pd.DataFrame({
    "ads": [12, 18, 25, 31, 40, 47, 55, 62],
    "visitors": [130, 165, 240, 255, 350, 380, 470, 505],
    "sales": [95, 140, 205, 230, 300, 345, 400, 455],
})

corr = shop.corr()
print(corr)
Вывод
               ads  visitors     sales
ads       1.000000  0.995941  0.998877
visitors  0.995941  1.000000  0.996866
sales     0.998877  0.996866  1.000000

Читаем таблицу. Диагональ всегда единицы — признак коррелирует сам с собой идеально. Матрица симметрична: связь ads-visitors и visitors-ads — одно число. Вне диагонали три числа близки к 1: реклама, трафик и выручка в этой неделе идут рука об руку. Вытащим одну ячейку поименованно — так удобнее в коде отчёта:

Одна корреляция из ячейки матрицы
import pandas as pd

shop = pd.DataFrame({
    "ads": [12, 18, 25, 31, 40, 47, 55, 62],
    "visitors": [130, 165, 240, 255, 350, 380, 470, 505],
    "sales": [95, 140, 205, 230, 300, 345, 400, 455],
})

corr = shop.corr()
print(round(corr.loc["ads", "sales"], 2))
Вывод
1.0

Округление до двух знаков даёт 1.0: связь бюджета с выручкой почти функциональная — 0.998877, как показала полная матрица. В реальных данных такие числа встречаются редко и заслуживают проверки: а не дублируют ли колонки друг друга?

heatmap корреляций: дивергентная палитра и vmin/vmax

Корреляция живёт на шкале от -1 до 1, и для неё нужна палитра с бледной серединой: слабые связи бледнеют, сильные прямые и обратные — загораются двумя цветами. Заодно фиксируем шкалу параметрами vmin=-1 и vmax=1, чтобы цвета были сравнимы между разными матрицами:

Корреляционная матрица в цвете
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

shop = pd.DataFrame({
    "ads": [12, 18, 25, 31, 40, 47, 55, 62],
    "visitors": [130, 165, 240, 255, 350, 380, 470, 505],
    "sales": [95, 140, 205, 230, 300, 345, 400, 455],
})

sns.set_theme()
corr = shop.corr()
ax = sns.heatmap(corr, annot=True, fmt=".2f",
                 cmap="RdBu_r", vmin=-1, vmax=1, center=0)
ax.set_title("Корреляции магазина: красное минус, синее плюс")
plt.show()
Вывод
График отображается под редактором

fmt=".2f" округлил числа до двух знаков — в ячейках ровно столько точности, сколько читается. center=0 поставил бледный цвет в ноль шкалы. Без vmin и vmax Seaborn растянул бы палитру на фактический диапазон данных, и в матрице без отрицательных связей даже слабая корреляция выглядела бы насыщенно-красной — сравнивать две такие карты между собой было бы нельзя.

Параметры vmin= и vmax= работают и на картах величин: явные границы удобны, когда данные имеют понятный потолок — проценты от 0 до 100 или число мест в зале. С ними две тепловые карты за разные недели сравниваются честно, ячейка в ячейку.

Шкала закреплена: от нуля до вместимости зала
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

visits = pd.DataFrame(
    [[14, 8, 5, 9, 22, 30],
     [12, 7, 4, 8, 20, 28],
     [16, 9, 6, 10, 25, 33],
     [15, 8, 5, 9, 24, 31],
     [20, 12, 8, 14, 38, 55],
     [34, 28, 22, 30, 52, 61],
     [30, 25, 18, 26, 47, 58]],
    index=["Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"],
    columns=["9:00", "12:00", "15:00", "18:00", "21:00", "23:00"],
)

sns.set_theme()
ax = sns.heatmap(visits, annot=True, fmt="d",
                 cmap="YlGnBu", vmin=0, vmax=70)
ax.set_title("Шкала от 0 до 70: цвет сравним с прошлой неделей")
plt.show()
Вывод
График отображается под редактором

cmap: какой набор цветов взять

Параметр cmap= принимает те же наборы, что мы разбирали в уроке про палитры, и выбор снова определяется смыслом цвета:

ДанныеТип палитрыПримеры cmap
Величина «от малого к большому»ПоследовательнаяYlGnBu, Blues, viridis
Отклонение от серединыДивергентнаяRdBu_r, coolwarm, PiYG
Особая точка данныхКатегориальная не подходит: heatmap кодирует величину—

Для посещений клуба подошла последовательная YlGnBu: больше visits — темнее клетка. Для корреляций — дивергентная RdBu_r: два смысла по обе стороны нуля. Проверим границы наших данных числом — они и попадут в края палитры:

Края шкалы активности
import pandas as pd

visits = pd.DataFrame(
    [[14, 8, 5, 9, 22, 30],
     [12, 7, 4, 8, 20, 28],
     [16, 9, 6, 10, 25, 33],
     [15, 8, 5, 9, 24, 31],
     [20, 12, 8, 14, 38, 55],
     [34, 28, 22, 30, 52, 61],
     [30, 25, 18, 26, 47, 58]],
    index=["Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"],
    columns=["9:00", "12:00", "15:00", "18:00", "21:00", "23:00"],
)

print(visits.values.min(), visits.values.max())
Вывод
4 61

От четырёх посещений в полдень вторника до 61 в воскресенье ночью — именно эти ячейки станут крайними точками шкалы цвета. Промежуточные оттенки распределятся линейно между ними; адресовать отдельную ячейку можно через .loc, как мы делали с корреляцией.

Сводная таблица перед heatmap

В реальных проектах данные чаще лежат длинным форматом: строка на каждое посещение с колонками day, hour, visits. Тепловой карте нужна широкая матрица — и её собирает pivot_table, знакомый по уроку про группировку Pandas: pv = df.pivot_table(index="day", columns="hour", values="visits", aggfunc="sum"). Дальше — знакомый вызов sns.heatmap(pv). Единственная ловушка уже описана выше: если в ячейках появились NaN, fmt="d" упадёт, и понадобится либо fillna(0), либо fmt=".0f".

Что дальше

Тепловая карта закрыла таблицы: матрица активности и корреляции признаков читаются в цвете. Следующий шаг — pairplot: сетка диаграмм рассеяния по всем парам числовых колонок сразу, разведочный анализ всего набора данных двумя строками. Вместе они — стандартный старт любого нового датасета.

Тепловая карта делает незаметное видимым: сорок два числа таблицы превращаются в картинку, где горячие зоны светятся сами.

Что выведет код?

Сначала предскажи ответ в голове — это главный навык программиста.

import pandas as pd

df = pd.DataFrame({
    "x": [1, 2, 3, 4],
    "y": [2, 4, 6, 8],
})
print(df["x"].corr(df["y"]))
import pandas as pd

df = pd.DataFrame({
    "x": [1, 2, 3, 4],
    "y": [8, 6, 4, 2],
})
print(df["x"].corr(df["y"]))
import pandas as pd

visits = pd.DataFrame(
    [[14, 8],
     [12, 7],
     [16, 9]],
    index=["Пн", "Вт", "Ср"],
    columns=["9:00", "21:00"],
)
print(visits.shape)
Проверь себя
0 / 5

1. Что ожидает sns.heatmap на входе?

2. Зачем в корреляционной heatmap параметры vmin=-1 и vmax=1?

3. Почему на диагонали корреляционной матрицы стоят единицы?

4. Код упал с ValueError: Unknown format code 'd' for object of type 'float'. Что произошло?

5. Какую палитру логично взять для heatmap корреляций от -1 до 1?

Карточки терминов
Запомнено: 0 / 6
Практика

В редакторе — таблица цены товара и спроса за шесть недель. Посчитайте корреляции df.corr() и выведите одним числом связь колонок price и demand, округлённую до двух знаков после запятой.

practice.py
Вопросы и ответы по уроку

Что показывает sns.heatmap?

Тепловая карта перекрашивает матрицу в цвет: каждая ячейка DataFrame получает оттенок по величине. Классические сценарии — активность по дням и часам, корреляционная матрица df.corr(), расписание загрузки. annot=True добавляет числа в ячейки, cmap выбирает набор цветов.

Урок 12: heatmap — тепловая карта

Как построить матрицу корреляций в Python?

Две строки: corr = df.corr() считает парные корреляции числами от -1 до 1, затем sns.heatmap(corr, annot=True, cmap="RdBu_r", vmin=-1, vmax=1) рисует их в цвете. Дивергентная палитра и фиксированная шкала обязательны: сильная обратная связь должна красным отличаться от слабой.

Почему heatmap выдаёт ValueError с fmt="d"?

Формат "d" работает только с целыми числами. Дробные значения или пропуски NaN в матрице вызывают ValueError: Unknown format code 'd' for object of type 'float'. Используйте fmt=".0f" или приведите таблицу к целым: df.astype(int).

Диагональ корреляционной матрицы всегда единичная — это нормально?

Да: признак коррелирует сам с собой идеально, поэтому на диагонали единицы, а матрица симметрична. На heatmap диагональ часто скрывают (np.fill_diagonal или маска), чтобы взгляд цеплялся за связи между разными признаками.

Понравился урок? Сошлитесь на него

«Тепловая карта делает незаметное видимым: сорок два числа таблицы превращаются в картинку, где горячие зоны светятся сами.»

Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.

TelegramVK

Похожие уроки по темам

Подобраны автоматически по пересечению тем и ключевых слов.