Получите подробное представление об обнаружении выбросов и о том, как реализовать 3 простых, интуитивно понятных и мощных алгоритма обнаружения выбросов в Python.

Я уверен, что вы сталкивались с несколькими из следующих сценариев:

  1. Ваша модель работает не так, как вы хотели.
  2. Вы не можете не заметить, что некоторые моменты, кажется, сильно отличаются от остальных.

Что ж, поздравляю, потому что в ваших данных могут быть выбросы!

Что такое выбросы?

В статистике выброс - это точка данных, которая значительно отличается от других наблюдений. Из рисунка выше мы можем ясно видеть, что хотя большинство точек лежат внутри и вокруг линейной гиперплоскости, можно увидеть, что одна точка отличается от остальных. Это выброс.

Например, взгляните на список ниже:

[1,35,20,32,40,46,45,4500]

Здесь ясно видно, что 1 и 4500 являются выбросами в наборе данных.

Почему в моих данных есть выбросы?

Обычно выбросы могут возникать в одном из следующих сценариев:

  1. Иногда они могут возникать случайно, возможно, из-за ошибки измерения.
  2. Иногда они могут встречаться в данных, поскольку данные редко бывают на 100% чистыми без каких-либо выбросов.

Почему выбросы - проблема?

Вот несколько причин:

  1. Линейные модели

Предположим, у вас есть данные, и вы хотите спрогнозировать на их основе цены на жилье с помощью линейной регрессии. Возможная гипотеза могла бы выглядеть так:

В этом случае мы на самом деле слишком хорошо подгоняем данные (переоснащение). Однако обратите внимание, что все точки расположены примерно в одном диапазоне.

Теперь посмотрим, что произойдет, когда мы добавим выброс.

Ясно, что мы видим, как изменилась наша гипотеза, и поэтому вывод будет намного хуже, чем без выброса. К линейным моделям относятся:

  • Перцептрон
  • Линейная + логистическая регрессия
  • Нейронные сети
  • KNN

2. Расчет данных

Распространенный сценарий - отсутствие данных, и можно выбрать один из двух подходов:

  1. Удалить экземпляры с отсутствующими строками
  2. Данные о вменении с использованием статистического метода

Если бы мы выбрали второй вариант, у нас могли бы возникнуть проблемы с вменением, поскольку выбросы могут сильно изменить значения статистических методов. Например, возвращаясь к нашим вымышленным данным без выбросов:

# Data with no outliers
np.array([35,20,32,40,46,45]).mean() = 36.333333333333336
# Data with 2 outliers
np.array([1,35,20,32,40,46,45,4500]).mean() = 589.875

Ясно, что это довольно крайняя аналогия, но идея остается той же; Выбросы в наших данных обычно представляют собой проблему, поскольку выбросы могут вызвать серьезные проблемы при статистическом анализе и моделировании. Однако в этой статье мы рассмотрим несколько способов их обнаружения и борьбы с ними.

Решение 1: DBSCAN

Пространственная кластеризация приложений с шумом на основе плотности (или, проще говоря, DBSCAN) на самом деле является неконтролируемым алгоритмом кластеризации, как и KMeans. Однако одно из его применений - это также возможность обнаруживать выбросы в данных.

DBSCAN популярен, потому что он может находить нелинейно разделяемые кластеры, чего нельзя сделать с помощью KMeans и Gaussian Mixture. Он хорошо работает, когда кластеры и достаточно плотные, и разделены областями с низкой плотностью.

Общий обзор того, как работает DBSCAN

Алгоритм определяет кластеры как непрерывные области с высокой плотностью. Алгоритм довольно простой:

  1. Для каждого экземпляра подсчитывается, сколько экземпляров находится на небольшом расстоянии ε (эпсилон) от него. Этот регион называется ε-окрестностью экземпляра.
  2. Если в ε-окрестности экземпляра находится более min_samples экземпляров, он считается основным экземпляром. Это означает, что экземпляр находится в области с высокой плотностью (регион, в котором много экземпляров).
  3. Все экземпляры внутри ε-окрестности основного экземпляра назначаются одному кластеру. Это может включать другие экземпляры ядра, поэтому одна длинная последовательность соседних экземпляров ядра образует единый кластер.
  4. Любые экземпляры, которые не являются основным экземпляром или не расположены в ε-окрестности какого-либо основного экземпляра, являются выбросами.

