Feature engineering для time series на собеседовании Data Scientist
Содержание:
Почему это спрашивают
Прогноз временного ряда почти всегда упирается не в модель, а в признаки. Даже сильный градиентный бустинг ничего не выжмет из голого столбца дат — весь сигнал приходит из того, как вы превратили историю в фичи: лаги, скользящие окна, сезонность. Поэтому на собесе DS по time series разбор feature engineering — обязательный блок.
Интервьюер смотрит на две вещи: умеете ли вы собрать осмысленный набор признаков под задачу и, главное, не протекает ли у вас будущее в прошлое. Утечка данных (data leakage) — самая частая и самая дорогая ошибка на временных рядах, поэтому её проверяют почти в каждом вопросе.
Lag-признаки
Lag-признак — это значение таргета в прошлом, поданное на вход модели. Самый прямой способ дать модели «память»: что было вчера, неделю и месяц назад.
df['lag_1'] = df['sales'].shift(1) # вчера
df['lag_7'] = df['sales'].shift(7) # неделю назад (тот же день недели)
df['lag_30'] = df['sales'].shift(30) # месяц назадДневные, недельные и месячные лаги — база для любой задачи с суточной сезонностью. Недельный лаг особенно ценен: он ловит эффект дня недели, потому что сравнивает понедельник с понедельником.
Ключевая оговорка. Нельзя заглядывать в будущее: на момент времени t доступны только данные строго до t. shift с положительным аргументом сдвигает ряд назад и это безопасно, а вот shift(-1) возьмёт завтрашнее значение — это прямая утечка. И ещё нюанс, о котором любят спросить: если прогноз строится на 7 дней вперёд, то lag_1 на инференсе недоступен (вчерашних фактов ещё нет), и брать можно только лаги не меньше горизонта прогноза либо считать прогноз рекурсивно.
Скользящие статистики
Скользящее окно агрегирует несколько прошлых точек в один признак — так модель видит тренд и волатильность, а не одно значение.
df['rolling_mean_7'] = df['sales'].rolling(7).mean() # уровень за неделю
df['rolling_std_30'] = df['sales'].rolling(30).std() # волатильность за месяц
df['rolling_max_14'] = df['sales'].rolling(14).max() # пик за две неделиСреднее по окну сглаживает шум и показывает уровень, стандартное отклонение — меру разброса, максимум и минимум — экстремумы за период. Расширяющееся окно (expanding()) агрегирует всё от начала ряда до текущей точки — полезно, когда важна вся накопленная история.
Здесь тоже прячется утечка: pandas по умолчанию считает окно назад, и это правильно, но rolling(7, center=True) центрирует окно и захватывает будущие точки — на трейне так делать нельзя. И помните, что скользящее среднее «знает» текущую точку: если оно потом используется как признак для предсказания этой же точки, добавьте shift(1).
Сезонные признаки
Календарные компоненты — час, день недели, месяц — несут сильный сигнал, но подавать их как обычные числа неправильно: для модели 23 час и 0 час окажутся на разных концах шкалы, хотя это соседние моменты. Спасает циклическое кодирование через синус и косинус:
df['hour_sin'] = np.sin(2 * np.pi * df['hour'] / 24)
df['hour_cos'] = np.cos(2 * np.pi * df['hour'] / 24)
df['day_of_week'] = df['ts'].dt.dayofweek
df['day_of_month'] = df['ts'].dt.day
df['week_of_year'] = df['ts'].dt.isocalendar().weekПара sin/cos замыкает шкалу в круг, и 23:00 оказывается рядом с 00:00. Для древесных моделей (бустинги, случайный лес) циклическое кодирование не так критично — они и так режут по порогам, — а вот линейным моделям и нейросетям оно заметно помогает.
Праздники и события
Праздники и выходные ломают обычную сезонность, поэтому их выделяют в отдельные признаки. Для российского календаря удобна библиотека holidays:
import holidays
ru_holidays = holidays.RU()
df['is_holiday'] = df['date'].apply(lambda d: d in ru_holidays)
df['is_weekend'] = df['day_of_week'].isin([5, 6])Помимо календарных праздников важны бизнес-события — маркетинговые кампании, распродажи, запуски продукта. Их кодируют флагом активности на нужный период:
df['campaign_active'] = df['date'].between('2026-05-01', '2026-05-15')Такие флаги объясняют модели всплески, которые иначе выглядят как необъяснимые выбросы. Полезно добавлять и «окрестности» события: день до и день после праздника часто ведут себя не так, как сам праздник.
Кросс-серийные признаки
Когда рядов много (магазины, товары, регионы), сигнал можно черпать из соседей — особенно если отдельный ряд короткий или разреженный:
- Иерархические. Суммарные продажи по всей категории для товара внутри неё. Агрегат по группе стабильнее, чем шумный отдельный ряд.
- Географические. Среднее по ближайшим магазинам — ловит локальные факторы (погода, события в городе), общие для точек рядом.
- Кластерные. Среднее внутри группы похожих товаров. Помогает новинке, у которой ещё нет своей истории, опереться на поведение похожих SKU.
Кросс-серийные фичи особенно выручают на разреженных данных: там, где своей истории мало, поведение связанных рядов даёт модели опору. Но и здесь агрегаты должны считаться только по прошлому — иначе снова утечка.
Утечка данных — главная ловушка
Утечка (data leakage) на временных рядах — когда в признаки просачивается информация из будущего, которой на момент прогноза не существует. Модель показывает отличные метрики на валидации и разваливается в проде. Типичные источники:
- Центрированные или заглядывающие вперёд окна (
center=True,shift(-1)) — берут будущие точки. - Глобальная нормализация по всему датасету — среднее и стандартное отклонение, посчитанные по всем данным включая тест, подмешивают будущее в трейн. Скейлер надо фитить только на трейне.
- Заполнение пропусков будущим —
bfillтянет назад значения из будущего. На рядах используютffill. - Случайный k-fold — перемешивает время и учит модель на будущем, предсказывая прошлое. Нужен временной сплит:
TimeSeriesSplitили walk-forward валидация, где трейн всегда раньше теста.
Именно об этом чаще всего гоняют на собесе: покажите, что вы сначала думаете «а знал бы я это в момент прогноза?», и только потом добавляете признак.
Связанные темы
- ARIMA на собесе DS
- Holt-Winters для DS
- Forecasting system design для DS
- Feature engineering для DS
- Подготовка к собесу Data Scientist
FAQ
Как выбрать, какие лаги брать?
Отталкивайтесь от сезонности задачи и горизонта прогноза. Для суточных данных базовый набор — lag_1, lag_7, lag_14, lag_30 плюс лаги, кратные явным периодам (365 для годовой сезонности). Помогает автокорреляционная функция (ACF/PACF): пики на ней показывают, на каких лагах сохраняется зависимость. И всегда проверяйте, что выбранные лаги будут доступны на инференсе при вашем горизонте прогноза.
Почему сезонность кодируют через sin/cos, а не просто номером часа?
Календарные величины цикличны: после 23 часа идёт 0, после декабря — январь. Если подать их числом, модель решит, что 23 и 0 максимально далеки, хотя это соседние моменты. Пара sin/cos замыкает шкалу в окружность и сохраняет близость соседних значений. Для линейных моделей и нейросетей это важно; деревья справляются и с сырыми номерами.
Как избежать утечки данных при скользящих окнах?
Три правила: окно смотрит только назад (без center=True), при использовании агрегата как признака для текущей точки добавляйте shift(1), а нормализацию и заполнение пропусков делайте только по прошлому. И валидируйте временным сплитом, а не случайным k-fold, — тогда утечка через окна всплывёт сразу.
В чём разница между скользящим и расширяющимся окном?
Скользящее окно (rolling(n)) агрегирует фиксированное число последних точек и реагирует на недавние изменения — хорошо для локального тренда и волатильности. Расширяющееся (expanding()) берёт всю историю от начала ряда до текущей точки и даёт устойчивую долгосрочную оценку уровня. Часто в модель кладут оба — они ловят сигнал на разных горизонтах.
Как валидировать модель на временных рядах?
Только с сохранением порядка времени: трейн всегда раньше теста. Используют TimeSeriesSplit из sklearn или walk-forward (расширяющееся окно обучения со сдвигом теста вперёд). Обычный перемешивающий k-fold запрещён — он учит модель на будущем и предсказывает прошлое, что даёт оптимистичные метрики и провал в проде.
Это официальная информация?
Нет. Статья основана на общепринятых практиках feature engineering для временных рядов. Конкретные требования зависят от компании, задачи и уровня позиции.
Тренируйте Data Science — откройте тренажёр с 1500+ вопросами для собесов.