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

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

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

violinplot в Seaborn: форма распределения вместо ящика

Скрипка показывает распределение целиком: где масса наблюдений, где хвосты и есть ли второй горб. Сравниваем с boxplot, режем скрипку параметром split.

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

У boxplot из прошлого урока есть слепое пятно: он показывает квартили, но прячет форму. Две группы с одинаковым ящиком могут быть устроены принципиально по-разному — одна горкой вокруг медианы, другая двумя стаями по краям. violinplot («скрипичная диаграмма») рисует форму целиком: зеркальная фигура, толщина которой в каждом месте пропорциональна плотности наблюдений.

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

Первая скрипка: оценки двух групп

Сквозной пример — баллы за итоговый тест в двух группах курса. Группа А училась ровно, группа Б рассыпалась на отставших и отличников:

Оценки двух групп: violinplot
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

scores = pd.DataFrame({
    "group": ["А"] * 6 + ["Б"] * 6,
    "score": [60, 65, 70, 72, 75, 78, 55, 58, 60, 88, 90, 92],
})

sns.set_theme()
ax = sns.violinplot(data=scores, x="group", y="score")
ax.set_title("Баллы за тест: группа А и группа Б")
ax.set_xlabel("Группа")
ax.set_ylabel("Баллы")
plt.show()
Вывод
График отображается под редактором
Первый запуск загружает pandas и seaborn — это займёт до минуты

Скрипка группы А — один ровный бугор: масса баллов собрана вокруг середины. Скрипка группы Б — два горба с провалом между ними: студентов «на середняка» просто нет. Внутри каждой скрипки прячется мини-boxplot — белая точка это медиана, толстая линия квартили. Ширина скрипки — это плотность: где скрипка толще, там лежит больше наблюдений, а узкие места и хвосты говорят о редких значениях.

Средние и минимумы: почему числа не видят двух горбов
import pandas as pd

scores = pd.DataFrame({
    "group": ["А"] * 6 + ["Б"] * 6,
    "score": [60, 65, 70, 72, 75, 78, 55, 58, 60, 88, 90, 92],
})

print(scores.groupby("group")["score"].mean().round(1).tolist())
print(scores["score"].min(), scores["score"].max())
Вывод
[70.0, 73.8]
55 92

Средние групп почти равны — 70.0 против 73.8, и диапазон один: от 55 до 92. Сводные числа уверяют, что группы похожи; скрипка показывает, что устроены они противоположно. Это тот же урок, что с ящиком с усами, но острее: двугорбость не увидеть ни в среднем, ни в квартилях.

boxplot против violinplot: одна картина дважды

Лучшая привычка — рисовать оба графика рядом: ящик даёт точные пятичисловые сводки, скрипка — форму. Seaborn рисует на общие оси через subplots Matplotlib:

Одни данные, два взгляда
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

scores = pd.DataFrame({
    "group": ["А"] * 6 + ["Б"] * 6,
    "score": [60, 65, 70, 72, 75, 78, 55, 58, 60, 88, 90, 92],
})

sns.set_theme()
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4), sharey=True)
sns.boxplot(data=scores, x="group", y="score", ax=ax1)
sns.violinplot(data=scores, x="group", y="score", ax=ax2)
ax1.set_title("boxplot: квартили и выбросы")
ax2.set_title("violinplot: форма распределения")
plt.show()
Вывод
График отображается под редактором

Ящик группы Б честно широк — но выглядит как «просто большой разброс»; только скрипка справа показывает, что разброс двугорбый. Обратная ситуация бывает при малых выборках: ящик из десяти чисел надёжен, а скрипка тех же десяти чисел может льстить глазу плавными изгибами. Правило: boxplot — для сводки и групп поменьше, violinplot — когда важна форма и наблюдений хотя бы двадцать на группу.

split=True: две группы в одной скрипке

Если в hue ровно две группы, скрипку можно разрезать: каждая половина получит свою группу. Это разделение — параметр split=True, и он экономит полширины графика, когда группы естественно парные:

Утро против вечера: split-скрипка
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

lessons = pd.DataFrame({
    "course": ["Английский"] * 6 + ["Математика"] * 6,
    "time": ["утро", "вечер"] * 6,
    "score": [72, 65, 78, 61, 84, 70, 81, 58, 76, 62, 88, 66],
})

sns.set_theme()
ax = sns.violinplot(data=lessons, x="course", y="score",
                    hue="time", split=True)
ax.set_title("split=True: утро слева, вечер справа")
ax.set_xlabel("Курс")
ax.set_ylabel("Баллы")
plt.show()
Вывод
График отображается под редактором

Каждая скрипка теперь зеркальная пара половин: утренние группы слева, вечерние справа. Читается мгновенно — если половины совпадают, время занятий ни на что не влияет; если расходятся, как здесь у математики, вечерние группы проседают. Без split Seaborn нарисовал бы две отдельные скрипки на курс — тоже рабочий вариант, просто вдвое шире.

cut=0: не выходить за пределы данных

KDE-сглаживание не знает о смысле данных: для величин, которые не бывают отрицательными — время, цена, балл от нуля — кривая на краях может уйти в минус. Параметр cut= запрещает ей отходить от крайних значений:

Очередь в поликлинике: без вымышленного минуса
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