DBSCAN в действии

Алгоритм DBSCAN очень прост в использовании благодаря интуитивно понятному API Scikit-Learn. Давайте посмотрим, как работает алгоритм:

from sklearn.cluster import DBSCAN 
from sklearn.datasets import make_moons
X, y = make_moons(n_samples=1000, noise=0.05)
dbscan = DBSCAN(eps=0.2, min_samples=5)
dbscan.fit(X)

Здесь мы создадим экземпляр DBSCAN с длиной ε-окрестности 0,05, а 5 - это минимальное количество выборок, необходимых для того, чтобы экземпляр считался основным экземпляром.

Помните, что мы не передаем наши метки, так как это неконтролируемый алгоритм . Мы можем увидеть метки меток, созданных алгоритмом, используя следующую команду:

dbscan.labels_
OUT:
array([ 0,  2, -1, -1,  1,  0,  0,  0, ...,  3,  2,  3,  3,  4,  2,  6,  3])

Обратите внимание, что некоторые метки имеют значения, равные -1: это выбросы.

DBSCAN не имеет метода прогнозирования, только метод fit_predict, что означает, что он не может кластеризовать новые экземпляры. Вместо этого мы можем использовать другой классификатор для обучения и прогнозирования. В этом примере давайте использовать KNN:

from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors=50)
knn.fit(dbscan.components_, dbscan.labels_[dbscan.core_sample_indices_])
X_new = np.array([[-0.5, 0], [0, 0.5], [1, -0.1], [2, 1]])
knn.predict(X_new)
OUT:
array([1, 0, 1, 0])

Здесь мы подбираем классификатор KNN для образцов керна и их соответствующих соседей.

Однако мы сталкиваемся с одной проблемой; мы предоставили данные KNN без каких-либо выбросов. Это проблематично, поскольку это заставит KNN выбрать кластер для новых экземпляров, даже если новый экземпляр действительно является выбросом.

Чтобы бороться с этим, мы используем метод «коленей» классификатора KNN, который, учитывая набор экземпляров, возвращает расстояния и индексы k ближайших соседей обучающего набора. Затем мы можем установить максимальное расстояние, и если экземпляр превышает это расстояние, мы квалифицируем его как выброс:

y_dist, y_pred_idx = knn.kneighbors(X_new, n_neighbors=1)
y_pred = dbscan.labels_[dbscan.core_sample_indices_][y_pred_idx]
y_pred[y_dist > 0.2] = -1
y_pred.ravel()
OUT:
array([-1, 0, 1, -1])

Здесь мы обсудили и реализовали DBSCAN для обнаружения аномалий. DBSCAN великолепен, потому что он быстрый, имеет всего два гиперпараметра и устойчив к выбросам.

Решение 2: IsolationForest

IsolationForest - это алгоритм обнаружения аномалий с обучением ансамблю, который особенно полезен при обнаружении выбросов в наборах данных большой размерности. Алгоритм в основном делает следующее:

  1. Он создает случайный лес, в котором деревья решений выращиваются случайным образом: в каждом узле случайным образом выбираются функции, и он выбирает случайное пороговое значение, чтобы разделить набор данных на два.
  2. Он продолжает вырезать набор данных до тех пор, пока все экземпляры не будут изолированы друг от друга.
  3. Аномалия обычно находится далеко от других экземпляров, поэтому в среднем (по всем деревьям решений) она становится изолированной за меньшее количество шагов, чем обычные экземпляры.

ИзоляцияЛес в действии

Опять же, благодаря интуитивно понятному API Scikit-Learn мы можем легко реализовать класс IsolationForest. Давайте посмотрим, как работает алгоритм:

