Условие
В датасете users(age, income, city, last_login) пропуски в каждой колонке. Опиши стратегии и где какая уместна.
Решение
Сначала разобраться, почему пропуск
- MCAR (missing completely at random) — случайно: можно удалять или импьютировать чем угодно.
- MAR (missing at random) — зависит от других колонок: модельная импьютация.
- MNAR (not at random) — пропуск сам по себе сигнал: добавить флаг
is_missing.
Стратегии
| Колонка | Стратегия | Почему |
|---|---|---|
age (числ.) |
медиана по сегменту, + флаг | устойчиво к выбросам |
income (числ., скошенное) |
медиана + флаг is_missing | MNAR: «не сказали» = сигнал |
city (категория) |
«Unknown» категория | не теряем строки |
last_login (дата) |
оставить NaN или max(date) | NaN = «не логинился» — это важная инфа |
Реализация
import pandas as pd
import numpy as np
# Флаг пропуска как фича
for c in ['age', 'income']:
df[f'{c}_is_missing'] = df[c].isna().astype(int)
# Медиана по сегменту
df['age'] = df.groupby('city')['age'].transform(lambda s: s.fillna(s.median()))
df['income'] = df.groupby('city')['income'].transform(lambda s: s.fillna(s.median()))
# Категория
df['city'] = df['city'].fillna('Unknown')
# Без импьютации — оставить NaN как сигнал
# df['last_login'] остаётся NaNКогда удалять строки
- Пропусков в колонке >50% и нет хороших прокси → колонку дропнуть.
- Пропусков в target → строки выкинуть (для ML).
- Иначе — не удалять, импьютировать + флаг.
Подводные камни
fillna(mean)ломает дисперсию и корреляции. Медиана безопаснее. Идеально — модельная (KNNImputer/IterativeImputer).- Импьютация до train/test split = leakage. Считать статистики только на train, применять к test.
dropna()безsubset=дропнет строки, где есть NaN в любой колонке — обычно слишком много.- NaN в индексе/ключе merge: матчей не будет, но строки не пропадут. Контролировать
indicator=True. - Pandas-NaN ≠ numpy-NaN ≠ NaT (для дат).
isna()ловит все три, но арифметика — нет.
Эталонный ответ
Сначала понять MCAR/MAR/MNAR, потом: числовые — медиана по сегменту + флаг, категориальные — «Unknown», даты — часто оставить NaN. Импьютацию считать только на train.