queue = pd.DataFrame({"minutes": [2, 3, 3, 4, 4, 4, 5, 5, 6, 20]})

sns.set_theme()
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4), sharey=True)
sns.violinplot(y=queue["minutes"], ax=ax1)
sns.violinplot(y=queue["minutes"], cut=0, ax=ax2)
ax1.set_title("cut по умолчанию: хвост ушёл в минус")
ax2.set_title("cut=0: скрипка кончается на данных")
plt.show()
Вывод
График отображается под редактором

Слева скрипка обещает «возможны отрицательные минуты ожидания» — этого не бывает. Справа фигура обрезана по реальным данным и не выдаёт желаемое за действительное. Тот же контроль даёт inner=: значение "quart" рисует внутри линии квартилей, "box" — мини-ящик (по умолчанию), "point" — точки, None убирает внутренности вовсе.

inner="quart": квартили линиями
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

scores = pd.DataFrame({
    "group": ["А"] * 6 + ["Б"] * 6,
    "score": [60, 65, 70, 72, 75, 78, 55, 58, 60, 88, 90, 92],
})

sns.set_theme()
ax = sns.violinplot(data=scores, x="group", y="score", inner="quart")
ax.set_title("Квартили видны, форма не спрятана")
ax.set_xlabel("Группа")
ax.set_ylabel("Баллы")
plt.show()
Вывод
График отображается под редактором

Такой вариант — золотая середина: скрипка показывает форму, а линии внутри несут точные квартили. Для отчётов, где читатель привык к ящикам, inner="quart" часто мягче вводит в скрипичную диаграмму, чем мини-бокс по умолчанию.

Что дальше

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

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

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

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

import pandas as pd

s = pd.Series([2, 2, 2, 8, 8, 8])
print(s.mean())
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

df = pd.DataFrame({
    "g": ["x", "y", "z", "x", "y", "z"],
    "v": [1, 2, 3, 4, 5, 6],
})
ax = sns.violinplot(data=df, x="g", y="v")
print(len(ax.collections))
import pandas as pd

df = pd.DataFrame({
    "g": ["a", "a", "b", "b"],
    "v": [1, 3, 5, 7],
})
print(df.groupby("g")["v"].median().tolist())
Проверь себя
0 / 5

1. Что означает ширина скрипки в violinplot?

2. Что прячется внутри скрипки по умолчанию?

3. Что делает split=True в sns.violinplot с hue из двух групп?

4. Данные — время ожидания в минутах, оно не бывает отрицательным. Какой параметр violinplot уберёт вымышленный хвост ниже нуля?

5. Когда честнее показать boxplot, а не violinplot?

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

В редакторе — время сборки заказа двумя сотрудниками (минуты). Постройте violinplot по сотрудникам с линиями квартилей внутри (inner="quart") и заголовком «Время сборки заказа».

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

Что показывает violinplot и чем он отличается от boxplot?

Violinplot («скрипичная диаграмма») рисует форму распределения целиком: ширина фигуры — оценка плотности, поэтому двугорбость и хвосты видны сразу. Boxplot сжимает данные до пяти чисел и форму прячет. Внутри скрипки обычно остаётся мини-ящик, так что медиана и квартили никуда не деваются.

Урок 11: violinplot — форма распределения

Как сравнить две группы в одной скрипке?

Передайте hue с двумя значениями и добавьте split=True: каждая группа получит свою половину фигуры. Утро слева, вечер справа — совпадающие половины означают, что фактор времени ни на что не влияет.

Почему на скрипке появляются отрицательные значения, которых нет в данных?

Это сглаживание KDE: кривая плотности не знает о смысле данных и дорисовывает хвост за крайними значениями. Параметр cut=0 запрещает ей выходить за пределы наблюдений — обязательно включайте его для неотрицательных величин вроде времени и цены.

Когда violinplot вводит в заблуждение?

На малых выборках: KDE сгладит даже три наблюдения в безупречно гладкую фигуру, и читатель принимает форму за факт. Для групп из нескольких значений честнее boxplot или точки; скрипка раскрывается на выборках от двадцати наблюдений.

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

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

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

TelegramVK

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

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

Matplotlib / Seaborn · Урок 8

Гистограмма и ящик с усами в Matplotlib: hist и boxplot

plt.hist и plt.boxplot на одних данных: бины и density, чтение формы распределения, медиана, квартили, усы 1.5 × IQR и выбросы — плюс сравнение двух групп обоими способами. Весь код запускается на странице.

matplotlib boxplotсравнение распределений python

Matplotlib / Seaborn · Урок 10

Проект по визуализации: дашборд данных о фильмах на Matplotlib и Seaborn

Финальный проект курса: датасет из 200 фильмов, EDA, дашборд 2x2 с трендом, жанрами, гистограммой и scatter — плюс чек-лист «график готов к показу» и итоги десяти уроков.

matplotlib seabornстолбчатая диаграмма python

Seaborn · Урок 4

histplot в Seaborn: распределения, bins и kde

Гистограмма отвечает на вопрос «как распределились числа», а не «каково их среднее». bins задаёт ширину карманов, kde=True добавляет гладкую форму — и бимодальность больше не спрячется.

seaborn histplotраспределение python seaborn