|
Очистка данных и работа с выбросами
ОП.09 Обработка и анализ данных
Практическая работа №7
Тема: Очистка данных и работа с выбросами
Цель работы: Научиться выявлять и обрабатывать выбросы (аномалии) в данных, применять методы фильтрации и трансформации.
Время выполнения: 2 академических часа
Инструменты: Python + Jupyter Notebook, библиотеки pandas, numpy, matplotlib, scipy
ЗАДАНИЕ ДЛЯ СТУДЕНТА
Этап 1. Создание данных с выбросами (10 минут)
Создадим датафрейм с зарплатами сотрудников, содержащий выбросы:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# Нормальные данные (50 сотрудников)
np.random.seed(42)
normal_salaries = np.random.normal(50000, 10000, 50) # средняя 50000, std 10000
# Добавляем выбросы (аномалии)
outliers = np.array([150000, 200000, 3000, 250000, 5000])
# Объединяем
salaries = np.concatenate([normal_salaries, outliers])
employee_ids = range(1, len(salaries) + 1)
df = pd.DataFrame({
'employee_id': employee_ids,
'salary': salaries,
'experience': np.random.randint(0, 30, len(salaries))
})
print("Первые 10 строк:")
print(df.head(10))
print(f"Статистика зарплат:")
print(df['salary'].describe())
Вопрос: Посмотрите на статистику: в чём разница между median и mean? Почему?
Этап 2. Визуальное обнаружение выбросов (15 минут)
Постройте графики для выявления аномалий:
# Boxplot (ящик с усами)
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.boxplot(df['salary'])
plt.title('Boxplot зарплат')
plt.ylabel('Зарплата')
# Гистограмма
plt.subplot(1, 2, 2)
plt.hist(df['salary'], bins=20, edgecolor='black')
plt.title('Гистограмма зарплат')
plt.xlabel('Зарплата')
plt.ylabel('Частота')
plt.tight_layout()
plt.show()
# Scatter plot (зарплата vs опыт)
plt.figure(figsize=(8, 6))
plt.scatter(df['experience'], df['salary'], alpha=0.7)
plt.title('Зависимость зарплаты от опыта работы')
plt.xlabel('Опыт (лет)')
plt.ylabel('Зарплата')
plt.show()
Вопрос: Какие точки на графике выглядят как выбросы? Сколько их?
Этап 3. Метод IQR (межквартильный размах) (20 минут)
Используйте IQR для выявления выбросов:
# Расчёт квартилей
Q1 = df['salary'].quantile(0.25)
Q3 = df['salary'].quantile(0.75)
IQR = Q3 - Q1
# Границы для выбросов
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
print(f"Q1 (25-й перцентиль): {Q1:.2f}")
print(f"Q3 (75-й перцентиль): {Q3:.2f}")
print(f"IQR: {IQR:.2f}")
print(f"Нижняя граница: {lower_bound:.2f}")
print(f"Верхняя граница: {upper_bound:.2f}")
# Выявление выбросов
outliers_iqr = df[(df['salary'] < lower_bound) | (df['salary'] > upper_bound)]
print(f"Найдено выбросов: {len(outliers_iqr)}")
print("Выбросы:")
print(outliers_iqr)
# Удаление выбросов
df_cleaned = df[(df['salary'] >= lower_bound) & (df['salary'] <= upper_bound)]
print(f"Размер после удаления: {len(df_cleaned)} (было {len(df)})")
Вопрос: Почему выбирают множитель 1.5? Что означают границы IQR?
Этап 4. Метод Z-оценки (15 минут)
Используйте Z-score для выявления выбросов:
from scipy import stats
# Расчёт Z-оценок
z_scores = np.abs(stats.zscore(df['salary']))
df['z_score'] = z_scores
# Выбросы с Z-score > 3
threshold = 3
outliers_z = df[df['z_score'] > threshold]
print(f"Z-score > {threshold}: {len(outliers_z)} выбросов")
print(outliers_z)
# Удаление по Z-score
df_cleaned_z = df[df['z_score'] <= threshold]
print(f"Размер после удаления: {len(df_cleaned_z)} (было {len(df)})")
Вопрос: Что означает Z-score? Какое пороговое значение обычно выбирают и почему?
Этап 5. Методы замены выбросов (15 минут)
Вместо удаления выбросы можно заменить:
# Создаём копию исходных данных
df_capped = df.copy()
# 1. Замена на границы (Capping)
df_capped['salary_capped'] = df_capped['salary'].clip(lower=lower_bound, upper=upper_bound)
# 2. Замена на медиану
median_salary = df['salary'].median()
df_capped['salary_median'] = df_capped['salary'].copy()
outliers_mask = (df_capped['salary'] < lower_bound) | (df_capped['salary'] > upper_bound)
df_capped.loc[outliers_mask, 'salary_median'] = median_salary
# Сравнение методов
print("Сравнение методов обработки выбросов:")
print("Исходная средняя:", df['salary'].mean())
print("После удаления IQR:", df_cleaned['salary'].mean())
print("После удаления Z-score:", df_cleaned_z['salary'].mean())
print("После Capping:", df_capped['salary_capped'].mean())
print("После замены на медиану:", df_capped['salary_median'].mean())
# Визуализация результатов
plt.figure(figsize=(12, 6))
plt.hist(df['salary'], bins=20, alpha=0.5, label='Исходные', edgecolor='black')
plt.hist(df_capped['salary_capped'], bins=20, alpha=0.5, label='Capping', edgecolor='black')
plt.hist(df_capped['salary_median'], bins=20, alpha=0.5, label='Замена на медиану', edgecolor='black')
plt.title('Сравнение методов обработки выбросов')
plt.xlabel('Зарплата')
plt.ylabel('Частота')
plt.legend()
plt.show()
Вопрос: Какой метод лучше всего сохранил распределение данных? Когда уместно удаление, а когда замена?
Этап 6. Практическое задание: анализ датасета о ценах на жильё (25 минут)
Проанализируем выбросы в датасете о недвижимости:
# Создаём данные о ценах на жильё
np.random.seed(42)
normal_prices = np.random.normal(5000000, 1000000, 100) # 5 млн ± 1 млн
# Добавляем аномальные цены
outlier_prices = np.array([500000, 20000000, 35000000, 100000, 45000000])
prices = np.concatenate([normal_prices, outlier_prices])
housing = pd.DataFrame({
'house_id': range(1, len(prices) + 1),
'price': prices,
'area': np.random.normal(80, 20, len(prices)) # площадь в м2
})
housing['price_per_m2'] = housing['price'] / housing['area']
Выполните анализ:
# 1. Статистика цен
print("Статистика цен на жильё:")
print(housing['price'].describe())
# 2. Поиск выбросов по цене (IQR)
Q1_price = housing['price'].quantile(0.25)
Q3_price = housing['price'].quantile(0.75)
IQR_price = Q3_price - Q1_price
lower_price = Q1_price - 1.5 * IQR_price
upper_price = Q3_price + 1.5 * IQR_price
price_outliers = housing[(housing['price'] < lower_price) | (housing['price'] > upper_price)]
print(f"Ценовые выбросы: {len(price_outliers)}")
# 3. Поиск выбросов по цене за м2 (Z-score)
z_prices = np.abs(stats.zscore(housing['price_per_m2']))
housing['z_price_m2'] = z_prices
m2_outliers = housing[housing['z_price_m2'] > 3]
print(f"Выбросы по цене за м2: {len(m2_outliers)}")
# 4. Визуализация
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
plt.boxplot(housing['price'])
plt.title('Boxplot цен на жильё')
plt.ylabel('Цена (руб)')
plt.subplot(1, 2, 2)
plt.scatter(housing['area'], housing['price'], alpha=0.7)
plt.title('Зависимость цены от площади')
plt.xlabel('Площадь (м2)')
plt.ylabel('Цена (руб)')
plt.tight_layout()
plt.show()
Вопрос: Почему цена за квадратный метр может быть лучшим показателем для выявления аномалий, чем просто цена?
Этап 7. Выводы (10 минут)
Напишите развёрнутый вывод по работе:
1. Какие методы обнаружения выбросов вы использовали? Каковы их преимущества и недостатки?
2. В каких случаях выбросы нужно удалять, а в каких — оставлять или заменять?
3. Как выбросы влияют на среднее значение и стандартное отклонение?
КРИТЕРИИ ОЦЕНКИ
| Критерий | Макс. балл |
|---|
| Все этапы выполнены (1-6) | 3 | | Корректное использование IQR и Z-score | 3 | | Правильная обработка выбросов (удаление/замена) | 2 | | Выводы осмысленные, примеры из работы | 2 | | Итого | 10 |
ОТВЕТ ДЛЯ ПРЕПОДАВАТЕЛЯ (эталонный результат)
Нажмите, чтобы увидеть ответы
Этап 1. Статистика:
• Mean (среднее) чувствительно к выбросам, median (медиана) — устойчива.
• В данных с выбросами среднее будет выше/ниже медианы.
Этап 2. Графики:
• Boxplot показывает выбросы как точки за «усами».
• На scatter plot выбросы видны как точки, далёкие от основного облака.
Этап 3. Метод IQR:
• Множитель 1.5 — стандартное правило (≈99.3% данных в нормальном распределении).
• Границы: Q1 - 1.5*IQR и Q3 + 1.5*IQR.
Этап 4. Z-score:
• Z-score показывает, на сколько стандартных отклонений точка отклоняется от среднего.
• Порог 3 (99.7% данных) или 2 (95% данных).
Этап 5. Замена выбросов:
• Capping сохраняет количество данных, но искажает распределение на краях.
• Замена на медиану сильнее сжимает распределение.
• Удаление уместно, когда выбросы — ошибки ввода.
• Замена уместна, когда выбросы редки, но удалять их нельзя.
Этап 6. Недвижимость:
• Цена за м2 лучше, так как нормализует по площади.
• Большая квартира может быть дорогой, но с адекватной ценой за м2.
Этап 7. Выводы (пример):
1. IQR — устойчив к выбросам, Z-score — требует нормального распределения.
2. Удалять — при ошибках данных. Оставлять — если выбросы реальны (например, элитное жильё).
3. Выбросы сильно искажают среднее и std, но не влияют на медиану и IQR.
Автор: УМК СПО
Лицензия: Бесплатное использование с указанием источника
|
|
|