Как выбрать способ заполнения пропусков в данных?

«Как выбрать способ заполнения пропусков в данных?» — вопрос из категории Предобработка данных, который задают на 26% собеседований Data Scientist / ML Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Стратегия импутации зависит от природы пропусков (MCAR, MAR, MNAR), типа признака и модели, которую я планирую использовать. Вот мой алгоритм действий:

1. Анализ пропусков: Первым делом я смотрю на процент и паттерн пропусков.

import pandas as pd
import missingno as msno

# Процент пропусков по колонкам
missing_percent = df.isnull().mean() * 100
print(missing_percent[missing_percent > 0].sort_values(ascending=False))

# Матрица пропусков для выявления паттернов
msno.matrix(df)
plt.show()

2. Выбор метода в зависимости от сценария:

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

    df['category_col'].fillna('MISSING', inplace=True)
  • Числовые признаки с нормальным распределением: Использую медиану, так как она устойчива к выбросам.

    median_value = df['numeric_col'].median()
    df['numeric_col'].fillna(median_value, inplace=True)
  • Временные ряды или упорядоченные данные: Применяю интерполяцию или forward/backward fill.

    df['timeseries_col'].interpolate(method='linear', inplace=True)
  • Сложные случаи с неслучайными пропусками: Использую модели предсказания, например, IterativeImputer (ранее MICE) из sklearn.

    from sklearn.experimental import enable_iterative_imputer
    from sklearn.impute import IterativeImputer
    from sklearn.ensemble import RandomForestRegressor
    
    # Итеративная импутация на основе случайного леса
    imputer = IterativeImputer(estimator=RandomForestRegressor(),
                               max_iter=10, random_state=42)
    df_imputed = pd.DataFrame(imputer.fit_transform(df), columns=df.columns)

3. Важное правило: Я всегда создаю бинарный признак-маску [feature]_is_missing, который указывает на то, что значение было пропущено. Это часто улучшает качество модели, так как сам факт пропуска может быть информативным.

df['age_is_missing'] = df['age'].isnull().astype(int)
# Затем заполняю сам age медианой
df['age'].fillna(df['age'].median(), inplace=True)

Итог: Не существует одного лучшего метода. Я тестирую несколько стратегий импутации и оцениваю их влияние на производительность модели через кросс-валидацию.