Структурированный подход к изучению нового набора данных
Что такое ЭДА?
EDA, или исследовательский анализ данных, — это процесс изучения и понимания структуры набора данных. Это важная часть любого проекта по машинному обучению, и это инструмент в вашем наборе инструментов, который позволяет вам обращаться к данным, которые вы никогда раньше не видели, и освоиться со всеми видами характеристик.
Я обнаружил, что многие люди сразу же переходят к своим данным, не выполнив должным образом EDA. Особенно после того, как они сделали несколько проектов и думают, что знают, что делают. Для меня это важный первый шаг, который раскрывает так много скрытых жемчужин в данных, что это необходимо.
Для этого процесса я буду использовать набор банковских данных, доступный на Kaggle.
Мой подход к ЭДА
В этой статье описывается обобщенный процесс, который я использую для выполнения EDA. Это может варьироваться от набора данных к набору данных, в зависимости от типа данных, сложности и беспорядка. Однако эти шаги, как правило, одинаковы для всех наборов данных.
- Базовое исследование
- Проверка нулевых и повторяющихся значений
- Работа с выбросами
- Визуализируйте данные
Базовое исследование
Начните с импорта необходимых пакетов. Это мои настройки по умолчанию для всех новых исследований. Я склонен считать, что seaborn
— это удобная оболочка поверх matplotlib
, которая помогает быстрее создавать визуализации.
import numpy as np import pandas as pd import seaborn as sns import matplotlib.pyplot as plt
См. размер, столбцы и образцы строк
После того, как я импортирую данные, есть несколько функций из pandas
, которые я запускаю в первую очередь. Давайте пройдемся по ним.
df.shape (4521, 17)
Из вывода видно, что имеется 4,521
наблюдений с 17
столбцами.
df.info() <class 'pandas.core.frame.DataFrame'> RangeIndex: 4521 entries, 0 to 4520 Data columns (total 17 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 age 4521 non-null int64 1 job 4521 non-null object 2 marital 4521 non-null object 3 education 4521 non-null object 4 default 4521 non-null object 5 balance 4521 non-null int64 6 housing 4521 non-null object 7 loan 4521 non-null object 8 contact 4521 non-null object 9 day 4521 non-null int64 10 month 4521 non-null object 11 duration 4521 non-null int64 12 campaign 4521 non-null int64 13 pdays 4521 non-null int64 14 previous 4521 non-null int64 15 poutcome 4521 non-null object 16 y 4521 non-null object dtypes: int64(7), object(10) memory usage: 600.6+ KB
Способ, которым pandas
импортирует данные, состоит в том, чтобы дать наилучшее предположение о типах данных. Вы можете изменить их позже, если они не импортировались должным образом. В моем случае выше похоже, что большинство типов данных — это integers
и objects
или строки.
Затем отобразите первую и последнюю несколько строк данных.
df.head()
и
df.tail()
См. статистическую сводку
Существует очень мощная команда, которая в одной функции df.describe()
может дать вам представление о ваших данных с высоты птичьего полета. Он покажет количество, среднее значение, стандартное отклонение, минимум, максимум и многое другое. С этими данными вы можете получить довольно хорошее представление о том, с чем вы имеете дело; однако визуализация этого позже добавит к вашему анализу.
# Without any parameters passed into the describe function, it will return numerical only. df.describe().round(2) age balance day duration campaign pdays previous count 4521.00 4521.00 4521.00 4521.00 4521.00 4521.00 4521.00 mean 41.17 1422.66 15.92 263.96 2.79 39.77 0.54 std 10.58 3009.64 8.25 259.86 3.11 100.12 1.69 min 19.00 -3313.00 1.00 4.00 1.00 -1.00 0.00 25% 33.00 69.00 9.00 104.00 1.00 -1.00 0.00 50% 39.00 444.00 16.00 185.00 2.00 -1.00 0.00 75% 49.00 1480.00 21.00 329.00 3.00 -1.00 0.00 max 87.00 71188.00 31.00 3025.00 50.00 871.00 25.00
Проверка категориальных значений
Следующая функция очень удобна при работе с категориальными данными. Сначала выбираются только столбцы типа object
; в нашем случае все текстовые поля являются действительно категориальными. Затем он перебирает каждый из этих столбцов и печатает value_counts
каждого.
# get categorical data cat_data = df.select_dtypes(include=['object']) # show counts values of each categorical variable for colname in cat_data.columns: print (colname) print (cat_data[colname].value_counts(), '\n')
Вот как выглядит пример вывода для одного столбца. Обратите внимание, как здорово это обобщает категориальные данные.
marital married 2797 single 1196 divorced 528 Name: marital, dtype: int64
Проверка нулевых и повторяющихся значений
И пустые значения, и повторяющиеся значения в конечном итоге становятся проблемой в моделях машинного обучения, поэтому давайте проверим их и разберемся с ними.
Нулевые значения
Во-первых, я начинаю с проверки нулевых значений. Потребуется некоторое исследование и интерпретация, чтобы понять, как с ними справляться. Это может варьироваться от простого удаления их, когда их очень мало, до заполнения их значением по умолчанию, например 0
, или заполнения их на основе соседних значений. Заполнение их на основе соседних значений распространено в данных временных рядов, где вы можете заполнить отсутствующее значение средним предыдущего и последующего значений. Панды имеют обширную поддержку с их функцией fillna()
, о которой вы можете прочитать здесь.
В этом примере я изучу, есть ли они, и уберу их из набора данных.
# check for nan/null df.isnull().values.any() # count of nulls per column df.isnull().sum()
и бросить их
# Drop NULL values df.dropna(inplace=True)
Повторяющиеся значения
Далее стоит искать дубликаты. Дубликаты аналогичны нулевым значениям, поскольку их необходимо интерпретировать как фактические чистые, полезные данные или ошибочные данные. Дубликаты также могут быть проблемой в машинном обучении, потому что они могут привести к чрезмерному смещению вашей модели к наблюдениям, которые не являются частью реального набора данных. Вы можете использовать приведенный ниже код либо для проверки дубликатов, либо для их удаления, как это реализовано здесь:
len_before = df.shape[0] df.drop_duplicates(inplace=True) len_after = df.shape[0] print(f"Before = {len_before}") print(f"After = {len_after}") print("") print(f"Total Removed = {len_before - len_after}") Before = 4521 After = 4521 Total Removed = 0
В этом случае не было повторяющихся строк или нулевых значений, но очень важно проверить и обработать их соответствующим образом для вашего варианта использования.
Работа с выбросами
Выбросы — еще одна чрезвычайно распространенная проблема с данными. Выбросы необходимо оценивать, являются ли они хорошими наблюдениями в наборе данных или являются ошибками. Во-первых, вы можете проверить, сколько их. Для нормально распределенных данных наиболее распространенным методом является поиск любых наблюдений, которые лежат за пределами +/- 3 стандартных отклонения. Если вы помните свой урок статистики, правило 68–95–99,7. Это правило гласит, что 99,7% всех данных в нормальном распределении лежат в пределах трех стандартных отклонений от среднего. Когда ваши данные сильно смещены влево или вправо, это не будет правдой. Вы можете использовать диаграммы или гистограммы для проверки нормальности. Я расскажу об этом ниже.
def get_outliers(df): '''Identify the number of outliers +/- 3 standard deviations. Pass this function a data frame and it returns a dictionary''' outs = {} df = df.select_dtypes(include=['int64']) for col in df.columns: # calculate summary statistics data_mean, data_std = np.mean(df[col]), np.std(df[col]) # identify outliers cut_off = data_std * 3 lower, upper = data_mean - cut_off, data_mean + cut_off # identify outliers outliers = [x for x in df[col] if x < lower or x > upper] outs[col] = len(outliers) return outs
Затем передайте dataframe
в функцию, чтобы вернуть количество выбросов.
get_outliers(df) {'age': 44, 'balance': 88, 'day': 0, 'duration': 88, 'campaign': 87, 'pdays': 171, 'previous': 99}
Удаление выбросов
Если вы решите удалить выбросы из набора данных, вот простой способ сделать это. Из scipy.stats
вы можете использовать функцию zscore
для легкого выявления выбросов, аналогично описанному выше методу:
from scipy import stats # build a list of columns that you wish to remove ouliers from out_list = ['balance', 'pdays', 'duration'] # overwrite the dataframe with outlier rows removed. df = df[((np.abs(stats.zscore(df[out_list])) < 3)).all(axis=1)]
Визуализируйте данные
Мне нравится разбивать свои визуализации на две разные части. Явно сначала использовать только одномерные графики или графики, такие как гистограммы и диаграммы для отдельных переменных. Затем я предпочитаю накладывать двумерные (или многомерные) графики по разным переменным. Этот процесс помогает мне разбить анализ на уровни понимания данных.
Одномерные графики
Одномерный график — это именно то, что и звучит — график против одной переменной. Давайте начнем с просмотра диаграмм всех числовых переменных в наборе данных. Давайте начнем с двух самых распространенных: коробчатой диаграммы и гистограммы.
Коробчатые участки
Блочная диаграмма принимает одну переменную и отображает информацию о том, как данные распределяются по ее квартилям, что по существу означает разделение данных на четверти. Этот простой графический пакет содержит много информации и может быть удобным инструментом для сравнения двух или более различных категориальных подсегментов. В приведенном ниже примере показана переменная duration
на блочной диаграмме, а затем она разделена по переменной y' variable, in this case the
y', являющейся целью в модели классификации (да/нет).
plt.figure(figsize=(7,6)) sns.boxplot(x="y", y="duration", data=df, showfliers=True)
Используя этот график, мы видим, что как медиана (линия в центре поля), так и межквартильный размах (IQR) или верхняя и нижняя части поля в целом больше для yes
, чем no
. ценности.
Фантастическая статья о науке о данных о том, как интерпретировать ящичные диаграммы: Понимание ящичных диаграмм.
Гистограммы
Гистограммы показывают распределение одной переменной в «ячейках» или группах данных в зависимости от частоты появления значения. Тем, кто не знаком с ними, может быть трудно сначала прочитать их, потому что их можно перепутать с гистограммами. Однако гистограмма, отображающая две переменные по оси X и оси Y, в то время как ось X в гистограмме представляет собой интервал а ось Y – это число в этой ячейке.
sns.histplot(x='age', data=df, bins=20, kde=True)
Если вы посмотрите на гистограмму переменной age
, то увидите, что число людей в возрасте от 30 до 40 лет в наборе данных имеет наибольшую частоту. Поскольку гистограмма не центрирована, мы называем это асимметрией. В этом случае перекос называется Перекос вправо, поскольку правый конец длиннее.
Двумерные графики
Корреляционная матрица
Абсолютно обязательным двумерным (или многомерным) графиком, который вам нужно запустить, является корреляционная матрица. Это матрица, которая показывает корреляцию между всеми переменными в наборе данных. Это поможет вам определить, какие переменные сильно коррелированы, для таких случаев, как простая линейная регрессия или те, которые не коррелированы, для случаев, когда вы пытаетесь моделировать несколько функций, повышающих производительность вашей модели.
corr = df.corr() f, ax = plt.subplots(figsize=(12, 8)) sns.heatmap(corr, annot=True, square=False, ax=ax, linewidth = 1) plt.title('Pearson Correlation of Features')
По большей части нет сильно коррелированных переменных, кроме pdays
и previous
, между которыми значение положительной корреляции 0.62
. Было бы полезно понять, почему это так и связаны ли они с аналогичными действиями.
Парные участки
Далее идет фантастический инструмент под названием Pair Plots. Аналогичен корреляционной матрице, но дает вам диаграмму рассеяния для каждой из пар X и Y. По диагонали находится KDE или гистограмма переменной.
g.fig.set_size_inches(12,12) g=sns.pairplot(df, diag_kind = 'auto', hue="y")
С помощью всего одной строки кода вы можете быстро визуально оценить отношения, показанные в матрице корреляции. Теперь мы видим визуально, что корреляция между переменными pdays
и previous
невелика.
Дополнительные участки
Отсюда вы можете создать любое количество визуальных элементов, которые помогут вам лучше понять данные. Вам решать, какая история кроется в данных. У меня есть много других примеров в Notebook на Github.
Заключение
EDA — это мощная техника, с которой вы должны освоиться каждый раз, когда начинаете работать с новым набором данных. Существует множество других техник, которые вы можете добавить к этим основам. Отправляйтесь туда, изучите и узнайте свои данные, прежде чем приступить к машинному обучению.
Если вам нравится читать такие истории и вы хотите поддержать меня как писателя, подумайте о том, чтобы зарегистрироваться и стать участником Medium. Это 5 долларов в месяц, что дает вам неограниченный доступ к тысячам статей. Если вы зарегистрируетесь по моей ссылке, я получу небольшую комиссию без каких-либо дополнительных затрат для вас.