Введение

Seaborn - это библиотека для визуализации данных и выделения их статистических особенностей. Seaborn написанна поверх библиотеки Matplotlib, но предлагает интерфейс более высокого уровня, а это значит, что "тонны" шаблонного кода, формирующего внешний вид графиков, спрятаны "под капотом". Что бы создать график более чем пригодный для быстрой "разведки" данных, нужно всего одна-две строчки кода, а если требуется подготовить график для презентации или публикации, то Seaborn так же предоставляет доступ к низкоуровневым настройкам.

Еще одна важная особенность библиотеки Seaborn - это заложенный в нее механизм предобработки данных, возможный благодаря тесной интеграции с библиотекой Pandas. Данные можно передавать прямо в объектах DataFrame, а Seaborn сама выполнит всю необходимую работу: агрегацию (выделение подмножеств), статистическую обработку (например вычисление доверительных интервалов) и визуальное выделение полученных результатов.

Точно так же как Python позволяет сосредоточиться на задаче, а не коде с помощью которого она решается, Seaborn позволяет сконцентрироваться на важных нюансах визуального представления ваших данных, а не коде с помощью которого эти нюансы нужно рисовать и выделять.

Пожалуй единственный минус Seaborn заключается в том, что получаемые графики не являются интерактивными - нельзя навести указатель мыши на какую-нибудь точку и увидеть соответствующее ей значение, нельзя выделить какую-то область графика и увеличить ее. Но вы не замечали, что некоторые опытные кодеры вообще не пользуются мышкой? Интерактивность это конечно круто, однако, извлечение конкретной числовой информации из самих графиков - это не путь самурая.


Первый график в Seaborn

Построить довольно сложный график в Seaborn проще простого:

# Импортируем Seaborn:
import seaborn as sns

# Устанавливаем тему по умолчанию:
sns.set_theme()

# Загружаем набор данных для примера:
tips = sns.load_dataset('tips')

# Строим график:
sns.relplot(
    data=tips,
    x='total_bill',
    y='tip',
    col='time',
    hue='sex',
    style='smoker',
    size='size');

Простой пример построения графика разброса в Seaborn

Сначала, командой import seaborn as sns мы импортировали Seaborn в пространство имен, как сокращенный псевдоним sns (уже общепринятый и традиционный).

Далее, следует простая строчка sns.set_theme(), которая использует систему rcParam для настройки стиля графиков. Стиль графиков, после этой команды изменится даже если их строить только средствами Matplotlib. Помимо темы, используемой по умолчанию, есть и другие, если вам нравится стиль используемый Matplotlib по умолчанию, то данную команду можно вообще не использовать.

Следующая команда - tips = sns.load_dataset('tips') использует функцию load_dataset(), которая создана в Seaborn только для демонстрации примеров работы с библиотекой. Данная функция загружает набор демонстрационных данных в виде Dataframe из репозитория GitHub и в реальных условиях может быть заменена на функцию read_csv() библиотеки Pandas. В качестве аргумента функции load_dataset() была передана строка с именем необходимого датафрейма 'tips' - данные о размере чаевых в ресторане.

После того как данные были загружены и присвоены переменной tips, можно приступать к построению графика, которое выполняется функцией relplot():

sns.relplot(
    data=tips,
    x='total_bill',
    y='tip',
    col='time',
    hue='sex',
    style='smoker',
    size='size')

Обратите внимание, что мы просто передали переменную tips параметру data, не выполняя никаких преобразований формата данных. В параметре x мы указали имя столбца из датафрейма Pandas, который содержит информацию об общей стоимости заказа, а в параметре y имя столбца с размером чаевых. Но на этом все только начинается... в параметре col мы указали имя столбца с информацией о времени дня в которое был обслужен столик - это привело к тому, что было построено два графика разброса для первой и второй половины дня. Обратите внимание, что Seaborn сама выполнила разбиение данных на подмножества. Точно так же, с помощью параметра hue были выделены цветом два подмножества пола людей оплачивавших заказ ('sex'), а размер точек, благодаря параметру size='size', начал соответствовать количеству людей за столиком.

А теперь представьте, сколько бы кода потребовало построение того же самого графика в Matplotlib?


Интерфейс переключения между типами графиков

В примере выше мы построили график разброса с помощью функции relplot(). Но графики разброса это лишь малая часть тех видов графиков, которые нам могут понадобиться. Например, нам очень часто приходится изображать данные в виде линий, если мы предполагаем, что одна переменная функционально зависит от другой, т.е. \(y = f(x)\). В этом случае, мы можем воспользоваться все той же функцией relplot(), но что бы переключить ее в режим рисования линейного графика нужно передать параметру kind значение line (по умолчанию kind='scatter').

Давайте сначала загрузим данные для примера:

penguins = sns.load_dataset("penguins")

И посмотрим, что они из себя представляют:

penguins.head()
species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g sex
0 Adelie Torgersen 39.1 18.7 181.0 3750.0 Male
1 Adelie Torgersen 39.5 17.4 186.0 3800.0 Female
2 Adelie Torgersen 40.3 18.0 195.0 3250.0 Female
3 Adelie Torgersen NaN NaN NaN NaN NaN
4 Adelie Torgersen 36.7 19.3 193.0 3450.0 Female

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

sns.relplot(data=penguins,
            kind='line',
            x='body_mass_g',
            y='flipper_length_mm',
            col='island',
            row='sex',
            hue='species',
            ci=None,
            facet_kws={'sharex': False});

Простой пример построения линейного графика в Seaborn

С помощью параметров col и row мы создали три столбца графиков для каждого отдельного острова и две строки для самцов и самок. Но самое интересное, это параметр hue, который может выделять цветом подмножества в данных, как на графиках разброса, так и линейных графиках. В данном случае с помощью параметра hue мы выделили цветом три разных вида пингвинов, благодаря чему стало заметно, что некоторые виды могут совместно обитать на одном острове. При этом мы могли бы построить все эти данные и на одном графике, воспользовавшись уже знакомыми параметрами style и size:

sns.relplot(data=penguins,
            kind='line',
            x='body_mass_g',
            y='flipper_length_mm',
            hue='species',
            style='island',
            size='sex',
            ci=None,);

Пример построения простого линейного графика с визуальным выделением множества переменных в Seaborn

Но от этого сильно страдает восприятие визуальной информации и лучше использовать многосегментные графики (с отдельными столбцами и (или) строками для каждой отдельной переменной).

Однако, суть все же в том, что для рисования разных видов графиков используется одна и таже функция relplot(), переключаемая на разные типы графиков с помощью параметра kind. При этом для визуальной семантики подмножеств данных используются одни и теже параметры: для relplot() это hue, style и size.

Более того, другие высокоуровневые функции устроены таким же образом. Например, если вам так же как и мне показалось, что длина крыльев пингвинов зависит от массы тела линейно, то это можно легко проверить с помощью модели линейной регрессии:

sns.lmplot(data=penguins,
            x='body_mass_g',
            y='flipper_length_mm',
            col='island',
            row='sex',
            hue='species');

Пример построения простого графика с линейной регрессией в Seaborn

В дальнейшем вы будете часто видеть, что одна и таже функция в Seaborn может применяться для построения разных графиков, но тип графика всегда указывается в параметре kind.


Построение распределений

Очень часто нам нужно знать, как распределена величина (и так же часто мы надеемся что она распределена нормально). Что бы визуально оценить распределение мы можем воспользоваться функцией displot(), которая по умолчанию рисует гистограммы (т.е. kind='hist'):

sns.displot(data=penguins,
            x='body_mass_g',
            hue='sex',
            col='species',
            bins=30,
            kde=True);

Пример построения простого графика с гистограммой в Seaborn

Или мы можем оценить плотность ядра двумерного распределения, установив kind='kde':

sns.displot(data=penguins,
            x='body_mass_g',
            y='flipper_length_mm',
            hue='sex',
            col='species',
            kind='kde');

Пример построения простого графика с оценкой плотности ядра в Seaborn


Категориальные данные

Если нас интересует распределение значений внутри категорий (внутри подмножеств данных), то лучше воспользоваться функцией catplot() которая по умолчанию рисует график разброса значений внутри каждой категории (т.е. kind='strip'):

sns.catplot(data=penguins,
            x='species',
            y='body_mass_g',
            hue='sex');

Пример построения графика stripplot в Seaborn

Передав параметру kind значение 'box' мы можем взглянуть на то же самое внутрикатегориальное распределение, изображенное в виде ящиков с усами:

sns.catplot(data=penguins,
            x='species',
            y='body_mass_g',
            hue='sex',
            kind='box');

Пример построения графика boxplot в Seaborn

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

sns.boxplot(data=penguins,
            x='species',
            y='body_mass_g',
            hue='sex');

Пример построения графика boxplot в Seaborn с помощью функции boxplot()

Но дело в том,что функция boxplot() строит график на уровне области Axes и по сути может принимать любые последовательности данных (списки, кортежи, массивы NumPy и серии Pandas). Но она не может разграничивать область всей картинки на подграфики, как это умеет catplot(). По сути, catplot() использует boxplot() как рабочий инструмент, для создания графиков в каждой отдельной ячейки строки или столбца:

sns.catplot(data=penguins,
            x='species',
            y='body_mass_g',
            col='island',
            hue='sex',
            kind='box');

Пример построения графика boxplot с несколькими столбцами в Seaborn


Композиции из нескольких видов графиков

Крайне полезной является визуализация совместного распределения двух величин и визуализация распределений каждой из них в отдельности. Для этого можно воспользовоться функцией jointplot(), которая как раз и объединяет несколько видов графиков:

sns.jointplot(data=penguins,
              x='flipper_length_mm',
              y='body_mass_g',
              hue='species');

Пример построения графика jointplot в Seaborn

С другой стороны, если переменных больше чем две, то лучше воспользоваться функцией pairplot() которая показывает все парные и индивидуальные распределения:

sns.pairplot(penguins, hue='sex');

Пример построения графика pairplot в Seaborn

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

g = sns.PairGrid(penguins, hue="species")
g.map_upper(sns.histplot, bins=30)
g.map_lower(sns.kdeplot, bw_adjust=0.7)
g.map_diag(sns.histplot, kde=True);

Пример построения графика PairGrid в Seaborn