В Data Science важно понимать не только как заполнить пропуск, но и почему он появился. Пустое значение не всегда означает потерю информации. Иногда сам факт пропуска уже несет сигнал. Если человек не указал доход, это может быть важной характеристикой поведения. Если датчик не прислал значение, возможно, у нас не случайная потеря, а системная проблема измерения.
Эту тему полезно сразу видеть как часть более широкой работы с качеством входа: пока команда не понимает, насколько вообще данным можно доверять, разговор об импутации быстро превращается в механическую подстановку чисел вместо реальной диагностики проблемы.
Почему missing values нельзя считать просто пустыми клетками
Интуитивно кажется, что пропуск — это отсутствие числа. На практике пропуск меняет распределение признака и структуру данных. Если мы удалим все строки с пустыми значениями, можем потерять не шум, а целый сегмент объектов. Если бездумно подставим среднее, можем сгладить важные различия между группами.
Для модели пропуск опасен сразу в двух смыслах. Во-первых, многие алгоритмы не умеют работать с NaN напрямую. Во-вторых, даже если умеют, способ появления пропуска может влиять на предсказание. Поэтому missing values — это не чисто технический дефект, а часть статистической структуры выборки.
Какие бывают механизмы пропусков
Самое полезное различие — не между нулями и пустыми ячейками, а между типами причин, по которым данные исчезли.
MCAR — missing completely at random. Пропуск никак не связан ни с самим признаком, ни с другими переменными. Например, файл частично повредился случайным образом.
MAR — missing at random. Пропуск зависит от других признаков. Например, доход чаще не указывают студенты младших возрастных групп.
MNAR — missing not at random. Пропуск зависит от самого скрытого значения. Например, люди с очень высоким доходом чаще не хотят его раскрывать.
Это различие принципиально. Если пропуски случайны, простая импутация может работать приемлемо. Если нет, пустота сама становится признаком, и ее уже нельзя считать нейтральной. Именно поэтому полезно отдельно смотреть и на missing data patterns: они помогают увидеть, где отсутствие значения уже само по себе описывает поведение процесса.
Как пропуски искажают статистику
Когда в признаке есть missing values, любая сводная статистика зависит от того, какие объекты сохранились, а какие исчезли. Если пропуски неслучайны, то даже среднее по наблюдаемым значениям уже может не описывать всю генеральную совокупность.
Раздел математики: математическая статистика.
(\bar{x} = \frac{1}{n_{obs}}\sum_{i=1}^{n_{obs}} x_i)
Что означает каждый символ:
(\bar{x}) — среднее по наблюдаемым значениям признака;
(n_{obs}) — число объектов, у которых признак не пропущен;
(x_i) — наблюдаемое значение признака у объекта с номером (i);
(\sum) — суммирование всех доступных значений.
Роль формулы: среднее по наблюдаемым значениям часто используется для импутации. Но если набор наблюдаемых объектов уже смещен, такая оценка переносит смещение в данные дальше по pipeline.
Численный пример: пусть истинные доходы пяти клиентов равны (20, 25, 30, 35, 120). Истинное среднее равно (\frac{20 + 25 + 30 + 35 + 120}{5} = 46). Если значение (120) пропущено, наблюдаемое среднее станет (\frac{20 + 25 + 30 + 35}{4} = 27.5). Мы уже потеряли реальный центр распределения.
Именно поэтому пропуски ломают анализ еще до модели. Даже простая статистика начинает описывать не весь набор данных, а только сохранившуюся часть.
Когда строки можно удалять, а когда нельзя
Удаление строк или столбцов — самый простой путь, но он хорош только в ограниченных случаях. Если пропусков мало и они близки к случайным, удаление может почти не навредить. Но когда пропуски систематические, удаление меняет состав выборки.
Если признак пропущен у 60–80% объектов и при этом его бизнес-смысл слабый, колонку действительно бывает разумно убрать. Но если это важный признак, проще потерять немного точности на импутации, чем выбросить сильный источник сигнала.
С удалением строк еще опаснее. В маленькой выборке каждая строка ценна. Если после фильтрации мы теряем редкие, но важные кейсы, модель начинает видеть мир слишком упрощенно.
Самая популярная стратегия: импутация средним и медианой
Средняя импутация удобна, потому что проста. Мы берем все наблюдаемые значения признака, считаем среднее и подставляем его в пустые ячейки. Но это сжимает дисперсию: одинаковое число вставляется много раз, и признак становится менее естественным.
Медианная импутация часто устойчивее, особенно если в признаке есть выбросы. Медиана меньше реагирует на экстремальные значения и лучше сохраняет типичный уровень признака.
Раздел математики: описательная статистика.
(x_{miss} \leftarrow \operatorname{median}(x_{obs}))
Что означает каждый символ:
(x_{miss}) — пропущенное значение, которое мы хотим заполнить;
(x_{obs}) — набор наблюдаемых значений признака;
(\operatorname{median}(\cdot)) — медиана по наблюдаемым значениям.
Где применяется: в табличных данных как базовый и устойчивый способ заполнения числовых признаков. Особенно полезен, когда признак асимметричен или содержит выбросы.
Численный пример: пусть наблюдаемые значения признака равны (10, 12, 14, 100). Среднее равно (34), а медиана равна (13). Если подставить среднее, мы вставим в типичный объект слишком крупное значение, потому что на среднее повлиял выброс (100).
Почему one-hot для пропуска иногда полезнее самой импутации
Если признак пропущен неслучайно, то полезно не только заполнить его, но и добавить бинарный индикатор: был пропуск или нет. Тогда модель сможет использовать сам факт отсутствия информации как отдельный сигнал.
Это особенно полезно в кредитном скоринге, медицинских данных и CRM-таблицах. Там пропуск часто связан с поведением пользователя, этапом воронки или особенностью процесса сбора данных. В таких случаях missing value уже становится частью feature engineering, а не просто технической грязью, которую надо скрыть от модели.
Как missing values связаны с оптимизацией и качеством модели
Пропуски влияют на модель не только как проблема ввода, но и как искажение признакового пространства. Если импутация сделана грубо, многие объекты начинают искусственно слипаться. Например, десятки клиентов получают одинаковый возраст или одинаковый доход после заполнения средним. Это меняет расстояния, плотности и форму распределений.
Для методов на расстояниях, таких как k-NN или k-means, это особенно заметно. Для линейных моделей грубая импутация способна менять коэффициенты, потому что признак теряет естественную вариативность. Для деревьев ситуация мягче, но и там пропуски влияют на то, где будут стоять разбиения, если данные предварительно заполнялись неаккуратно.
Самая частая ошибка: leakage при импутации
Это одна из самых опасных ошибок в preprocessing. Люди берут всю таблицу, считают среднее или медиану по всему датасету, заполняют пропуски, а только потом делят данные на train и test. В этот момент test уже незаметно подмешался в preprocessing.
Правильный порядок всегда такой: сначала split, потом fit импутера только на train, потом transform на train, validation и test. Иначе оценка качества становится слишком оптимистичной. На практике это удобно связывать и с тем, как проверяются данные до обучения модели, чтобы контроль схемы, диапазонов и пропусков не жил отдельно от самого pipeline.
Раздел математики: теория вероятностей и машинное обучение.
(\tilde{x}_{test} = f(x_{train}))
Что означает каждый символ:
(\tilde{x}_{test}) — преобразованный test-признак после импутации;
(f) — правило заполнения, которое мы выучили на train;
(x_{train}) — обучающая часть данных.
Роль формулы: она подчеркивает главный принцип: параметры импутации должны вычисляться только на train. Test не должен участвовать в построении функции (f).
Python: как работать с missing values аккуратно
На практике удобнее всего использовать SimpleImputer и встраивать его в pipeline. Тогда легко избежать утечки и повторить те же шаги на новых данных.
import pandas as pd
# Импортируем pandas для работы с таблицей.
from sklearn.model_selection import train_test_split
# Импортируем функцию разбиения на train и test.
from sklearn.impute import SimpleImputer
# Импортируем простой импутер для заполнения пропусков.
data = pd.DataFrame({
"age": [21, 25, None, 32, 40, None],
"income": [50, None, 70, 90, None, 120]
})
# Создаем таблицу, где в двух признаках есть пропуски.
train_df, test_df = train_test_split(data, test_size=0.33, random_state=42)
# Сначала делим данные, чтобы не считать статистики по всему датасету.
imputer = SimpleImputer(strategy="median")
# Создаем импутер с медианной стратегией заполнения.
train_imputed = imputer.fit_transform(train_df)
# Считаем медианы только на train и сразу заполняем train.
test_imputed = imputer.transform(test_df)
# Применяем те же медианы к test без повторного обучения.
train_imputed = pd.DataFrame(train_imputed, columns=data.columns)
# Возвращаем train в формат DataFrame с исходными названиями колонок.
test_imputed = pd.DataFrame(test_imputed, columns=data.columns)
# Возвращаем test в формат DataFrame для удобного просмотра.
print("Train after imputation:")
# Печатаем заголовок для train после заполнения.
print(train_imputed)
# Показываем train-таблицу после импутации.
print("Test after imputation:")
# Печатаем заголовок для test после заполнения.
print(test_imputed)
# Показываем test-таблицу после импутации.Для категориальных признаков обычно используют самую частую категорию, отдельный токен вроде Unknown или модельные импутеры. Выбор зависит от природы признака и от того, несет ли пропуск смысл.
Когда достаточно простого решения, а когда нужен более сложный подход
Если пропусков мало, а данные табличные и стабильные, базовая импутация медианой или модой часто дает хороший результат. Не нужно усложнять pipeline ради самой сложности.
Но если пропусков много, если они систематические или если признак критичен для качества, лучше смотреть глубже. Здесь уже полезны KNN-imputation, model-based imputation, MICE и отдельные индикаторы пропусков. Важна не сама модность метода, а его согласованность с природой данных.
Что важно запомнить
Missing values — это не просто отсутствие числа. Это часть структуры данных. Иногда пропуск означает потерю сигнала, а иногда сам является сигналом.
Хорошая работа с пропусками строится вокруг трех вопросов: почему данные пропали, как это влияет на статистику и где в pipeline нужно делать импутацию, чтобы не сломать валидацию. Если держать в голове именно эти три вопроса, решение обычно становится очевиднее, чем кажется в начале.
Kaggle notebook по теме:
https://www.kaggle.com/code/alexisbcook/missing-values/tutorial