Пропуски и нерегулярные ряды
Учимся аккуратно заполнять дыры в ряде и приводить нерегулярные данные к ровной сетке без обмана модели.
Интерполяция — оценка пропущенного значения по известным соседям; для рядов чаще всего линейная (по прямой между ближайшими известными точками).
Зачем заниматься пропусками
Реальные ряды дырявые: датчик молчал, выгрузка сбоила, магазин не работал. Большинство моделей требуют сплошной регулярной сетки, поэтому дыры нужно заполнить — но честно. Неаккуратное заполнение вносит ложную информацию и портит и обучение, и валидацию. Способ заполнения зависит от природы пропуска.
Важно понять, что заполнение пропуска — это всегда выдумывание данных, которых у нас нет. Любой метод подставляет некую гипотезу о том, что происходило в дыре, и эта гипотеза может оказаться ложной. Поэтому первый вопрос не «каким методом заполнить», а «почему вообще возник пропуск». Датчик завис на пару часов и величина менялась плавно — одна ситуация; магазин был закрыт и продаж физически не было — совсем другая; данные просто не доехали из-за сбоя ETL — третья. От ответа зависит, что честно подставить вместо дыры.
Методы заполнения
| Метод | Когда уместен |
| forward fill (ffill) | «уровни»: остаток, статус, цена держится |
| линейная интерполяция | плавно меняющиеся величины (температура) |
| заполнение нулём | счётчики событий: нет данных = не было событий |
| сезонное значение | сильная сезонность: взять тот же день прошлой недели |
Логика таблицы — в природе измеряемой величины. ffill подходит «уровням», которые держатся между измерениями: остаток на складе, текущий статус, цена — они не меняются сами по себе, пока не пришло новое значение. Линейная интерполяция уместна для плавных физических величин вроде температуры, где разумно предположить постепенный переход между известными точками. Ноль ставят счётчикам событий, где отсутствие записи означает отсутствие события. А сезонное значение выручает на рядах с сильным недельным или годовым ритмом: вместо плоской прямой берут аналогичный момент прошлого периода, сохраняя форму волны.
Линейная интерполяция руками
Заполним внутренние пропуски, проведя прямую между ближайшими известными точками.
series = [10, None, None, 16, 18, None, 22]
known = [(j, v) for j, v in enumerate(series) if v is not None]
filled = series[:]
for i in range(len(series)):
if filled[i] is None:
prev = max(j for j, _ in known if j < i)
nxt = min(j for j, _ in known if j > i)
vp, vn = series[prev], series[nxt]
filled[i] = round(vp + (vn - vp) * (i - prev) / (nxt - prev), 1)
print(filled)
Вывод:
[10, 12.0, 14.0, 16, 18, 20.0, 22]
Дыры между 10 и 16 заполнились как 12 и 14 — ровно по прямой. Это разумно для плавной величины, но опасно для скачкообразной: интерполяция «сгладит» реальный провал, которого модель потом не увидит.
Сглаживание реального провала — коварная ловушка. Допустим, в дыре на самом деле случился резкий обвал спроса из-за сбоя оплаты, а потом возврат к норме. Линейная интерполяция нарисует на этом месте гладкий подъём по прямой, и обвал бесследно исчезнет из данных. Модель, обученная на таком «причёсанном» ряде, будет считать, что подобных провалов не бывает, и недооценит риск. Поэтому интерполяция хороша ровно там, где предположение о плавности оправдано; на скачкообразных величинах она искажает саму суть ряда.
Опасность заполнения для валидации
Заполнять пропуск можно только данными из прошлого. Линейная интерполяция использует будущую точку (nxt) — это допустимо при подготовке исторических данных, но недопустимо при онлайн-прогнозе: в реальном времени будущего ещё нет. Тогда берут только ffill или прогноз модели. Смешение этих режимов — частый источник утечки.
Различие двух режимов стоит закрепить. При офлайн-подготовке готового исторического датасета, где все точки уже известны, интерполяция между прошлым и будущим соседом законна — мы реконструируем прошлое, имея на руках весь ряд. Но в продакшене, когда прогноз делается в момент t, точки nxt из будущего ещё не существует, и подставить её неоткуда. Если в обучении вы заполняли дыры интерполяцией с заглядыванием вперёд, а в проде так сделать нельзя, обучение и инференс расходятся: метрики на истории завышены, а в реальной работе модель видит другие, хуже заполненные данные. Правило простое — заполнение в обучении должно повторять ровно то, что физически возможно в продакшене.
Как работает под капотом
Нерегулярный ряд (события в произвольные моменты) сначала ресемплируют к сетке: группируют по бакетам и агрегируют, как в разделе 1. Пустые бакеты — это и есть пропуски, которые затем заполняют выбранным методом. Важно различать «нет данных» (датчик молчал — интерполировать) и «значение равно нулю» (событий не было — ставить 0). Перепутать их — значит исказить ряд: ноль продаж и отсутствие данных о продажах — это совсем не одно и то же.
Различие «нет данных» против «ноль» влияет на бизнес-решения напрямую. Если магазин был закрыт и продаж не было, честное значение — ноль, и модель должна знать, что в этот день спрос отсутствовал. Если же касса просто не выгрузила данные, а продажи шли, то ноль обманет модель, занизив средний спрос и спровоцировав недозаказ товара. Обратная ошибка не лучше: заполнив реальный ноль интерполяцией, мы выдумаем продажи, которых не было, и завысим прогноз. Поэтому при ресемплинге к сетке критично сохранить информацию об источнике каждого пустого бакета — был там настоящий ноль или просто пробел в данных.
Частые ошибки
- Заполнять счётчик событий интерполяцией вместо нуля (выдумываем события, которых не было).
- Использовать линейную интерполяцию (видит будущее) в онлайн-прогнозе — утечка.
- Не отличать «нет данных» от «значение ноль» — искажение природы ряда.
- Сглаживать интерполяцией скачкообразную величину, стирая реальные провалы и всплески.
- Заполнять длинные дыры одним методом, не отметив их как ненадёжные участки для валидации.
Итоги
- Пропуски заполняют по природе величины: ffill для уровней, интерполяция для плавных, ноль для счётчиков.
- Линейная интерполяция использует будущую точку — нельзя в онлайн-прогнозе.
- Нерегулярные ряды сперва ресемплируют к сетке, отличая «нет данных» от «ноль».
- Любое заполнение — это гипотеза о дыре; она должна совпадать с тем, что возможно в продакшене.