Практика EDA. Часть 2 | Анализ и обработка данных

Практика EDA. Часть 2

Все курсы > Анализ и обработка данных > Занятие 4 (часть 2)

Во второй части занятия рассмотрим нахождение различий в данных и выявление взаимосвязи.

Продолжим работать в том же ноутбуке

Нахождение различий

нахождение различий в данных в процессе исследовательского анализа данных

Два категориальных признака

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

countplot и barplot

В первую очередь стоит визуально оценить, есть ли такое различие или нет. Для этого подойдут столбчатые диаграммы, где мы либо располагаем два столбца целевого признака рядом друг с другом (grouped), либо делаем один столбец и разбиваем его на две части (stacked).

Библиотека Seaborn

Начнем с того, что построим несколько counplots/barplots в библиотеке Seaborn с помощью функции countplot() и параметра hue.

countplot для визуализации двух категориальных переменных в Seaborn
горизонтальный countplot для визуализации двух категориальных переменных в Seaborn

Для создания таких графиков мы также можем использовать более универсальную функцию catplot(). Передадим ей все те же параметры, что и функции countplot(), а также параметр kind = ‘count’, который и сообщит, что мы хотим построить именно countplot.

использование функции sns.catplot() для создания countplot
визуализация трех категориальных переменных с помощью двух grouped countplots

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

Теперь посмотрим, как создать подобные графики в библиотеке Plotly.

Библиотека Plotly

Для построения графика countplot используем функцию px.histogram() (для barplot подойдет px.bar()). Начнем с варианта, когда разбитые по какому-либо признаку столбцы стоят рядом друг с другом (grouped).

использование функции px.histogram() для создания countplot

Теперь выведем вариант, когда каждый столбец диаграммы разделен на две части (stacked). Так как мы будем вручную корректировать подписи к графику и расстояние между столбцами, необходимо использовать объектно-ориентированный подход.

использование функции px.histogram() для создания stacked countplot

Теперь разобьем данные по трем категориальным переменным: полу, классу и выживаемости.

использование функции px.histogram() для создания grouped countplot с параметрами color и facet_col

Более того, мы можем добавить еще один категориальный признак, порт посадки пассажира (Embarked).

использование функции px.histogram() для создания grouped countplot с параметрами color, facet_col и facel_row

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

Таблица сопряженности

Таблица сопряженности (contingency table) позволяет количественно измерить зависимость одной категориальной переменной от другой. Например, количественно оценим зависимость выживаемости от класса пассажира. Вначале оценим абсолютное количество наблюдений.

Абсолютное количество наблюдений

Для создания таблиц сопряженности в библиотеке Pandas используется функция pd.crosstab().

таблица сопряженности и функция pd.crosstab()

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

Начнем с библиотеки Pandas.

grouped столбчатая диаграмма на основе таблицы сопряженности
stacked столбчатая диаграмма на основе таблицы сопряженности

Теперь посмотрим, как построить stacked barplot в библиотеке Matplotlib.

stacked столбчатая диаграмма на основе таблицы сопряженности в Matplotlib
Таблица сопряженности вместе с суммой

С помощью параметра margins = True мы можем вывести сумму наблюдений по каждой строке и каждому столбцу (эти показатели еще называют маргинальными частотами, marginal frequencies).

таблица сопряженности вместе с суммой (маргинальными частотами)
Относительное количество наблюдений

Для получения относительного количества наблюдений (относительных частот) следует использовать параметр normalize. Так как нам важно понимать долю выживших и долю погибших, укажем normalize = 'index'. В этом случае каждое значение будет разделено на общее количество наблюдений в строке.

таблица сопряженности: относительное количество наблюдений по строкам

Если бы в индексе (в строках) была выживаемость, а в столбцах — классы, то логично было бы использовать параметр normalize = 'columns' для деления на сумму по столбцам.

таблица сопряженности: относительное количество наблюдений по столбцам

Теперь на stacked barplot мы видим доли выживших в каждом из классов.

столбчатая диаграмма на основе таблицы сопряженности с относительным количеством наблюдений

Количественный и категориальный признаки

