Как удалить выбросы в pandas
Перейти к содержимому

Как удалить выбросы в pandas

  • автор:

Как удалить выбросы в Python

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

В этом руководстве объясняется, как идентифицировать и удалять выбросы в Python.

Как идентифицировать выбросы в Python

Прежде чем вы сможете удалить выбросы, вы должны сначала решить, что вы считаете выбросом. Есть два распространенных способа сделать это:

1. Используйте межквартильный диапазон.

Межквартильный размах (IQR) — это разница между 75-м процентилем (Q3) и 25-м процентилем (Q1) в наборе данных. Он измеряет разброс средних 50% значений.

Вы можете определить наблюдение как выброс, если оно в 1,5 раза превышает межквартильный размах, превышающий третий квартиль (Q3), или в 1,5 раза превышает межквартильный размах, меньше первого квартиля (Q1).

Выбросы = наблюдения > Q3 + 1,5*IQR или Q1 – 1,5*IQR

2. Используйте z-значения.

Z-оценка показывает, сколько стандартных отклонений данного значения от среднего. Мы используем следующую формулу для расчета z-показателя:

z = (X — μ) / σ

  • X — это одно необработанное значение данных.
  • μ — среднее значение населения
  • σ — стандартное отклонение населения

Вы можете определить наблюдение как выброс, если его z-оценка меньше -3 или больше 3.

Выбросы = наблюдения с z-показателями> 3 или

Как удалить выбросы в Python

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

import numpy as np import pandas as pd import scipy.stats as stats #create dataframe with three columns 'A', 'B', 'C' np.random.seed(10) data = pd.DataFrame(np.random.randint(0, 10, size=(100, 3)), columns=['A', 'B', 'C']) #view first 10 rows data[:10] A B C 0 13.315865 7.152790 -15.454003 1 -0.083838 6.213360 -7.200856 2 2.655116 1.085485 0.042914 3 -1.746002 4.330262 12.030374 4 -9.650657 10.282741 2.286301 5 4.451376 -11.366022 1.351369 6 14.845370 -10.798049 -19.777283 7 -17.433723 2.660702 23.849673 8 11.236913 16.726222 0.991492 9 13.979964 -2.712480 6.132042 

Затем мы можем определить и удалить выбросы, используя метод z-оценки или метод межквартильного диапазона:

Метод Z-оценки:

#find absolute value of z-score for each observation z = np.abs(stats.zscore(data)) #only keep rows in dataframe with all z-scores less than absolute value of 3 data_clean = data[(z<3).all(axis=1)] #find how many rows are left in the dataframe data_clean.shape (99,3) 

Метод межквартильного диапазона:

#find Q1, Q3, and interquartile range for each column Q1 = data.quantile(q=.25) Q3 = data.quantile(q=.75) IQR = data.apply(stats.iqr) #only keep rows in dataframe that have values within 1.5\*IQR of Q1 and Q3 data_clean = data[~((data < (Q1-1.5\*IQR)) | (data >(Q3+1.5\*IQR))).any(axis=1)] #find how many rows are left in the dataframe data_clean.shape (89,3) 

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

Когда удалять выбросы

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

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

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

Дополнительные ресурсы

Если вы работаете с несколькими переменными одновременно, вы можете использовать расстояние Махаланобиса для обнаружения выбросов.

Обнаружение выбросов в Machine Learning

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

Выбросы (англ. outliers) – объекты, значения признаков которых сильно отличаются от признаков основной массы объектов. То есть это объекты, сильно выпадающие из общей картины.

Откуда же берутся выбросы? Во-первых, появление выбросов может быть обусловлено технической стороной эксперимента (например, в процессе сбора данных сменилось оборудование или у прежнего изменилась чувствительность датчиков). Во-вторых, на появление выбросов может влиять человеческий фактор. Возможны и другие причины: изменение правил проведения эксперимента, какая-то случайность или действительно уникальное значение.

В любом случае, какой бы ни была причина появления выбросов, они мешают алгоритмам обучаться, т.е. не дают построить хорошее решающее правило. Конечно, существуют алгоритмы машинного обучения (робастные), которые сами удачно обрабатывают выбросы. Но большинство алгоритмов путается в выбросах, пытается предсказать соответствующее значение для них, а в итоге плохо предсказывает значения как для выбросов, так и для остальных значений. Поэтому для получения хорошего решающего правила для основной массы объектов рекомендуется выявлять аномалии и обрабатывать их.

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

