Выброс – это наблюдение, удаленное от других в выборке. Другими словами, это Наблюдение (Observation), которое расходится с общей закономерностью Выборки (Sample).
Стоит различать это определение с Несбалансированным датасетом (Imbalanced Dataset). Хоть в определениях и есть некоторые сходства, однако несбалансированный набор данных с точки зрения Машинного обучения (ML) – это меньший размер выборки одного класса в сравнении с другим.
Появление таких наблюдений может быть вызвано:
Выбросы могут быть результатом ошибки во время сбора данных или индикатором расхождения наблюдений. Потому их надлежит исключить из Датасета (Dataset). Однако Дата-сайентисты (Data Scientist) могут столкнуться с трудностями во время разграничения выбросов и нормальных значений, потому и не спешат исключать то или иное наблюдение.
Выделяют 3 типа выбросов:
Алгоритмы Машинного обучения чувствительны к диапазону и распределению значений атрибутов. Выбросы могут ввести в заблуждение Модель (Model), что приведет к увеличению времени обучения, меньшей Точности (Accuracy) и, в конечном итоге, к худшим результатам.
Выбросы легко обнаружить с помощью следующих графиков:
Наряду с визуальными методами мы также можем использовать некоторые математические функции:
Это далеко не полный список методов для поиска выбросов.
Выбросы можно найти с помощью Scikit-learn. Начнем с импорта необходимых библиотек:
import numpy as np
import pandas as pd
import matplotlib
import matplotlib.pyplot as plt
import seaborn as sns
import sklearn
from sklearn import datasets
import scipy
from scipy import stats
Затем мы загрузим "Бостонский датасет" о ценах на недвижимость:
# Используем встроенную функцию load_boston
boston = datasets.load_boston(return_X_y = False)
# Преобразуем данные в датафрейм
boston_df = pd.DataFrame(boston.data)
# Присвоим названиям столбцов заранее подготовленный список feature_names
boston_df.columns = boston.feature_names
boston_df.head()
Мы будем работать со следующим Датафреймом (DataFrame):
Названия признаков имеют следующие значения:
Отобразим ящик с усами для одного из признаков – расстояния от бостонских центров занятости:
sns.boxplot(x = boston_df['DIS'])
Теперь – точечную диаграмму:
fig, ax = plt.subplots(figsize = (16, 8))
ax.scatter(boston_df['INDUS'], boston_df['TAX'])
ax.set_xlabel('INDUS')
ax.set_ylabel('TAX')
plt.show()
Обратимся к математическим методам обнаружения выбросов и начнем со Стандартизированной оценки:
# Получим абсолютное значение (модуль) чисел признаков
z = np.abs(stats.zscore(boston_df))
print(z)
Мы получим полный перечень стандартизированных оценок для каждого значения признака:
[[0.41978194 0.28482986 1.2879095 ... 1.45900038 0.44105193 1.0755623 ]
[0.41733926 0.48772236 0.59338101 ... 0.30309415 0.44105193 0.49243937]
[0.41734159 0.48772236 0.59338101 ... 0.30309415 0.39642699 1.2087274 ]
...
[0.41344658 0.48772236 0.11573841 ... 1.17646583 0.44105193 0.98304761]
[0.40776407 0.48772236 0.11573841 ... 1.17646583 0.4032249 0.86530163]
[0.41500016 0.48772236 0.11573841 ... 1.17646583 0.44105193 0.66905833]]
Сузим область поиска и отсечем нормальные значения:
# Отсечем значения, лежашие в пределах трех стандартных отклонений
threshold = 3
print(np.where(z > 3))
Список значительно сузился:
(array([ 55, 56, 57, 102, 141, 142, 152, 154, 155, 160, 162, 163, 199,
200, 201, 202, 203, 204, 208, 209, 210, 211, 212, 216, 218, 219,
220, 221, 222, 225, 234, 236, 256, 257, 262, 269, 273, 274, 276,
277, 282, 283, 283, 284, 347, 351, 352, 353, 353, 354, 355, 356,
357, 358, 363, 364, 364, 365, 367, 369, 370, 372, 373, 374, 374,
380, 398, 404, 405, 406, 410, 410, 411, 412, 412, 414, 414, 415,
416, 418, 418, 419, 423, 424, 425, 426, 427, 427, 429, 431, 436,
437, 438, 445, 450, 454, 455, 456, 457, 466]), array([ 1, 1, 1, 11, 12, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1,
1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 3, 3, 1, 5,
5, 3, 3, 3, 3, 3, 3, 1, 3, 1, 1, 7, 7, 1, 7, 7, 7,
3, 3, 3, 3, 3, 5, 5, 5, 3, 3, 3, 12, 5, 12, 0, 0, 0,
0, 5, 0, 11, 11, 11, 12, 0, 12, 11, 11, 0, 11, 11, 11, 11, 11,
11, 0, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11]))
Удалим из датасета значения, чей Z-критерий меньше 3:
clean_boston_df = boston_df
clean_boston_df = clean_boston_df[z < 3].all(axis = 1)
display(clean_boston_df.shape)
Размер датасета слегка изменился:
(6478,)
Рассмотрим еще один способ – межквартильный размах:
# Первая квантиль
Q1 = boston_df.quantile(0.25)
# Третья квантиль
Q3 = boston_df.quantile(0.75)
# Межквантильное расстояние
IQR = Q3 - Q1
print(IQR)
Применив метод quantile()
к датасету, мы получили список межквартильных размахов для каждого признака датасета:
CRIM 3.595038
ZN 12.500000
INDUS 12.910000
CHAS 0.000000
NOX 0.175000
RM 0.738000
AGE 49.050000
DIS 3.088250
RAD 20.000000
TAX 387.000000
PTRATIO 2.800000
B 20.847500
LSTAT 10.005000
dtype: float64
Очистим набор данных с помощью специального условия:
# Это причудливое выражение – прекрасный способ отфильтровать значения, выходящие за пределы
# трех межквартильных размахов (по полторы с каждой стороны), причем с обеих сторон.
# Мы намеренно отыскиваем значения, выходящие за эти пределы и инвертируем их тильдой ('~'),
# тем самым выделяя нормальные значения в отдельный временный список.
clean_iqr_boston_df = boston_df[~((boston_df < (Q1 - 1.5 * IQR)) | (boston_df > (Q3 + 1.5 * IQR))).any(axis = 1)]
clean_iqr_boston_df.shape
(274, 13)
Ноутбук, не требующий дополнительной настройки на момент написания статьи, можно скачать здесь.
Попробуйте наши курсы по Машинному обучению на Udemy.
Фото: @woblack
© Лена Капаца. Все права защищены.