rcParams

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

В библиотеке Matplotlib и связанных с ней библиотеках (например, Seaborn) есть так называемые параметры конфигурации среды (runtime configuration parameters), то есть параметры, которые используются по умолчанию при создании графиков.

Эти параметры и их значения содержатся в словаре, к которому можно получить доступ через атрибут rcParams библиотеки Matplotlib.

Изменить эти параметры можно, обновив значение словаря rcParams по соответствующему ключу. Передадим новое значение размера по ключу figure.figuresize.

Также можно воспользоваться функцией sns.set() или, что то же самое, sns.set_theme().

Теперь все последующие графики в библиотеках Matplotlib, Seaborn и Pandas будут иметь размеры восемь на пять дюймов. Вернемся к исследованию переменных.

Гистограммы

Когда у нас есть одна количественная и одна категориальная переменные, для их визуализации проще всего построить две наложенные друг на друга гистограммы. Мы уже строили такие графики в рамках вводного курса.

Посмотрим, различается ли распределение возраста выживших и погибших пассажиров Титаника.

гистограмма для визуализации количественной и категориальной переменных (Matpotlib)

Теперь посмотрим, зависит ли распределение возраста от пола пассажира.

гистограмма для визуализации количественной и категориальной переменных (Seaborn)
гистограмма для визуализации количественной и категориальной переменных (Plotly)

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

Исправить ситуацию может параметр density = True.

две гистограммы в Seaborn с параметром density = True

В этом случае гистограмма показывает плотность вероятности, а ее общая площадь всегда равна единице. Как следствие, мы можем адекватно сравнивать распределения между собой.

График плотности

С другой стороны, для плотности вероятности есть отдельный график, density plot. Площадь под кривой такого графика также всегда равна единице. Воспользуемся функцией .displot() с параметром kde = True.

два графика плотности с функцией .displot()

Из-за особенностей расчета графика kde мы можем получить «неестественные значения». Например, на диаграмме выше встречаются отрицательные значения чека. В реальности такого быть не может.

Избавиться от таких значений можно с помощью параметра clip, который задает диапазон значений.

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

boxplots

Для сравнения распределений количественной переменной, разбитой по какому-либо категориальному признаку, также очень удобно использовать несколько графиков boxplot (side-by-side boxplots).

Построим такие графики в библиотеках Seaborn и Plotly. Вначале посмотрим, как различается сумма чека по дням недели.

несколько boxplots на одном графике

Что можно сказать про эти распределения?

  • Медианный чек выше по воскресеньям
  • Самый широкий диапазон суммы по чеку наблюдается в субботу, в пятницу же наоборот разброс наименьший
  • Выбросы присутствуют только в верхних значениях распределения

Теперь посмотрим, как различается сумма чека в обеденное и вечернее время.

несколько boxplots и stripplots на одном графике

Ожидаемо, как разброс, так и медианное значение меньше в обеденное время.

Дополнительно замечу, что с помощью параметра points = ‘all’ в библиотеке Plotly для каждого распределения мы построили график, который называется stripplot. Он, в частности, показывает, что гостей за ужином бывает существенно больше. Об этом графике мы дополнительно поговорим чуть ниже.

Гистограммы и boxplots

Гистограммы и boxplots можно совместить. Сделать это проще всего в Plotly.

гистограммы и boxplots на одном графике в библиотеке Plotly

stripplot, violinplot

Более редкими типами графиков для визуализации количественных распределений являются stripplot и violinplot. Первый график, stripplot, как мы уже видели выше, визуализирует сами наблюдения.

несколько stripplots на одном графике

График stripplot можно построить как с помощью приведенной в примере выше функции sns.stripplot(), так и с помощью функции sns.catplot() с параметром kind = ‘strip’.

функция sns.catplot()

Хотя stripplot достаточно информативен сам по себе, его очень удобно применять совместно с boxplot (как мы это делали выше).

График violinplot (от англ. violin, «скрипка») представляет собой комбинацию boxplot и графика плотности.

функция sns.violinplot()

Внутри каждого из violinplot находится миниатюрный boxplot, который помогает более точно оценить параметры распределения.