import pandas as pd import seaborn as sns 

Для рассмотрения методов воспользуемся встроенным датасетом Boston библиотеки Scikit-learn. Он содержит набор данных о жилье в Бостоне (506 объектов, 13 признаков, целевая переменная – цена за дом).

from sklearn.datasets import load_boston dataset = load_boston() df = pd.DataFrame(dataset.data, columns = dataset.feature_names) df['target'] = dataset.target 

Для начала рассмотрим признак ‘RM’ – количество комнат в доме. Построим «ящик с усами» или бокс-диаграмму. Это один из способов визуального представления распределения. Нижний и верхний концы ящика соответствуют 1-му и 3-му квартилям (25% и 75% квантилям соответственно), а горизонтальная линия внутри ящика – медиане. Верхний «ус» продолжается вверх вплоть до максимального значения, но не выше полуторного межквартильного расстояния от верхней кромки ящика. Аналогично нижний ус продолжается вниз до минимального значения, но не ниже полуторного межквартильного расстояния от нижней кромки ящика. Концы «усов» обозначаются небольшими горизонтальными линиями. А за пределами усов значения изображаются в виде отдельных точек – эти значения можно считать выбросами.

figure=df.boxplot(column='RM')

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

sns.distplot(df['RM'].dropna())

Как видно по графику данные в столбце ‘RM’ распределены нормально. Воспользуемся следующей формулой для определения границ выбросов:

Нижняя граница = Среднее значение – 3 * Стандартное отклонение

Верхняя граница = Среднее значение + 3 * Стандартное отклонение

lower_bound = df['RM'].mean()-3*df['RM'].std() upper_bound = df['RM'].mean()+3*df['RM'].std() print(lower_bound, upper_bound) 

Таким образом, все значения меньшие, чем 4.1768 и большие, чем 8.3925 будут считаться выбросами.

Теперь рассмотрим данные из столбца ‘DIS’ (взвешенные расстояния до пяти бостонских центров занятости). Выведем аналогичные графики.

figure=df.boxplot(column='DIS')

sns.distplot(df['DIS'].dropna())

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

Нижняя граница = Первый квартиль – 1.5 * Межквартильное расстояние,

Верхняя граница = Третий квартиль + 1.5 * Межквартильное расстояние,

Межквартильное расстояние (IQR) = 75% квантиль – 25% квантиль

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

Нижняя граница = Первый квартиль – 3 * Межквартильное расстояние,

Верхняя граница = Третий квартиль + 3 * Межквартильное расстояние

lower_bound = df['DIS'].quantile(0.25)-3*IQR upper_bound = df['DIS'].quantile(0.75)+3*IQR print(lower_bound, upper_bound) 

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

Обнаружение статистических выбросов в Python

Параллельно с выходом материала «Обнаружение выбросов в R» предлагаем посмотреть, как те же методы обнаружения выбросов реализовать в Python.

Данные

Для наглядности эксперимента возьмём тот же пакет данных mpg — скачать его в виде csv-таблицы можно с GitHub. Импортируем библиотеки и читаем таблицу в DataFrame:

import pandas as pd import matplotlib.pyplot as plt import numpy as np df = pd.read_csv('mpg.csv')

Минимальные и максимальные значения

Тут всё просто. Выводим описание всего датасета методом describe():

df.describe()

Гистограмма

Такой график тоже можно построить в одну строку, используя внутренние средства библиотеки pandas:

df.hwy.plot(kind='hist', density=1, bins=20, stacked=False, alpha=.5, color='grey')

Box plot

В случае ящика с усами далеко идти тоже не приходится — в pandas есть метод и для этого:

_, bp = df.hwy.plot.box(return_type='both')

Получим точки с графика и выведем их в таблице, используя объект bp:

outliers = [flier.get_ydata() for flier in bp["fliers"]][0] df[df.hwy.isin(outliers)]

Процентили

При помощи метода quantile получаем соответствующую нижнюю и верхнюю границы, а затем выводим всё, что выходит за их рамки:

lower_bound = df.hwy.quantile(q=0.025) upper_bound = df.hwy.quantile(q=0.975) df[(df.hwy < lower_bound) | (df.hwy >upper_bound)]