from sklearn.ensemble import IsolationForest
from sklearn.metrics import mean_absolute_error
import pandas as pd

Мы также импортируем mean_absolute_error, чтобы измерить нашу ошибку. Для данных мы будем использовать набор данных, который можно получить из GitHub Джейсона Браунли:

url='https://raw.githubusercontent.com/jbrownlee/Datasets/master/housing.csv'
df = pd.read_csv(url, header=None)
data = df.values
# split into input and output elements
X, y = data[:, :-1], data[:, -1]

Прежде чем мы создадим Isolation Forest, давайте попробуем подогнать к данным обычную модель линейной регрессии и получить нашу MAE:

from sklearn.linear_model import LinearRegression
lr = LinearRegression()
lr.fit(X,y)
mean_absolute_error(lr.predict(X),y)
OUT:
3.2708628109003177

Относительно хороший результат. А теперь давайте посмотрим, сможет ли Изоляционный лес улучшить счет, удалив аномалии!

Сначала мы создадим экземпляр нашего IsolationForest:

iso = IsolationForest(contamination='auto',random_state=42)

Наиболее важным гиперпараметром в алгоритме, вероятно, является параметр загрязнение, который используется для оценки количества выбросов в наборе данных. Это значение от 0,0 до 0,5 и по умолчанию установлено на 0,1.

Однако, по сути, это случайный случайный лес, поэтому в алгоритме также могут использоваться все гиперпараметры случайного леса.

Далее подгоним данные под алгоритм:

y_pred = iso.fit_predict(X,y)
mask = y_pred != -1

Обратите внимание, как мы также отфильтровываем значения прогнозов = -1, как и в DBSCAN, они считаются выбросами.

Теперь мы переназначим X и Y с данными, отфильтрованными по выбросам:

X,y = X[mask,:],y[mask]

А теперь давайте попробуем подогнать нашу модель линейной регрессии к данным и измерить MAE:

lr.fit(X,y)
mean_absolute_error(lr.predict(X),y)
OUT:
2.643367450077622

Ух ты, неплохое снижение стоимости. Это наглядно демонстрирует мощь Леса Изоляции.

Решение 3. Коробчатые диаграммы + метод Таки

Хотя коробчатые диаграммы - довольно распространенный способ выявления выбросов, я действительно считаю, что последний, вероятно, наиболее недооцененный метод выявления выбросов. Но прежде чем мы перейдем к методу Таки, давайте поговорим о коробчатых графиках:

Коробчатые графики

Коробчатые диаграммы, по сути, предоставляют графический способ отображения числовых данных с помощью квантилей. Это очень простой, но эффективный способ визуализации выбросов.

Верхние и нижние усы показывают границы распределения, а все, что выше или ниже, считается выбросом. На рисунке выше все, что выше ~ 80 и ниже ~ 62, считается выбросом.

Как работают Boxplots

По сути, блочная диаграмма работает путем разделения набора данных на 5 частей:

  • Мин: самая низкая точка данных в распределении без учета выбросов.
  • Макс: самая высокая точка данных в распределении без каких-либо выбросов.
  • Медиана (Q 2/50-й процентиль): среднее значение набора данных.
  • Первый квартиль (Q 1/25-й процентиль): медиана нижней половины набора данных.
  • Квартиль третей (Q3 / 75-й процентиль): медиана верхней половины набора данных.

Межквартильный размах (IQR) важен, поскольку он определяет выбросы. По сути, это следующее:

IQR = Q3 - Q1
Q3: third quartile
Q1: first quartile

На прямоугольной диаграмме расстояние 1,5 * IQR измеряется и охватывает более высокие наблюдаемые точки набора данных. Точно так же расстояние 1,5 * IQR измеряется в нижних наблюдаемых точках набора данных. Все, что находится за пределами этих расстояний, является исключением. Более конкретно:

  • Если наблюдаемые точки находятся ниже (Q1 - 1,5 * IQR) или нижнего уса диаграммы, то они считаются выбросами.
  • Точно так же, если наблюдаемые точки находятся выше (Q3 + 1,5 * IQR) или верхнего уса диаграммы, то они также считаются выбросами.

