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

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

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

jointplot в Seaborn: связь двух величин и распределения по краям

jointplot рисует связь двух величин в центре и их распределения по краям: сгущения и выбросы видны одновременно. kind="kde" и kind="hex" для плотных данных.

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

Scatterplot из пятого урока отвечает на вопрос «связаны ли величины» облаком точек. Но у внимательного читателя сразу два вопроса: как связаны — и как каждая величина распределена сама по себе. Pairplot прошлой минуты рисует все пары сразу, но мелко. jointplot — крупный план одной пары: диаграмма рассеяния в центре и распределения обеих величин по краям той же картинки.

Строим первый jointplot, разбираемся с JointGrid, добавляем подписи, пробуем kind="kde" и kind="hex" и формулируем, когда этот график лучше обычного scatter.

Первый jointplot: часы подготовки и балл

Сквозной пример — выпускники курса: часы подготовки против балла за экзамен. Один вызов — и три панели вместо трёх отдельных графиков:

Часы и балл: scatter с распределениями
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

exam = pd.DataFrame({
    "hours": [2, 3, 4, 5, 6, 7, 8, 9, 10, 12],
    "score": [45, 52, 58, 65, 70, 74, 80, 84, 88, 95],
})

sns.set_theme()
g = sns.jointplot(data=exam, x="hours", y="score")
plt.show()
Вывод
График отображается под редактором
Первый запуск загружает pandas и seaborn — это займёт до минуты

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

JointGrid: это не Axes

jointplot возвращает не оси, а объект JointGrid — контейнер из трёх осей: центральной ax_joint и двух краевых ax_marg_x и ax_marg_y. Проверим:

Что возвращает jointplot
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

exam = pd.DataFrame({
    "hours": [2, 3, 4, 5, 6, 7, 8, 9, 10, 12],
    "score": [45, 52, 58, 65, 70, 74, 80, 84, 88, 95],
})

g = sns.jointplot(data=exam, x="hours", y="score")
print(type(g).__name__)
print(len(g.figure.axes))
Вывод
JointGrid
3

JointGrid, и на фигуре три оси — центральная и две краевые гистограммы. Из этого следует всё остальное: у JointGrid свои методы доводки, а к центральной панели можно обращаться напрямую, как к обычным осям Matplotlib.

Доводка: подписи и заголовок

Подписи через g.set_axis_labels(), заголовок центральной панели — привычным методом у g.ax_joint:

Готовый к отчёту jointplot
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

exam = pd.DataFrame({
    "hours": [2, 3, 4, 5, 6, 7, 8, 9, 10, 12],
    "score": [45, 52, 58, 65, 70, 74, 80, 84, 88, 95],
})

sns.set_theme()
g = sns.jointplot(data=exam, x="hours", y="score")
g.set_axis_labels("Часы подготовки", "Баллы")
g.ax_joint.set_title("Экзамен: чем больше часов, тем выше балл")
plt.show()
Вывод
График отображается под редактором

Через ax_joint доступна вся техника Matplotlib: сетка, аннотации, сохранение. Есть и встречное движение — метод g.plot_joint(sns.regplot) дорисует в центр линию регрессии; ей посвящён шестнадцатый урок про regplot, а следующий, пятнадцатый, отдаёт сеткам категориальных графиков — catplot.

kind="kde": плавные контуры

Аргумент kind= меняет тип центрального и краевых графиков. Значение "kde" рисует сглаженные контуры плотности — облако точек превращается в топографическую карту:

Плотность вместо точек
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

np.random.seed(42)
hours = np.random.normal(6, 2, 300).clip(0.5, 12)
score = 40 + 5 * hours + np.random.normal(0, 6, 300)
exam = pd.DataFrame({"hours": hours.round(1), "score": score.round(1)})

sns.set_theme()
g = sns.jointplot(data=exam, x="hours", y="score", kind="kde",
                  fill=True, cmap="Blues")
g.set_axis_labels("Часы подготовки", "Баллы")
plt.show()
Вывод
График отображается под редактором

Контуры сгущаются к центру массы — самый тёмный бугор показывает, где сидит типичный выпускник: около шести часов и семидесяти баллов. Краевые кривые тоже стали KDE — тот же вид плотности, что в уроке про violinplot. Для трёхсот точек это честная картина; для десяти наблюдений KDE нарисовал бы горы из ничего.

kind="hex": соты для плотных данных

На тысячах точек scatter перестаёт читаться: точки ложатся друг на друга, и график чернеет — это оверплоттинг из четвёртого урока Matplotlib. Лечение — kind="hex": плоскость делится на шестиугольные ячейки, цвет показывает число точек в ячейке:

Три тысячи точек в сотах
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

np.random.seed(42)
ages = np.random.normal(45, 12, 3000).clip(18, 80)
spend = 300 + 8 * ages + np.random.normal(0, 60, 3000)
mall = pd.DataFrame({"age": ages.round(0), "spend": spend.round(0)})

sns.set_theme()
g = sns.jointplot(data=mall, x="age", y="spend", kind="hex")
g.set_axis_labels("Возраст посетителя", "Средний чек, руб.")
plt.show()
Вывод
График отображается под редактором

Тёмные соты — сгущения, светлые — редкие зоны; краевые гистограммы остались на месте. Мини-наблюдение за один взгляд: скопление аудитории 35–55 лет с чеком 600–750 рублей. Scatter из трёх тысяч точек показал бы вместо этого чёрный прямоугольник.

Когда jointplot лучше scatterplot

Выбор между ними — выбор вопроса. Если интересует только связь, scatterplot быстрее и гибче: в него встраиваются hue, размер и стиль точек. jointplot платит местом за краевые распределения и берёт своё, когда форму распределений нужно видеть рядом со связью:

ЗадачаИнструмент
Связь двух величин и их распределенияsns.jointplot
Только связь, с разбивкой по группамsns.scatterplot с hue=
Все пары колонок сразуsns.pairplot (урок 13)
Тысячи точек вместо чёрного пятнаsns.jointplot(kind="hex")

hue= поддерживается и в jointplot — краевые распределения станут KDE по группам. Но с ростом числа групп картинка теснится, и для сравнения групп обычно честнее вернуться к scatterplot на полной ширине.

Две школы в одном jointplot
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

prep = pd.DataFrame({
    "hours": [2, 3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 7],
    "score": [48, 55, 60, 68, 73, 80, 42, 50, 57, 61, 66, 74],
    "school": ["первая"] * 6 + ["вторая"] * 6,
})

sns.set_theme()
g = sns.jointplot(data=prep, x="hours", y="score", hue="school")
g.set_axis_labels("Часы подготовки", "Баллы")
plt.show()
Вывод
График отображается под редактором

Что дальше

Теперь у нас есть крупный план пары величин с их распределениями. Следующий урок снова меняет масштаб: catplot строит сетки категориальных графиков — один вызов рисует bar или box отдельно для каждой группы, малые кратные для сравнения городов и категорий.

jointplot честнее scatterplot: он показывает не только связь двух величин, но и их распределения по краям — сгущения и выбросы видны сразу.

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

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

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

df = pd.DataFrame({
    "x": [1, 2, 3, 4, 5],
    "y": [2, 4, 5, 4, 5],
})
g = sns.jointplot(data=df, x="x", y="y")
print(type(g).__name__)
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

df = pd.DataFrame({
    "x": [1, 2, 3, 4, 5],
    "y": [2, 4, 5, 4, 5],
})
g = sns.jointplot(data=df, x="x", y="y")
print(len(g.figure.axes))
import pandas as pd

exam = pd.DataFrame({
    "hours": [2, 3, 4, 5, 6, 7, 8, 9, 10, 12],
    "score": [45, 52, 58, 65, 70, 74, 80, 84, 88, 95],
})
print(round(exam["hours"].corr(exam["score"]), 2))
Проверь себя
0 / 5

1. Что рисует sns.jointplot(data=df, x="a", y="b") по умолчанию?

2. Какой объект возвращает jointplot?

3. Код g = sns.jointplot(...); g.set_title("Заголовок") упал. Как исправить?

4. На графике три тысячи точек слились в чёрное пятно. Что выбрать?

5. Когда обычный scatterplot предпочтительнее jointplot?

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

В редакторе — данные о стримах кафе: минуты музыки (minutes) и число новых подписчиков (followers). Постройте jointplot с плавными контурами плотности (kind="kde") и подпишите оси «Минуты стрима» и «Новые подписчики».

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

Что показывает jointplot в Seaborn?

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

Урок 14: jointplot — две величины и края

Почему у jointplot не работает set_title?

jointplot возвращает JointGrid, а не Axes — у этого объекта нет метода set_title. Заголовок центральной панели ставят через g.ax_joint.set_title(), общий заголовок фигуры — через g.figure.suptitle(), подписи обеих осей — через g.set_axis_labels().

Что выбрать — kind="kde" или kind="hex"?

kde рисует плавные контуры плотности и хорош на сотнях точек с одной сгущённой массой. hex делит плоскость на соты и честнее показывает сгущения на тысячах точек, где scatter чернеет. На малых выборках оба вводят в заблуждение — оставайтесь на scatter.

Можно ли добавить hue в jointplot?

Да: sns.jointplot(..., hue="group") раскрасит центральный scatter и превратит краевые гистограммы в KDE по группам. Для трёх и более групп картинка становится тесной — тогда лучше обычный scatterplot с hue на полной ширине.

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

«jointplot честнее scatterplot: он показывает не только связь двух величин, но и их распределения по краям — сгущения и выбросы видны сразу.»

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

TelegramVK

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

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