Как обрабатывать пропущенные значения в данных?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Обработка пропущенных значений (Missing Data)
Пропущенное значение — это отсутствие данных в какой-то ячейке таблицы. В Python/Pandas обозначается как NaN, None или NA. Это одна из самых частых задач при подготовке данных.
Виды пропусков (MCAR, MAR, MNAR)
1. MCAR (Missing Completely At Random)
Определение: вероятность пропуска не зависит ни от наблюдаемых, ни от ненаблюдаемых переменных. Полностью случайно.
Пример: из-за сбоя датчика потеряны некоторые измерения
Как обработать: любой метод работает хорошо (удаление, импутация, модели).
2. MAR (Missing At Random)
Определение: вероятность пропуска зависит от наблюдаемых переменных, но не от самого пропущенного значения.
Пример: люди с высокой зарплатой менее охотно её указывают.
Пропуск зарплаты зависит от: был ли вопрос, откуда человек узнал об опросе,
но не от самой зарплаты напрямую.
Как обработать: метод на основе всех доступных переменных (например, multiple imputation).
3. MNAR (Missing Not At Random)
Определение: вероятность пропуска зависит от самого пропущенного значения. Систематическое смещение.
Пример: люди с низким доходом не указывают зарплату
(пропуск зависит от величины дохода)
Как обработать: сложно, нужны модели с учётом механизма пропусков.
Первая диагностика пропусков
import pandas as pd
import numpy as np
df = pd.read_csv('data.csv')
# Подсчёт пропусков
print(df.isnull().sum())
# Процент пропусков по столбцам
print((df.isnull().sum() / len(df) * 100).round(2))
# Визуализация пропусков
import matplotlib.pyplot as plt
df.isnull().sum().plot(kind='barh')
plt.xlabel('Количество пропусков')
plt.show()
# Матрица пропусков (для понимания корреляции)
import seaborn as sns
sns.heatmap(df.isnull(), cbar=True, yticklabels=False)
plt.show()
Методы обработки пропусков
1. Удаление (Deletion)
Полное удаление строк с пропусками:
# Удалить строки с любыми пропусками
df_clean = df.dropna()
# Удалить строки с пропусками в конкретном столбце
df_clean = df.dropna(subset=['age', 'salary'])
# Удалить столбцы, в которых > 50% пропусков
df_clean = df.dropna(thresh=len(df) * 0.5, axis=1)
Когда использовать:
- Пропусков мало (< 5%)
- Тип: MCAR
- Размер выборки позволяет потерю строк
Минусы:
- Потеря информации
- Смещение, если пропуски не MCAR
2. Заполнение константой (Forward/Backward Fill)
# Заполнить последним известным значением (временные ряды)
df['price'] = df['price'].fillna(method='ffill') # forward fill
df['price'] = df['price'].fillna(method='bfill') # backward fill
# Заполнить средним
df['age'] = df['age'].fillna(df['age'].mean())
# Заполнить медианой (робастнее при выбросах)
df['salary'] = df['salary'].fillna(df['salary'].median())
# Заполнить модой (для категориальных)
df['city'] = df['city'].fillna(df['city'].mode()[0])
# Заполнить нулём (для признаков, где 0 имеет смысл)
df['purchases'] = df['purchases'].fillna(0)
Минусы:
- Ненеестественно — искусственно снижается дисперсия
- Модели могут переучиться на эту закономерность
3. Интерполяция (Interpolation)
Для временных рядов — предположить промежуточное значение:
# Линейная интерполяция
df['temperature'] = df['temperature'].interpolate(method='linear')
# Полиномиальная
df['temperature'] = df['temperature'].interpolate(method='polynomial', order=2)
# На основе индекса (для временных рядов)
df['value'] = df['value'].interpolate(method='index')
4. Простая импутация (Simple Imputation)
K-NN Imputer: заполнить значением K ближайших соседей
from sklearn.impute import KNNImputer
imputer = KNNImputer(n_neighbors=5)
df_imputed = pd.DataFrame(
imputer.fit_transform(df),
columns=df.columns
)
Iterative Imputer (MICE): несколько итераций с разными признаками
from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer
imputer = IterativeImputer(random_state=42, max_iter=10)
df_imputed = pd.DataFrame(
imputer.fit_transform(df),
columns=df.columns
)
5. Multiple Imputation (множественная импутация)
Заполнить несколько вариантов и усреднить результаты (для неопределённости):
from sklearn.impute import SimpleImputer
import numpy as np
n_imputations = 5
df_imputed_list = []
for i in range(n_imputations):
imputer = SimpleImputer(strategy='mean')
df_temp = pd.DataFrame(
imputer.fit_transform(df),
columns=df.columns
)
df_imputed_list.append(df_temp)
# Усреднить предсказания всех вариантов
df_final = pd.concat(df_imputed_list).groupby(level=0).mean()
6. Моделирование (Prediction-based Imputation)
Предсказать пропущенные значения через модель:
from sklearn.linear_model import LinearRegression
# Разделить на данные с известным и неизвестным возрастом
df_known = df[df['age'].notna()]
df_unknown = df[df['age'].isna()]
# Обучить модель
model = LinearRegression()
model.fit(df_known[['income', 'education']], df_known['age'])
# Предсказать
df.loc[df['age'].isna(), 'age'] = model.predict(
df_unknown[['income', 'education']]
)
Специальные техники
Создание флага пропуска
# Добавить столбец-индикатор (может быть информативно для модели)
df['age_missing'] = df['age'].isnull().astype(int)
Group-based Imputation
# Заполнить средним по группе (например, по возрастной группе)
df['salary'] = df.groupby('age_group')['salary'].transform(
lambda x: x.fillna(x.mean())
)
Выбор метода: рекомендации
| Ситуация | Метод | Примечание |
|---|---|---|
| < 5% пропусков, MCAR | Удаление | Простой и эффективный |
| Временной ряд | Интерполяция | Естественно для TS |
| Категориальные | Мода или отдельная категория | 'Missing' как отдельный класс |
| Структурные пропуски | Нулевое заполнение | age_child = 0 (не применимо) |
| Сложные зависимости | KNN или IterativeImputer | Учитывает корреляции |
| Production модель | Multiple Imputation | Честнее про неопределённость |
Антипаттерны
# ❌ ПЛОХО: заполнить всё средним
df.fillna(df.mean())
# ❌ ПЛОХО: удалить все строки с пропусками без анализа
df.dropna()
# ❌ ПЛОХО: проигнорировать пропуски
# ✅ ХОРОШО: явно разобраться с механизмом пропусков
Ключевые выводы
- Сначала анализируй: определи тип пропусков (MCAR/MAR/MNAR)
- Для малых пропусков: удаление часто работает
- Для зависимостей: KNN или IterativeImputer
- Для временных рядов: интерполяция
- Для категорий: мода или отдельная категория
- На production: multiple imputation честнее про неопределённость