- Импорт библиотек и подготовка данных
- Гистограмма распределения
- Ящик с усами
- Скрипичная диаграмма
- Круговая диаграмма
- Выводы
Графическое представление статистических значений и распределений дает аналитику возможность извлекать значимые характеристики из больших объемов данных. В этом уроке мы рассмотрим основные графики и диаграммы, которые используются аналитиками в своей работе, а именно:
- Гистограммы распределений —
hist
- Ящики с усами —
boxplot
- Скрипичные графики —
violinplot
- Круговые диаграммы —
pie
Импорт библиотек и подготовка данных
Для работы нам понадобятся модули sklearn.datasets
для получения данных и matplotlib.pyplot
для их визуализации:
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
В качестве данных возьмем тренировочный датасет с классификацией цветков ириса. Подробнее об этом можно прочитать в документации:
data = load_iris()
X = data.data
y = data.target
n = 0
print("Data example:")
print(X[n])
# => Data example:
# [5.1 3.5 1.4 0.2]
print("Data feature names:")
print(data.feature_names)
# => Data feature names:
# ['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']
print("Target example:")
print(y[n])
# => Target example:
# 0
print("Target name:")
print(data.target_names[n])
# => Target name:
# setosa
Код выше инициализирует две переменные. Переменная X
отвечает за работу с характеристиками цветков. В ней обозначена длина и ширина лепестков и листочков в сантиметрах. В нашем случае есть четыре признака:
sepal length (cm)
sepal width (cm)
petal length (cm)
petal width (cm)
Перейдем к переменной y
, которая нужна для работы с классификацией цветков. У нее есть три значения (таргета), каждое число обозначает один из видов ириса:
0
—setosa
1
—verticolor
2
—virginica
На датасете цветков ириса аналитики часто учатся работе с табличными данными. Далее мы проанализируем статистические характеристики признаков и количественные показатели таргетов с использованием визуализации.
Гистограмма распределения
Один из частых подходов к визуализации — это графические представление распределения данных.
Для этого подходит гистограмма, которая реализуется с использованием метода hist()
. Аналитик выбирает нужно количество интервалов и задает его через значение bins
. Мы выбрали значение 8:
# Гистограмма распределения
fig, ax = plt.subplots(2, 2)
fig.suptitle("Features of iris flowers")
fig.set_size_inches((10, 10))
n = 0
feature_name = data.feature_names[n]
ax[0, 0].hist(X[:, n], bins=8, linewidth=0.5, edgecolor="white", label=feature_name)
ax[0, 0].legend()
ax[0, 0].grid()
n = 1
feature_name = data.feature_names[n]
ax[0, 1].hist(X[:, n], bins=8, linewidth=0.5, edgecolor="white", label=feature_name)
ax[0, 1].legend()
ax[0, 1].grid()
n = 2
feature_name = data.feature_names[n]
ax[1, 0].hist(X[:, n], bins=8, linewidth=0.5, edgecolor="white", label=feature_name)
ax[1, 0].legend()
ax[1, 0].grid()
n = 3
feature_name = data.feature_names[n]
ax[1, 1].hist(X[:, n], bins=8, linewidth=0.5, edgecolor="white", label=feature_name)
ax[1, 1].legend()
ax[1, 1].grid()
plt.show()
Для построения гистограммы все значения раскладываются по соответствующим числовым интервалам и подсчитывается их количество в каждом интервале. В нашем случае интервалов 8 — столько же столбцов на графике. Ширина столбца и его позиция по горизонтальной оси описывает интервал, а высота — количество значений, которые в него попали.
Код выше можно сократить, если использовать циклы. Положение каждого из признаков на соответствующей области визуализации определяется через выражения n // 2
и n % 2
:
fig, ax = plt.subplots(2, 2)
fig.set_size_inches((10, 10))
fig.suptitle("Features of iris flowers")
for n in range(4):
feature_name = data.feature_names[n]
ax_position = ax[n // 2, n % 2]
ax_position.hist(
X[:, n], bins=8, linewidth=0.5, edgecolor="white", label=feature_name
)
ax_position.legend()
ax_position.grid()
plt.show()
Гистограммы хорошо описывают то, какие наиболее типичные значения признаков встречаются в данных. В примере выше значения sepal width (cm)
сосредоточены в районе 3
.
Чтобы получить ту же информацию о квантилях и медианных значениях, построим еще один график.
Ящик с усами
Чтобы построить этот график, воспользуемся методом boxplot()
. Первый необходимый параметр для него — это набор данных. В нашем случае мы используем ранее инициализированную переменную X
, в которой хранятся значения признаков.
Остальные параметры отвечают за формат и расположение ящиков на области визуализации. Стоит помнить, что positions
должно быть столько, сколько признаков. Мы указали четыре значения, поскольку у нас ровно четыре признака:
fig, ax = plt.subplots()
# fig.set_size_inches((10, 10))
fig.suptitle("Features of iris flowers")
ax.boxplot(X, positions=[2, 4, 6, 8], widths=1.5, patch_artist=True)
ax.grid()
plt.show()
Это тип графиков читают так:
- Синий ящик — это область, в которую попадают большая часть всех значений
- Линия в ящике — значение, меньше которого столько же признаков, сколько и больше него (медиана)
- Усы — границы, в пределах которых лежат практически все признаки (вариативность признаков)
Не всегда удобно размещать признаки на одной области визуализации, потому что значения признаков могут сильно отличаться. В нашем примере этого не происходит — все значения расположены в интервале от 0 до 8. Для примера представим, что среди признаков есть зарплата работника и его возраст. В таком случае лучше выделить отдельную область под каждый признак.
Посмотрим, как сделать это с помощью циклов. При этом мы ограничиваемся каждый раз только одним признаком, вырезая его из данных вот так:
X[:, n]
Так это выглядит на практике:
fig, ax = plt.subplots(2, 2)
fig.set_size_inches((10, 10))
fig.suptitle("Features of iris flowers")
for n in range(4):
ax_position = ax[n // 2, n % 2]
feature_name = data.feature_names[n]
ax_position.set_title(feature_name)
ax_position.boxplot(
X[:, n],
positions=[2],
widths=1.5,
patch_artist=True,
)
ax_position.grid()
plt.show()
Посмотрим на графики выше. Какие-то признаки выходят за пределы усов — значит, в данных есть некоторые нехарактерные значения. Обычно аналитик находит эти значения и выясняет причину их появления — это либо редкий реальный случай, либо ошибка в данных. В нашем примере у признака sepal width (cm)
наблюдаются такие значения.
Рассмотрим еще один график, который добавляет немного статистической информации о данных.
Скрипичная диаграмма
Чтобы построить этот график, заменим метод boxplot()
на violinplot()
в коде предыдущего примера:
fig, ax = plt.subplots(2, 2)
fig.set_size_inches((10, 10))
fig.suptitle("Features of iris flowers")
for n in range(4):
ax_position = ax[n // 2, n % 2]
feature_name = data.feature_names[n]
ax_position.set_title(feature_name)
ax_position.violinplot(
X[:, n],
positions=[2],
widths=1.5,
)
ax_position.grid()
plt.show()
Таким образом мы строим график распределения значений. Там где он толще, туда и попадает наибольшее количество значений. Иногда этот график рассматривают горизонтально. Для этого необходимо добавить параметр vert=False
:
fig, ax = plt.subplots(2, 2)
fig.set_size_inches((10, 10))
fig.suptitle("Features of iris flowers")
for n in range(4):
ax_position = ax[n // 2, n % 2]
feature_name = data.feature_names[n]
ax_position.set_title(feature_name)
ax_position.violinplot(X[:, n], positions=[2], widths=1.5, vert=False)
ax_position.grid()
plt.show()
Мы разобрались с признаками, но оставили без внимания значения целевых переменных. Поработаем и с ними с помощью круговой диаграммы.
Круговая диаграмма
Для работы с целевыми переменными мы сперва посчитаем их количество. Воспользуемся методами библиотеки Numpy:
unique, counts = np.unique(y, return_counts=True)
y_counts = np.asarray((unique, counts)).T
print(y_counts)
# =>
# [[ 0 50]
# [ 1 50]
# [ 2 50]]
Теперь мы можем построить круговую диаграмму. Воспользуемся методом pie()
. Возьмем количество наших таргетов y_counts[:, 1]
в качестве данных для отрисовки.
Зададим параметр autopct
лямбда-функцией, которая формирует подписи на секторах. Этот метод можно дополнительно параметризировать положением на области визуализации и размером. Не забудем также добавить легенду для наших секторов и отрисовать ее в отдельном окошке:
fig, ax = plt.subplots()
fig.set_size_inches((7, 7))
fig.suptitle("Targets of iris flowers")
def autopct_func(pct, y_values):
return f"{pct:.1f}%"
wedges, texts, autotexts = ax.pie(
y_counts[:, 1],
autopct=lambda pct: autopct_func(pct, y_counts[:, 1]),
radius=5,
center=(6, 6),
wedgeprops={"linewidth": 1, "edgecolor": "white"},
frame=True,
)
legend_text = [
f"{name} ({value})" for name, value in zip(data.target_names, y_counts[:, 1])
]
ax.legend(
wedges,
legend_text,
title="Target count",
loc="upper left",
)
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
plt.show()
График выше показывает, сколько цветков ириса мы рассмотрели. В этом случае все классы имеют одинаковый размер по 50 штук. Размеры целевых классов существенно влияют на построение методов машинного обучения и подбор метрик качества обучаемых алгоритмов, поэтому к круговой диаграмме часто прибегают при первичном анализе данных.
Выводы
На этом уроке мы изучили методы статистического анализа данных с использованием визуализации:
- Гистограммы распределений —
hist
- Ящики с усами —
boxplot
- Скрипичные графики —
violinplot
- Круговые диаграммы —
pie
На практических примерах разобрали, когда и как правильно применять рассмотренные методы. Это стандартный набор любого аналитика, с помощью которого можно производить первичный анализ данных, выявлять ошибки в них и даже формулировать гипотезы о их свойствах.
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.