Коробчатые сюжеты в действии

Давайте посмотрим, как мы можем обнаружить выбросы с помощью коробчатых диаграмм в Python!

import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
X = np.array([45,56,78,34,1,2,67,68,87,203,-200,-150])
y = np.array([1,1,0,0,1,0,1,1,0,0,1,1])

Давайте построим диаграмму наших данных:

sns.boxplot(X)
plt.show()

Итак, мы видим, что у нас есть медианное значение 50 и 3 выброса в наших данных, согласно нашему коробчатому графику. Избавимся от этих точек:

X = X[(X < 150) & (X > -50)]
sns.boxplot(X)
plt.show()

Здесь я в основном установил порог, так что все точки меньше -50 и больше 150 будут исключены. И результат; равномерное распределение!

Обнаружение выбросов по методу Таки

Обнаружение выбросов методом tuckey на самом деле является невизуальным методом ящичной диаграммы; метод тот же, за исключением того, что нет визуализации.

Причина, по которой я иногда предпочитаю этот метод, а не коробчатую диаграмму, заключается в том, что иногда взгляд на визуализацию и грубая оценка того, какой порог должен быть установлен, на самом деле неэффективен.

Вместо этого мы можем закодировать алгоритм, который может фактически возвращать экземпляры, которые он определил как выбросы.

Код для реализации следующий:

import numpy as np
from collections import Counter
def detect_outliers(df, n, features):
    # list to store outlier indices
    outlier_indices = []
    # iterate over features(columns)
    for col in features:
        # Get the 1st quartile (25%)
        Q1 = np.percentile(df[col], 25)
        # Get the 3rd quartile (75%)
        Q3 = np.percentile(df[col], 75)
        # Get the Interquartile range (IQR)
        IQR = Q3 - Q1
        # Define our outlier step
        outlier_step = 1.5 * IQR
       # Determine a list of indices of outliers
       outlier_list_col = df[(df[col] < Q1 - outlier_step) |     (df[col] > Q3 + outlier_step)].index
   # append outlier indices for column to the list of outlier indices 
        outlier_indices.extend(outlier_list_col)
   # select observations containing more than 2 outliers
    outlier_indices = Counter(outlier_indices)        
    multiple_outliers = list(k for k, v in outlier_indices.items() if v > n)
return multiple_outliers
# detect outliers from list of features
list_of_features = ['x1', 'x2']
# params dataset, number of outliers for rejection, list of features Outliers_to_drop = detect_outliers(dataset, 2, list_of_features)

По сути, этот код делает следующее:

  1. По каждой функции он получает:
  • 1-й квартиль
  • 3-й квартиль
  • IQR

2. Затем он определяет шаг выброса, который, как и в коробчатых диаграммах, составляет 1,5 * IQR.

3. Он обнаруживает выбросы по:

  • Проверка, соответствует ли наблюдаемая точка ‹Q1 - ступень выброса
  • Проверка того, является ли наблюдаемая точка Q3 + выпадающим шагом

4. Затем он проверяет выбранные наблюдения, которые имеют k выбросов (в данном случае k = 2).

Заключение

Подводя итог, можно сказать, что существует множество алгоритмов обнаружения выбросов, но мы рассмотрели 3 наиболее распространенных: DBSCAN, IsolationForest и Boxplots. Я призываю вас:

  1. Попробуйте эти методы на Titanic Dataset. Какой из них лучше всего обнаруживает выбросы?
  2. Поищите другие методы обнаружения выбросов и посмотрите, работают ли они лучше или хуже тех, которые вы пробовали изначально.

Я действительно ценю своих последователей, и я очень надеюсь, что буду постоянно писать и давать всем отличную пищу для размышлений. А пока я должен попрощаться;}