Выброс (Outlier) - Лена Капаца
Выброс (Outlier) by Лена Капаца Jan. 15, 2021 Статистика

Выброс – это наблюдение, удаленное от других в выборке. Другими словами, это Наблюдение (Observation), которое расходится с общей закономерностью Выборки (Sample).

Выбросы обозначены голубым цветом

Стоит различать это определение с Несбалансированным датасетом (Imbalanced Dataset). Хоть в определениях и есть некоторые сходства, однако несбалансированный набор данных с точки зрения Машинного обучения (ML) – это меньший размер выборки одного класса в сравнении с другим.

Источники выбросов

Появление таких наблюдений может быть вызвано:

Выбросы могут быть результатом ошибки во время сбора данных или индикатором расхождения наблюдений. Потому их надлежит исключить из Датасета (Dataset). Однако Дата-сайентисты (Data Scientist) могут столкнуться с трудностями во время разграничения выбросов и нормальных значений, потому и не спешат исключать то или иное наблюдение.

Разновидности выбросов

Выделяют 3 типа выбросов:

Стоимость акции $MOMO

Почему так важно идентифицировать выбросы?

Алгоритмы Машинного обучения чувствительны к диапазону и распределению значений атрибутов. Выбросы могут ввести в заблуждение Модель (Model), что приведет к увеличению времени обучения, меньшей Точности (Accuracy) и, в конечном итоге, к худшим результатам.

Визуальные методы обнаружения выбросов

Выбросы легко обнаружить с помощью следующих графиков:

Математические методы обнаружения выбросов

Наряду с визуальными методами мы также можем использовать некоторые математические функции:

Это далеко не полный список методов для поиска выбросов.

Выбросы и библиотека Scikit-learn

Выбросы можно найти с помощью 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

© Лена Капаца. Все права защищены.