Преобразования данных

Иногда так бывает, что для повышения читаемости графика, данные сначала нужно преобразовать.

Логарифмическая шкала

Например, возьмем вот такие данные о продажах.

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

преобразование данных с помощью логарифмической шкалы (до преобразования)

Для того чтобы эти продажи все-таки были видны, можно перевести ось y в логарифмическую шкалу.

преобразование данных с помощью логарифмической шкалы (после преобразования)

Границы по оси y

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

границы по оси y (до преобразования)

Иногда для наглядности бывает полезно ограничить диапазон значений по оси y.

границы по оси y (после преобразования)

Перейдем к выявлению взаимосвязи между переменными.

Выявление взаимосвязи

способы выявления взаимосвязи в данных

Выявление взаимосвязи предполагает анализ двух количественных переменных.

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

Линейный график

Базовым способом визуализации двух количественных переменных является линейный график (linear plot). Построить его можно с помощью функции plt.plot() библиотеки Matplotlib.

линейный график

Точечная диаграмма

Еще один базовый график — уже знакомая нам точечная диаграмма (scatter plot). Ее удобно использовать, когда одна переменная не имеет строгой зависимости от другой. Воспользуемся функцией plt.scatter() библиотеки Matplotlib.

точечная диаграмма matpltolib

Такой же график можно построить в библиотеке Pandas.

точечная диаграмма pandas

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

При этом мы видим гетероскедастичность (различную изменчивость) данных, когда при небольшом чеке диапазон чаевых меньше, чем когда сумма чека увеличивается.

Почему это влияет на качество модели и как с этим бороться, мы поговорим на следующем курсе.

В точечной диаграмме можно учесть и категориальный признак. Например, посмотрим, есть ли различие во взаимосвязи между суммой чека и размером чаевых в зависимости от времени дня.

категориальный признак в точечной диаграмме

Мы можем констатировать, что при сохранении взаимосвязи как в обеденное, так и в вечернее время, за ужином минимальная и максимальное сумма чека, а также разброс чаевых выше.

pairplot

График pairplot позволяет визуализировать взаимосвязи сразу нескольких количественных переменных. В библиотеке Pandas такой график строится с помощью функции pd.plotting.scatter_matrix().

pairplot в библиотеке pandas

Как вы видите, там, где перемекаются разные признаки, строится точечная диаграмма, на пересечении одного и того же признака по главной диагонали — его гистограмма.

Примерно такой же график можно построить с помощью функции sns.pairplot() библиотеки Pandas.

pairplot в библиотеке seaborn

Обратите внимание на метод .sample() с параметром frac = 0,2, который мы применили к датафрейму titanic. Таким образом, мы сделали случайную выборку из 20% или $ 891 \times 0,2 \approx 178 $ наблюдений.

случайная выборка с помощью метода .sample() в pandas

Метод .sample() в данном случае применяется для того, чтобы ускорить создание pairplot. Зачастую, при наличии большого числа наблюдений, график может строиться очень долго.

При добавлении параметра hue (разделение по категориальной переменной) гистограмма по умолчанию превращается в график плотности.

параметр hue в sns.pairplot()

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

Функция sns.pairplot() является надстройкой (упрощенной версией) другой функции этой библиотеки, sns.PairGrid(). Ее стоит использовать, если требуются более продвинутые настройки графика pairplot.

sns.PairGrid()

При построении таких сложных графиков важно помнить про их информативность. В примере выше некоторые графики (например, точечную диаграмму) уже достаточно сложно анализировать.

jointplot

Совместное распределение двух переменных

График плотности (kde plot) двух количественных признаков (верхний справа в примере выше) представляет собой визуализацию совместного распределения (joint distribution) двух количественных признаков (tip и total_bill) с разделением по категориальному признаку (time). Другими словами, мы смотрим на то, как изменяется распределение одного количественного признака под воздействием другого. И так для каждой из двух категорий.

