С помощью визуализации можно представить данные на графиках и диаграммах, и таким образом упростить анализ данных. В этом уроке мы научимся использовать инструменты визуализации на примере первичного анализа датасета Titanic. Мы будем использовать:
- Частотные графики
- Диаграммы
- Ящики с усами
- Скрипичные графики
- Гистограммы
- Тепловые карты
- Попарные графики
Подготовка данных
Начнем с предобработки датасета. Мы будем использовать следующие три модуля для разных задач:
# Работа с табличными данными
import pandas as pd
# Работа со структурами и алгоритмами для ускорения обработки данных
import numpy as np
# Создание регулярных выражений для поиска структур в строках
import re
Аналитики часто работают со структурированными данными в табличном виде. Для работы с ними в Python нужен модуль Pandas, который отличается скоростью и высокоуровневой реализацией функций. В этом уроке вы увидите, как этот модуль работает на практике.
Загрузим датасет в исходном виде:
# Чтение данных
train_data = pd.read_csv("./data/Titanic_train.csv")
Датасет Titanic содержит ряд столбцов c признаками, которые характеризуют пассажиров Титаника. Также есть метка Survived
— она показывает, выжил ли пассажир во время крушения корабля. Представим, что нам нужно изучить набор признаков каждого пассажира и предсказать, кто выжил. Посмотрим на весь набор параметров:
# Названия столбцов
print(train_data.columns.to_list())
# ['PassengerId', 'Survived', 'Pclass',
# 'Name', 'Sex', 'Age', 'SibSp', 'Parch',
# 'Ticket', 'Fare', 'Cabin', 'Embarked']
Для просмотра данных воспользуемся методом head()
:
train_data.head()
# PassengerId Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked
# 1 0 3 Braund, Mr. Owen Harris male 22.0 1 0 A/5 21171 7.2500 NaN S
# 2 1 1 Cumings, Mrs. John Brad female 38.0 1 0 PC 17599 71.2833 C85 C
# 3 1 3 Heikkinen, Miss. Laina female 26.0 0 0 STON/O2. 7.9250 NaN S
# 4 1 1 Futrelle, Mrs. Jacques female 35.0 1 0 113803 53.1000 C123 S
# 5 0 3 Allen, Mr. William Henr male 35.0 0 0 373450 8.0500 NaN S
Чтобы первично обработать данные, нужно:
- Удалить или заполнить пропуски
- Привести данные в столбцах к определенному типу
- Сформировать дополнительные признаки на основе других
Эти шаги не входят в рамки этого урока, поэтому не будем подробно вдаваться в код ниже. Вы можете изучить его самостоятельно:
# Переиндексируем данные и удаляем некоторые столбцы
train_data.index = train_data.PassengerId
del train_data["PassengerId"]
# Была ли у пассажира своя каюта?
train_data["Has_Cabin"] = train_data["Cabin"].apply(
lambda x: 0 if type(x) == float else 1
)
# Какого размера была семья пассажира?
train_data["FamilySize"] = train_data["SibSp"] + train_data["Parch"] + 1
# Пассажир был на Титанике один или с попутчиками?
train_data["Is_alone"] = train_data["FamilySize"].apply(lambda x: 0 if x > 1 else 1)
# Используем регулярные выражения, чтобы найти сокращения Ms, Mr или другие обращения
def get_title(name):
title_search = re.search(" ([A-Za-z]+)\.", name)
if title_search:
return title_search.group(1)
return ""
# Применяем к столбцу Name
train_data["Title"] = train_data["Name"].apply(get_title)
# Исправляем опечатки и группируем редкие обращения
train_data["Title"] = train_data["Title"].replace(
[
"Lady",
"Countess",
"Capt",
"Col",
"Don",
"Dr",
"Major",
"Rev",
"Sir",
"Jonkheer",
"Dona",
],
"Rare",
)
train_data["Title"] = train_data["Title"].replace("Mlle", "Miss")
train_data["Title"] = train_data["Title"].replace("Ms", "Miss")
train_data["Title"] = train_data["Title"].replace("Mme", "Mrs")
# Очищаем данные от обработанных столбцов
train_data = train_data.drop(["Name", "Ticket", "Cabin"], axis=1)
Исходные данные готовы — можно приступать к анализу.
Визуализация данных
Во время анализа нам нужно прийти к двум целям: выявить закономерности в данных и найти нехарактерные значения, которые могут указывать на ошибки. Для визуализации воспользуемся двумя популярными модулями:
- Matplotlib для настройки параметров графиков и форматирования вывода
- Seaborn для построения графиков
Эти модули помогают писать код с меньшим количеством дополнительных параметров. Это упрощает разработку и делает код более читабельным:
import matplotlib.pyplot as plt
import seaborn as sns
Мы объявили модули — можно начинать строить графики.
Частотные графики
Для начала посмотрим на частотный график для столбца Survived
:
plt.figure(figsize=(10, 8))
plt.title("Проверка сбалансированности данных по выжившим", fontsize=16)
sns.countplot(x="Survived", data=train_data)
На диаграмме выше видно, что погибших больше, чем выживших. Это важная характеристика датасета, потому что сбалансированность классов влияет на подходы к построению моделей, которые предсказывают тот или иной класс. В случае несбалансированности классов, нужно выполнять несколько дополнительных преобразований с данными.
Для примера приведем аналогичный график распределения пассажиров по полу:
plt.figure(figsize=(10, 8))
plt.title("Частотный анализ пола пассажиров", fontsize=16)
sns.countplot(x="Sex", data=train_data)
Библиотека Matplotlib позволяет создавать несколько графиков на одном полотне в виде окон. Причем в каждое окно можно поместить различные графики как самой библиотеки Matplotlib, так и ее приемников:
fig, ax = plt.subplots(
2, 3, sharex=False, sharey=False, figsize=(15, 15), constrained_layout=True
)
fig.suptitle("Частотный анализ данных", fontsize=25)
sns.countplot(x="Pclass", data=train_data, ax=ax[0, 0])
sns.countplot(x="Is_alone", data=train_data, ax=ax[0, 1])
sns.countplot(x="Title", data=train_data, ax=ax[0, 2])
sns.countplot(x="Embarked", data=train_data, ax=ax[1, 0])
sns.countplot(x="FamilySize", data=train_data, ax=ax[1, 1])
На картинке выше вы видите пять графиков. Они показывают частотную картину для значений из различных столбцов данных. Пустое окно создается по умолчанию, потому что Matplotlib отрисовывает прямоугольную таблицу по заранее заданным параметрам метода subplots()
. В нашем случае размер таблицы по умолчанию — 2 на 3.
Далее попробуем предсказать выживаемость пассажира. Для этих целей аналитики строят частотные графики в разрезе целевой переменной.
В случае с Seaborn можно использовать параметр hue
:
fig, ax = plt.subplots(
2, 3, sharex=False, sharey=False, figsize=(15, 15), constrained_layout=True
)
fig.suptitle("Частотный анализ данных относительно целевой переменной", fontsize=25)
sns.countplot(x="Pclass", hue="Survived", data=train_data, ax=ax[0, 0])
sns.countplot(x="Is_alone", hue="Survived", data=train_data, ax=ax[0, 1])
sns.countplot(x="Title", hue="Survived", data=train_data, ax=ax[0, 2])
sns.countplot(x="Embarked", hue="Survived", data=train_data, ax=ax[1, 0])
sns.countplot(x="FamilySize", hue="Survived", data=train_data, ax=ax[1, 1])
Графики выше показывают, что с большой вероятностью не выжили одинокие мистеры из третьего класса, севшие в Саутгемптоне — примерно такие гипотезы аналитики обычно выдвигают по графикам и диаграммам. Дальше эти гипотезы нужно подтвердить или опровергнуть методами статистического и корреляционного анализа — это типичный порядок работы аналитика.
Рассмотрим картину выживаемости для различных возрастов. По ней видно, что среди погибших много людей среднего возраста:
plt.figure(figsize=(20, 8))
plt.title("Частотный анализ Age относительно целевой переменной", fontsize=16)
sns.countplot(x="Age", hue="Survived", data=train_data)
Столбчатые диаграммы
Перейдем от частотного анализа к оценке статистических показателей. Воспользуемся методом barplot()
и построим среднее значение целевой переменной в зависимости от признака:
plt.figure(figsize=(10, 8))
plt.title("Среднее значение целевой переменной в зависимости от Title", fontsize=16)
sns.barplot(x="Title", y="Survived", data=train_data)
Этот график показывает среднюю вероятность выживаемости в зависимости от признака Title
. Черная вертикальная линия демонстрирует разброс вероятности относительно среднего — чем длиннее линия, тем разброс больше. Аналогичный график зависимости вероятности выживаемости в зависимости от размера семьи:
plt.figure(figsize=(10, 8))
plt.title(
"Среднее значение целевой переменной в зависимости от FamilySize", fontsize=16
)
sns.barplot(x="FamilySize", y="Survived", data=train_data)
Можно строить более сложные диаграммы для среднего, добавляя разрез по признакам. Для этого нужно добавить параметр hue
`. Изучим пример для среза по классу каюты:
plt.figure(figsize=(10, 8))
plt.title(
"Среднее значение целевой переменной в зависимости от Sex в срезе по Pclass",
fontsize=16,
)
sns.barplot(x="Sex", y="Survived", hue="Pclass", data=train_data)
Ящики с усами
Более детальный статистический анализ можно провести с помощью метода boxplot()
:
plt.figure(figsize=(15, 10))
plt.title("Среднее значение Age в зависимости от Survived в срезе по Sex", fontsize=16)
sns.boxplot(x="Age", y="Survived", hue="Sex", orient="h", data=train_data)
Этот метод отрисовывает график под названием «ящики с усами». Ящик показывает наиболее вероятные значения, а усы дают представление о разбросе. Это удобный способ определить выбросы — значения, которые выходят за границы усов, маловероятны и могут указывать на ошибки. Посмотрим, как выглядит сам график:
Обратите внимание на точки, не попавшие в границы усов — там могут быть ошибки, поэтому лучше проверить эти данные.
Скрипичные графики
Для дополнительного отображения формы распределения используют метод violinplot()
. Его следует читать по аналогии с boxplot()
:
plt.figure(figsize=(15, 10))
plt.title("Распределение Age в зависимости от Sex в срезе по Survived", fontsize=16)
sns.violinplot(x="Age", y="Sex", hue="Survived", orient="h", data=train_data)
Гистограммы
Для анализа характера распределения значений используют гистограммы. По ним можно судить о типе распределения, его значениях среднего, разброса и скошенности. Эта информация используется для дальнейшей подготовки данных в методах машинного обучения. Для построения гистограммы используем метод displot()
:
plt.figure(figsize=(12, 8))
plt.title("Гистограмма распределения возраста пассажиров", fontsize=16)
sns.displot(train_data["Age"], rug=True)
Попарные графики
Оценка влияния признаков на целевую переменную является основной целью анализа. Но взаимосвязь признаков друг с другом также несет важную информацию. На ее основе могут приниматься решения о формировании новых признаков или же наоборот удалении зависимых признаков как избыточных и влияющих на качество предиктивных моделей.
Для построения попарных графиков для всех признаков датасета используется метод pairplot()
:
plt.figure(figsize=(20, 20))
sns.pairplot(train_data)
Тепловые карты
Представление многомерных данных в виде плоской картинки — довольно непростая задача, потому что плоскость ограничивается только двумя измерениями. Для введения еще одного измерения используется цветовая дифференциация и тепловые карты.
Для построения тепловой карты в Seaborn существует метод heatmap()
. Попробуем составить карту вероятности выживания в зависимости от размера семьи и пола пассажира:
plt.figure(figsize=(15, 10))
plt.title(
"Тепловая карта значений Survived в зависимости от FamilySize и Sex", fontsize=16
)
sns.heatmap(
train_data.groupby(["FamilySize", "Sex"])["Survived"].mean().unstack().fillna(0)
)
Ниже пример для зависимости от признаков is_alone
и Sex
:
plt.figure(figsize=(10, 8))
plt.title(
"Тепловая карта значений Survived в зависимости от is_alone и Sex", fontsize=16
)
sns.heatmap(
train_data.groupby(["Is_alone", "Sex"])["Survived"].mean().unstack().fillna(0)
)
Самые светлые и темные области тепловой карты говорят о явной зависимости целевой переменной для конкретного квадрата — пары соответствующих значений признаков.
Выводы
В этом уроке мы реализовали основные графики, который аналитик использует в своей работе. На практическом примере разобрали, в каких случаях стоит применять:
- Частотные графики
- Диаграммы
- Ящики с усами
- Скрипичные графики
- Гистограммы
- Тепловые карты
- Попарные графики
Визуализация данных позволила выдвинуть ряд гипотез о качестве данных и закономерностях в них. Результаты этого урока могут использоваться как отчетный материал по первичному анализу данных.
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.