← Назад к вопросам

Как обнаружить и обработать выбросы (outliers) в данных?

2.0 Middle🔥 191 комментариев
#Pandas и обработка данных#Машинное обучение

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Обнаружение и обработка выбросов (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 — сохраняет информацию, но меняет распределение
  • Робастные методы — часто лучше удаления
Как обнаружить и обработать выбросы (outliers) в данных? | PrepBro