Фильтр Хэмпеля

Мы используем реализацию фильтра Хэмпеля, найденную на StackOverflow

Опишем функцию, которая заменяет на nan все значения, у которых разница с медианой больше, чем три медианных абсолютных отклонения.

def hampel(vals_orig): vals = vals_orig.copy() difference = np.abs(vals.median()-vals) median_abs_deviation = difference.median() threshold = 3 * median_abs_deviation outlier_idx = difference > threshold vals[outlier_idx] = np.nan return(vals)

И применим к нашему набору данных:

hampel(df.hwy) 0 29.0 1 29.0 2 31.0 3 30.0 4 26.0 . 229 28.0 230 29.0 231 26.0 232 26.0 233 26.0 Name: hwy, Length: 234, dtype: float64

В выводе нет nan-значений, а значит и выбросов фильтр Хэмпеля не обнаружил.

Тест Граббса

Автор реализации теста Граббса и теста Рознера для Python

Опишем три функции: первая находит значение критерия Граббса и максимальное значение в наборе данных, вторая — критическое значение с учётом объёма выборки и уровня значимости, а третья проверяет, является ли значение с максимальным индексом выбросом:

import numpy as np from scipy import stats def grubbs_stat(y): std_dev = np.std(y) avg_y = np.mean(y) abs_val_minus_avg = abs(y - avg_y) max_of_deviations = max(abs_val_minus_avg) max_ind = np.argmax(abs_val_minus_avg) Gcal = max_of_deviations / std_dev print(f"Grubbs Statistics Value: ") return Gcal, max_ind def calculate_critical_value(size, alpha): t_dist = stats.t.ppf(1 - alpha / (2 * size), size - 2) numerator = (size - 1) * np.sqrt(np.square(t_dist)) denominator = np.sqrt(size) * np.sqrt(size - 2 + np.square(t_dist)) critical_value = numerator / denominator print(f"Grubbs Critical Value: ") return critical_value def check_G_values(Gs, Gc, inp, max_index): if Gs > Gc: print(f" is an outlier") else: print(f" is not an outlier")

Заменим значение в 34 строке на 212:

df.hwy[34] = 212

И выполним три функции:

Gcritical = calculate_critical_value(len(df.hwy), 0.05) Gstat, max_index = grubbs_stat(df.hwy) check_G_values(Gstat, Gcritical, df.hwy, max_index) Grubbs Critical Value: 3.652090929984981 Grubbs Statistics Value: 13.745808761040397 212 is an outlier

Тест Рознера

Для теста Рознера достаточно дописать одну функцию, которая принимает набор данных, уровень значимости и число потенциальных выбросов:

def ESD_test(input_series, alpha, max_outliers): for iteration in range(max_outliers): Gcritical = calculate_critical_value(len(input_series), alpha) Gstat, max_index = grubbs_stat(input_series) check_G_values(Gstat, Gcritical, input_series, max_index) input_series = np.delete(input_series, max_index)

Используя функцию на нашем наборе данных получаем, что значение 212 является выбросом, а 44 — нет:

ESD_test(np.array(df.hwy), 0.05, 3) Grubbs Critical Value: 3.652090929984981 Grubbs Statistics Value: 13.745808761040408 212 is an outlier Grubbs Critical Value: 3.6508358337727187 Grubbs Statistics Value: 3.455960616168714 44 is not an outlier Grubbs Critical Value: 3.649574509044683 Grubbs Statistics Value: 3.5561478280392245 44 is not an outlier

Как удалить в dataframe выбросы с помощью boxplot?

введите сюда описание изображения

Есть данные с выбросами. Искала вариант удаления выбросов с помощью boxplot , но не нашла способ применить такой вариант:

Q1 = df['y'].quantile(0.25) Q3 = df['y'].quantile(0.75) IQR = Q3 - Q1 df[(df['y'] < Q1-1.5*IQR ) | (df['y'] >Q1+1.5*IQR)]['y'] 

Это маска? Как с ее помощью изменить основной датасет, чтобы он был без выбросов по полю y , значения которого как выбросы надо удалить: меньше Q1-1,5 * IQR и выше Q3 + 1,5 * IQR?

Отслеживать

51.6k 203 203 золотых знака 65 65 серебряных знаков 250 250 бронзовых знаков

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *