Как обнаружить и обработать выбросы (outliers) в данных?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Обнаружение и обработка выбросов (Outliers)
Выброс — это значение в данных, которое значительно отличается от других наблюдений и может свидетельствовать об ошибке измерения, ошибке ввода данных или редком, но реальном явлении.
Методы обнаружения выбросов
1. Правило Трёх Сигм (3-Sigma Rule)
Значение считается выбросом, если оно находится более чем в 3 стандартных отклонениях от среднего:
import numpy as np
import pandas as pd
data = pd.Series([1, 2, 3, 4, 5, 100]) # 100 — явный выброс
mean = data.mean()
std = data.std()
# Вычисляем z-score
z_scores = np.abs((data - mean) / std)
# Выбросы: |z| > 3
outliers = data[z_scores > 3]
print(f"Выбросы: {outliers.values}")
Предположение: данные распределены нормально. Для нормального распределения:
- 68% значений в пределах ±1σ
- 95% значений в пределах ±2σ
- 99.7% значений в пределах ±3σ
2. Метод Межквартильного Размаха (IQR)
Более надёжный метод, не требует нормальности:
Q1 = data.quantile(0.25) # 25-й процентиль
Q3 = data.quantile(0.75) # 75-й процентиль
IQR = Q3 - Q1
# Выбросы вне интервала
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers = data[(data < lower_bound) | (data > upper_bound)]
print(f"Выбросы: {outliers.values}")
# Визуализация
import matplotlib.pyplot as plt
plt.boxplot(data)
plt.show()
Интерпретация:
- Коэффициент 1.5 — стандарт (исходит из нормального распределения)
- 1.5 → умеренные выбросы (можно рассмотреть)
- 3.0 → экстремальные выбросы (почти всегда удаляют)
3. Z-Score Метод
from scipy import stats
# Стандартизируем данные
z_scores = np.abs(stats.zscore(data))
# Выбросы при |z| > 2.5 или > 3
outlier_mask = z_scores > 2.5
outliers = data[outlier_mask]
Пороги:
- |z| > 2.0 → 95% доверие (слишком мягкое)
- |z| > 2.5 → 98% доверие (оптимально)
- |z| > 3.0 → 99.7% доверие (жёсткое)
4. Modified Z-Score (для асимметричных данных)
Менее чувствителен к самим выбросам:
from scipy.stats import median_abs_deviation
median = np.median(data)
mad = median_abs_deviation(data)
modified_z_scores = 0.6745 * (data - median) / mad
outliers = data[np.abs(modified_z_scores) > 3.5]
5. Isolation Forest (Машинное обучение)
Для многомерных данных:
from sklearn.ensemble import IsolationForest
X = data.reshape(-1, 1) # Конвертируем в 2D массив
# -1 = выброс, 1 = нормальное значение
iso_forest = IsolationForest(contamination=0.1) # 10% выбросов
predictions = iso_forest.fit_predict(X)
outliers = data[predictions == -1]
print(f"Выбросы по Isolation Forest: {outliers.values}")
Что делать с выбросами?
1. Удаление
# Простое удаление
data_clean = data[(z_scores <= 3)]
# Или удаление по IQR
data_clean = data[(data >= lower_bound) & (data <= upper_bound)]
Минусы:
- Теряешь информацию
- Может смещать выводы
- Опасно, если выбросов много
2. Winsorization (Трансформация)
Замена экстремальных значений на граничные:
from scipy.stats import mstats
# Заменить значения > 95-го процентиля на сам 95-й процентиль
data_winsorized = mstats.winsorize(data, limits=[0.05, 0.05])
print(data_winsorized) # [1, 2, 3, 4, 5, 5] ← 100 замещён на 5
3. Логарифмирование
Для данных с правосторонней асимметрией (много больших выбросов):
data_log = np.log1p(data) # log(1 + x) — безопаснее для нулей
4. Квадратный корень
Легче логарифмирования:
data_sqrt = np.sqrt(data)
5. Трансформация Box-Cox
Автоматический поиск оптимальной трансформации:
from scipy.stats import boxcox
data_transformed, lambda_param = boxcox(data[data > 0])
print(f"Оптимальный λ: {lambda_param:.3f}")
6. Использование робастных методов
Модели, нечувствительные к выбросам:
from sklearn.linear_model import HuberRegressor, RANSACRegressor
from sklearn.ensemble import RandomForestRegressor
# Huber регрессия (робастна к выбросам)
huber = HuberRegressor(epsilon=1.35) # epsilon > близе к OLS
huber.fit(X, y)
# RANSAC (Random Sample Consensus)
ransac = RANSACRegressor()
ransac.fit(X, y)
# Random Forest (по сути робастна к выбросам)
rf = RandomForestRegressor()
rf.fit(X, y)
Когда НЕ удалять выбросы?
1. В естественных науках — выбросы часто реальны
2. При моделировании редких явлений (fraud, click fraud) — выбросы = сигнал
3. При недостаточном объёме данных — теряешь 10% информации
4. Когда выбросы информативны — они содержат важный сигнал
Полный пример обработки
import pandas as pd
import numpy as np
from scipy import stats
# Загружаем данные
df = pd.read_csv('data.csv')
# Визуализация
import matplotlib.pyplot as plt
plt.figure(figsize=(12, 4))
plt.subplot(1, 3, 1)
plt.hist(df['salary'], bins=50)
plt.title('Исходные данные')
# IQR метод
Q1 = df['salary'].quantile(0.25)
Q3 = df['salary'].quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - 1.5 * IQR
upper = Q3 + 1.5 * IQR
outlier_mask = (df['salary'] < lower) | (df['salary'] > upper)
df_clean = df[~outlier_mask]
plt.subplot(1, 3, 2)
plt.hist(df_clean['salary'], bins=50)
plt.title(f'После удаления (удалено {outlier_mask.sum()} строк)')
# Winsorization
df['salary_winsor'] = pd.Series(stats.mstats.winsorize(
df['salary'], limits=[0.05, 0.05]
))
plt.subplot(1, 3, 3)
plt.hist(df['salary_winsor'], bins=50)
plt.title('Winsorization')
plt.tight_layout()
plt.show()
Ключевые выводы
- IQR (межквартильный размах) — самый надёжный и простой метод
- Z-score — если знаешь, что данные нормальные
- Isolation Forest — для многомерных данных
- Удаление — только если уверен, что это ошибка
- Winsorization — сохраняет информацию, но меняет распределение
- Робастные методы — часто лучше удаления