В результате мы получаем графики изолиний (contour lines), которые показывают, что между суммой чека и чаевыми есть взаимосвязь (если бы ее не было, изолинии представляли бы собой круги). Теоретические основы совместных распределений мы рассмотрим на курсе по статистике вывода, а пока изучим инструмент их визуализации, который называется jointplot.

sns.jointplot()

Вначале построим точно такой же график плотности (kde plot) совместного распределения tip и total_bill с разделением по признаку time. Для этого функции sns.jointplot() передадим данные и укажем параметр kind = ‘kde’.

sns.jointplot()

По краям мы видим графики плотности так называемого безусловного распределения (marginal distribution) каждого из признаков. Это одномерные распределения (univariate distribution). Основной график показывает совместное распределение (joint distribution) уже двух переменных. Это двумерное распределение (bivariate distribution).

Возможно более интуитивным покажется использование точечной диаграммы (kind = ‘scatter’) вместо графика плотности.

sns.jointplot() + точечная диаграмма

Кроме того, мы можем построить линию регрессии, проходящую через точки. Правда в этом случае придется отказаться от параметра hue, разделять данные на категории и одновременно строить линию регрессии sns.jointplot() не умеет.

sns.jointplot() + линейная регрессия

heatmap

Наконец, если мы хотим вывести какие-либо статистические показатели взаимосвязи двух количественных переменных (например, корреляцию), это можно сделать с помощью чисел. Выведем корреляционную матрицу между total_bill и tip с помощью метода .corr().

корреляция в датасете tips

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

Или с помощью цвета. Во втором случае мы будем строить то, что называется тепловой картой (heatmap). Поместим созданную выше корреляционную матрицу в функцию sns.heatmap().

sns.heatmap()

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

Сравнение датасетов

Рассмотрим еще одну библиотеку, которая позволяет не просто сравнивать количественные и качественные переменные в датасете, а сразу сравнивать два датасета. Зачастую, сравнение двух датасетов имеет смысл, когда перед нами обучающая и тестовая выборки.

Скачаем и подгрузим в сессионное хранилище тестовую часть датасета «Титаник».

Библиотека Sweetviz

Теперь установим и импортируем библиотеку sweetviz.

Импортируем обучающую и тестовую выборки.

Передадим оба датасета в функцию sv.compare(). Эта функция создаст объект DataframeReport, к которому мы сможем применить метод .show_notebook() для выведения результата.

формирование сравнительного отчета в Sweetviz
сравнительный отчет в Sweetviz

Интерактивную версию этого отчета вы найдете в ноутбуке к занятию⧉.

Количественные переменные

По большому счету мы получаем информацию о каждой из переменных в разрезе двух датафреймов. Обратимся к столбцу Age.

количественная переменная в Sweetviz

В отчете есть информация о присутствующих (values) и отсутствующих значениях (missing), количестве уникальных (distinct) и нулевых (zeroes) значений. Кроме того, мы видим базовые статистические показатели и гистограмму распределения переменной в каждом из датафреймов.

Отдельно стоит отметить выявление взаимосвязи:

  • для двух количественных переменных используется коэффициент корреляции Пирсона (Pearson correlation coefficient); и здесь мы видим, что корреляция возраста со столбцами Fare и PassengerId ожидаемо близка к нулю
  • для выявления взаимосвязи между количественной и качественной переменными используется корреляционное отношение (correlation ratio); например, мы видим, что возраст в некоторой степени связан с классом пассажира Pclass

Качественные переменные

Обратимся к столбцу Sex.

качественная переменная в Sweetviz

В первую очередь отметим, что программа самостоятельно определила, что речь идет именно о категориальном признаке. Для его визуализации была построена столбчатая диаграмма с разбивкой по обучающей и тестовой выборке. Кроме того, мы можем количественно оценить значения в каждой из категорий.

Для поиска же взаимосвязи между двумя категориальными переменными используется коэффициент неопределенности (uncertainty coefficient) или U Тиля, и мы видим некоторую связь с целевой переменной Survived. Для количественной и качественной переменных по-прежнему используется корреляционное отношение.

Более подробную информацию об этой библиотеке можно посмотреть на странице документации⧉.

Перейдем к третьей части занятия.