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

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

Начать обучение
Урок 9 из 20 Средний 25 мин 110 XP

hue в Seaborn: цвет как третье измерение графика

hue разбивает график по третьей колонке таблицы: точки, столбцы и линии раскрашиваются по группам, а легенда строится сама. Разбираем scatterplot, barplot, lineplot и hue_order.

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

Две колонки таблицы легко становятся осями X и Y: бюджет против продаж, площадь против цены. Но настоящие таблицы шире: у каждой строки есть ещё канал, город, категория — и сравнивать группы хочется на одном графике, а не на трёх отдельных. Для этого в Seaborn есть параметр hue=. hue добавляет третье измерение, раскрасив данные по значениям ещё одной колонки, — легенда и цвета появляются сами. Набор цветов при этом берётся из палитры, которую мы разбирали в прошлом уроке.

План урока: hue в scatterplot и lineplot, связь hue с группировкой pandas, hue_order для порядка групп, перенос легенды и две типовые ловушки — числовая колонка в hue и слишком много групп.

hue в scatterplot: три канала на одной картинке

Сквозной пример урока — рекламные каналы интернет-магазина. По каждой кампании известны бюджет и выручка; колонка channel делит их на поиск, соцсети и email:

Бюджет против продаж с разбивкой по каналам
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

ads = pd.DataFrame({
    "channel": ["поиск", "поиск", "соцсети", "соцсети", "email", "email",
                "поиск", "соцсети", "email", "поиск", "соцсети", "email"],
    "budget": [30, 45, 25, 40, 15, 22, 60, 55, 30, 50, 70, 18],
    "sales": [310, 420, 260, 390, 140, 205, 505, 470, 290, 430, 590, 175],
})

sns.set_theme()
ax = sns.scatterplot(data=ads, x="budget", y="sales", hue="channel")
ax.set_title("Реклама: бюджет и выручка по каналам")
ax.set_xlabel("Бюджет, тыс. руб.")
ax.set_ylabel("Выручка, тыс. руб.")
plt.show()
Вывод
График отображается под редактором
Первый запуск загружает pandas и seaborn — это займёт до минуты

Один вызов — и три облака точек читаются раздельно: поиск и соцсети тянутся вдоль общей диагонали (больше бюджет — больше выручка), а email лежит заметно ниже при тех же бюджетах. Без hue три канала смешались бы в одно облако, и этот вывод пришлось бы добывать перебором фильтров.

Что hue делает под капотом: группировка

hue — это группировка pandas, нарисованная цветом: Seaborn разрезает таблицу по значениям колонки и рисует каждую часть своим слоем. Проверим числами то же самое через groupby из урока про группировку Pandas:

Средняя выручка каналов: то же, что на графике
import pandas as pd

ads = pd.DataFrame({
    "channel": ["поиск", "поиск", "соцсети", "соцсети", "email", "email",
                "поиск", "соцсети", "email", "поиск", "соцсети", "email"],
    "sales": [310, 420, 260, 390, 140, 205, 505, 470, 290, 430, 590, 175],
})

means = ads.groupby("channel")["sales"].mean().round(1)
print(means.tolist())
print(ads["channel"].nunique())
Вывод
[202.5, 416.2, 427.5]
3

Три канала, и email отстаёт в среднем вдвое — ровно то, что показывает цвет на scatterplot. Разница в том, что groupby выдаёт одно число на группу, а hue сохраняет каждую точку: видны и средние, и разброс вокруг них, и выбросы.

hue в barplot и lineplot: один параметр везде

hue работает почти во всех функциях Seaborn и всегда означает одно и то же: «разбей по этому столбцу и раскрась». В barplot группы встают столбцами рядом — удобно сравнивать месяцы между городами:

Средняя выручка по месяцам с разбивкой по городам
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

months = pd.DataFrame({
    "month": ["Май", "Июнь", "Июль", "Август",
              "Май", "Июнь", "Июль", "Август"],
    "city": ["Москва", "Москва", "Москва", "Москва",
             "Сочи", "Сочи", "Сочи", "Сочи"],
    "revenue": [420, 510, 590, 480, 610, 780, 920, 860],
})

sns.set_theme()
ax = sns.barplot(data=months, x="month", y="revenue", hue="city")
ax.set_title("Выручка по месяцам: Москва против Сочи")
ax.set_ylabel("Выручка, тыс. руб.")
plt.show()
Вывод
График отображается под редактором

В lineplot hue рисует по линии на каждую группу — классический способ сравнить два города или две модели на одной временной шкале:

Температура двух городов одной картинкой
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

temps = pd.DataFrame({
    "day": [1, 2, 3, 4, 5, 1, 2, 3, 4, 5],
    "city": ["Сочи"] * 5 + ["Мурманск"] * 5,
    "temp": [24, 25, 27, 26, 28, 12, 11, 14, 13, 15],
})

sns.set_theme()
ax = sns.lineplot(data=temps, x="day", y="temp", hue="city", marker="o")
ax.set_title("Дневная температура: две линии вместо двух графиков")
ax.set_xlabel("День наблюдений")
ax.set_ylabel("Температура, C")
plt.show()
Вывод
График отображается под редактором

Линии идут почти параллельно: разница между городами держится около 12 градусов каждый день. Параллельность — признак постоянной разницы; если бы линии сближались или пересекались, это значило бы, что разрыв меняется во времени. В scatterplot и lineplot hue ещё и добавляет histplot разрез по распределениям — в четвёртом уроке мы уже видели, как два гистограммных слоя сравниваются по цвету.

hue_order: управляем порядком групп

По умолчанию Seaborn сортирует группы по значениям колонки — текстовые категории встают по алфавиту. Если нужен свой порядок (например, от меньшего тарифа к большему), его фиксирует hue_order=:

Заказы кофейни: порядок напитков задан руками
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

orders = pd.DataFrame({
    "day": ["Пн", "Пн", "Вт", "Вт", "Ср", "Ср", "Ср",
            "Чт", "Чт", "Пт", "Пт", "Пт"],
    "drink": ["латте", "капучино", "эспрессо", "латте", "капучино", "латте",
              "эспрессо", "капучино", "латте", "эспрессо", "латте", "капучино"],
})

sns.set_theme()
ax = sns.countplot(
    data=orders, x="day", hue="drink",
    hue_order=["эспрессо", "капучино", "латте"],
)
ax.set_title("Напитки по дням: порядок в легенде задан hue_order")
plt.show()
Вывод
График отображается под редактором

hue_order меняет и порядок столбцов внутри дня, и порядок строк легенды. Убедимся, что легенда запомнила заданный порядок — вытащим её подписи из объекта осей:

Подписи легенды в порядке hue_order
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

orders = pd.DataFrame({
    "day": ["Пн", "Пн", "Вт", "Вт", "Ср", "Ср", "Ср",
            "Чт", "Чт", "Пт", "Пт", "Пт"],
    "drink": ["латте", "капучино", "эспрессо", "латте", "капучино", "латте",
              "эспрессо", "капучино", "латте", "эспрессо", "латте", "капучино"],
})

ax = sns.countplot(
    data=orders, x="day", hue="drink",
    hue_order=["эспрессо", "капучино", "латте"],
)
print([t.get_text() for t in ax.get_legend().get_texts()])
Вывод
['эспрессо', 'капучино', 'латте']

Порядок совпал с заданным: алфавит уступил месту бизнес-логике. Тот же параметр у обычного order= управляет категориями оси X — оба принимают список значений в нужной последовательности.

Чтение легенды и её перенос

Легенда при hue создаётся автоматически: заголовком становится имя колонки, подписями — её значения. Читатель сопоставляет цвет с группой по легенде, поэтому её стоит ставить так, чтобы она не накрывала данные. За перенос отвечает sns.move_legend():

Легенда туда, где пусто
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

ads = pd.DataFrame({
    "channel": ["поиск", "поиск", "соцсети", "соцсети", "email", "email",
                "поиск", "соцсети", "email", "поиск", "соцсети", "email"],
    "budget": [30, 45, 25, 40, 15, 22, 60, 55, 30, 50, 70, 18],
    "sales": [310, 420, 260, 390, 140, 205, 505, 470, 290, 430, 590, 175],
})

sns.set_theme()
ax = sns.scatterplot(data=ads, x="budget", y="sales", hue="channel")
sns.move_legend(ax, "upper left", title="Канал")
ax.set_title("Легенда переехала в свободный угол")
plt.show()
Вывод
График отображается под редактором

Первый аргумент — объект осей, второй — позиция: upper left, lower right, center и другие. Аргумент title= переименовал заголовок легенды: имя колонки channel из данных редко читается как подпись, а «Канал» — читается. Тонкости легенд Matplotlib разобраны в шестом уроке про оформление, и они применимы к Seaborn один в один.

Что дальше

hue — самый ходовой параметр библиотеки после data=: он в одну строку превращает одиночный график в сравнение групп. Со следующего урока он станет регулярным гостем: boxplot использует его для пар ящиков, а violinplot — для split-скрипок, где две группы делят одну фигуру пополам.

hue добавляет третье измерение, раскрасив данные по значениям ещё одной колонки, — легенда и цвета появляются сами.

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

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

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

df = pd.DataFrame({
    "day": ["Пн", "Пн", "Вт", "Вт"],
    "drink": ["чай", "кофе", "чай", "кофе"],
})
ax = sns.countplot(data=df, x="day", hue="drink")
print(len(ax.containers))
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

df = pd.DataFrame({
    "day": ["Пн", "Пн", "Вт", "Вт"],
    "drink": ["чай", "кофе", "чай", "кофе"],
})
ax = sns.countplot(data=df, x="day", hue="drink", hue_order=["кофе", "чай"])
print([t.get_text() for t in ax.get_legend().get_texts()])
import pandas as pd

df = pd.DataFrame({
    "city": ["Москва", "Казань", "Москва", "Казань"],
    "sales": [10, 20, 30, 40],
})
print(df.groupby("city")["sales"].sum().tolist())
Проверь себя
0 / 5

1. Что делает параметр hue="city" в sns.lineplot?

2. Какой порядок групп hue выберет по умолчанию?

3. Почему hue="price" с числовой колонкой — плохая идея?

4. Что делает sns.move_legend(ax, "upper left")?

5. Чем связан hue с groupby из pandas?

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

В редакторе — кампании интернет-магазина: budget (бюджет, тыс. руб.) и sales (выручка, тыс. руб.). Постройте scatterplot с разбивкой hue="channel", палитрой colorblind и легендой в верхнем левом углу с заголовком «Канал».

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

Что такое hue в Seaborn простыми словами?

Это параметр, который раскрашивает график по значениям ещё одной колонки таблицы: каждая группа получает свой цвет, а легенда строится автоматически. sns.scatterplot(data=df, x="budget", y="sales", hue="channel") покажет все каналы на одной картинке — это третье измерение графика без единой строки группировки.

Урок 9: hue как третье измерение

Как задать свой порядок групп в легенде?

Параметром hue_order=: передайте список значений в нужной последовательности, и он применится и к цветам на графике, и к строкам легенды. Для категорий самой оси X аналогично работает order=. По умолчанию Seaborn сортирует группы по значениям колонки — текстовые по алфавиту.

Как переместить легенду на графике Seaborn?

sns.move_legend(ax, "upper left") — первым аргументом объект осей, вторым позиция (upper left, lower right, center и т.д.). Там же меняется заголовок: sns.move_legend(ax, "upper left", title="Канал"). Легенда при hue создаётся сама, поэтому обычно её только переносят.

Сколько групп выдерживает hue?

Комфортно — три-четыре: глаз удерживает столько оттенков одновременно. При большем числе столбцы сужаются, легенда разрастается, а цвета повторяются. Сводите категории до осмысленных групп через pd.cut() или переходите к малым кратным — сетке графиков FacetGrid, по панели на группу.

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

«hue добавляет третье измерение, раскрасив данные по значениям ещё одной колонки, — легенда и цвета появляются сами.»

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

TelegramVK

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

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