- BrainTools - https://www.braintools.ru -

От нестационарности к прогнозу: пайплайн анализа и моделирования временных рядов

Я Михаил Зуев — Data Scientist из команды расходов корпоративного и инвестиционного бизнеса Сбера. Мы много предсказываем, классифицируем и прогнозируем. Впервые столкнувшись с последним и проведя исследование по этой теме, я столкнулся с большим количеством неструктурированной информации. Эта статья — одновременно описание моего пути и небольшое упорядоченное наставление по анализу и прогнозированию временных рядов, которое я сам хотел бы получить.

Начнём с теории.

Временной ряд (он же time series) — это последовательность упорядоченных во времени числовых показателей, характеризующих уровень состояния и изменения изучаемого явления

[Афанасьев, В.Н. Анализ временных рядов и прогнозирование: учебник / В.Н. Афанасьев, М.М. Юзбашев — М.: Финансы и статистика, 2001. — 228 с]

Графическое представление такого ряда:

От нестационарности к прогнозу: пайплайн анализа и моделирования временных рядов - 1

Это обыкновенный двумерный график, у которого по оси времени (X) через равные промежутки фиксируется некоторое измеряемое значение (Y): затраты на финансирование, объём сельскохозяйственной продукции, производство электроэнергии — в общем, буквально всё, что можно измерить по прошествии некоторого времени. Для построения ряда потребуется всего два показателя: измеряемая (а в недалёком будущем — прогнозируемая) величина и момент времени, в который происходило измерение. Применительно к Python, просто конкатенируем их в один Pandas Data Frame и строим график в Matplotlib (или в другой библиотеке для построения графиков) в среде jupyter‑notebook:

import pandas as pd
import matplotlib.pyplot as plt

plt.figure(figsize=(12, 6))
plt.plot(y['Месяц'], y['Сумма'], color='orangered')
plt.title(f'Исходный ряд')
От нестационарности к прогнозу: пайплайн анализа и моделирования временных рядов - 2

Одна из важных характеристик ряда — его тренд. Это такая зависимость, которая позволяет посмотреть на имеющийся ряд с прогнозной точки зрения [1]: есть ли тенденция увеличения некоторого показателя со временем? Если да, то какая? Знание об этом факте позволяет делать первые предположения о прогнозе.

Ещё выделяют сезонность — циклическое изменение уровня ряда с постоянным периодом (повторением [2] в определённые моменты времени). Применительно к графику выше можно сказать, что некоторые суммы люди с большой неохотой тратят по январям (наступают новогодние праздники), причём этот спад повторяется три года подряд. Это и есть сезонность.

Нельзя не учесть и влияние случайных колебаний — непрогнозируемого случайного изменения ряда, не имеющего тенденции к постоянству, которое всё‑таки влияет на то, каким получается наш ряд.

Чтобы не на словах, а статистически увидеть тренд, сезонность и случайные колебания, применим функцию seasonal_decompose() из библиотеки Statsmodels для Python:

from statsmodels.tsa.seasonal import seasonal_decompose

fig, axes = plt.subplots(4, 1, figsize=(12, 8))
y = y.set_index('Месяц')
decompose = seasonal_decompose(y, period=12)
y.plot(ax=axes[0], title='Исходные данные')
decompose.trend.plot(ax=axes[1], title='Тренд')
decompose.seasonal.plot(ax=axes[2], title='Сезонность')
decompose.resid.plot(ax=axes[3], title='Шум')
От нестационарности к прогнозу: пайплайн анализа и моделирования временных рядов - 3

Судя по графику, наблюдается явный тренд на увеличение, затухающий примерно в мае 2024, сезонность с ярко выраженными спадами в январе, зашумленность с декабрьскими пиком 2023 года (максимум) и 2024 года (минимум).

В случае единственного графика такой простой визуальный анализ может сработать, но графиков может быть много, а потребность [3] в их анализе — быстрой. В этом случае применяют статистическую оценку, то есть такие тесты, которые позволят однозначно выявить факт стационарности или нестационарности. Нестационарность — это свойство временного ряда, которое выражается в изменении с течением времени статистических характеристик, таких как математическое ожидание и дисперсия. Примеры подобных рядов:

От нестационарности к прогнозу: пайплайн анализа и моделирования временных рядов - 4

Нестационарные временные ряды трудно поддаются моделированию и прогнозированию, поскольку результаты, полученные с их использованием, часто оказываются ложными: анализ выявляет несуществующие зависимости и закономерности. Для получения корректных и надёжных результатов анализа нестационарные временные ряды необходимо преобразовать в стационарные.

Чтобы явно сказать, к какому типу рядов относится наш ряд, мы применяем статистические тесты на определение стационарности или нестационарности. Я пользовался расширенным тестом Дики‑Фуллера (ADF) (самым распространённым, но не единственным методом оценки). В очень широком смысле ADF‑тест определяет, является ли временной ряд стационарным, проверяя нулевую гипотезу о том, что в ряду присутствует единичный корень, что указывает на нестационарность. Если на выходе p‑value > 0.05, то гипотеза принимается как верная — ряд нестационарен, и наоборот при p‑value <= 0.05. Это даёт нам чёткое представление о том, стоит ли дифференцировать ряд для приведения к стационарности.

from statsmodels.tsa.stattools import adfuller
adf_result = adfuller(y)
print(f'p-value по Дики-Фулеру: {adf_result[1]:.4f}')

# p-value по Дики-Фулеру: 0.3856

Полученное p-value оказалось значительно выше 0.05, следовательно, нулевая гипотеза принимается — ряд нестационарен. И действительно: наличие сезонности и некоторого тренда на графиках выше этому подтверждение. Теперь перейдём к самому интересному — алгоритмам прогнозирования.

AR: как прошлое предсказывает будущее (и почему это не магия)

Представьте: вы хотите спрогнозировать, сколько пользователей зайдёт на ваш сервис завтра. У вас нет внешних данных — ни рекламы, ни праздников, ни погоды. Только история: сколько заходило вчера, позавчера и неделю назад. И тут приходит идея: «А что, если будущее уже закодировано в прошлом?» Именно на этом строится авторегрессионная модель (AR) — один из самых старых, но до сих пор актуальных инструментов анализа временных рядов.

Модель авторегрессии порядка p (обозначается как 𝐴𝑅(𝑝)) утверждает:

Сегодняшнее значение = взвешенная сумма p предыдущих значений + шум.

Формально:

X_t=varepsilon_t + phi_1 X_{t-1} + phi_2 X_{t-2} + dots + phi_p X_{t-p}

где:

  • X_t — значение ряда в момент t (например, продажи в день t);

  • phi_1...phi_2 — коэффициенты авторегрессии, которые показывают, насколько сильно каждое из p предыдущих значений влияет на текущее;

  • p — порядок модели AR, то есть количество лагов (прошлых значений ряда), учитываемых в модели;

  • varepsilon_t — гауссовский белый шум в момент t.

Интуитивно это можно понять так: если phi_1=0.9, то вчерашнее значение сильно влияет на сегодняшнее. Если phi_1=0.1 — почти не влияет.

И ещё, если модель обозначается как AR(3), то это значит, что она учитывает три
предыдущих значения:

X_t=phi_1 X_{t-1} + phi_2 X_{t-2} + phi_3 X_{t-3} + varepsilon_t

Модель AR(1) проста, понятна и удобна, если спрос ведёт себя стабильно. Она не требует сложных вычислений и часто используется в прогнозировании как базовая модель. Но если значение ряда резко меняется — из‑за акций, праздников, замены товара на аналог, то такая модель может сильно ошибаться. А у нас как раз наблюдается сезонность.

MA (Moving Average): «Я ошибся — и это полезно»

Представьте: вы прогнозировали продажи на вчерашний день. Реальность оказалась на 100 единиц выше. Эта разница — не просто статистический шум. На самом деле, именно такие отклонения лежат в основе одной из ключевых моделей временных рядов — MA(q), или модели скользящего среднего (Moving Average).

Но обратите внимание [4]: MA ≠ скользящему среднему в привычном понимании сглаживания данных. Это распространённое заблуждение. Давайте разберёмся, почему.

Модель MA порядка q выглядит так:

X_t=varepsilon_t + theta_1 varepsilon_{t-1} + theta_2 varepsilon_{t-2} + dots + theta_q varepsilon_{t-q}

где:

  • X_t— значение ряда в момент t (аналогично AR(p));

  • theta_1...theta_q— коэффициенты MA-части. Показывают, насколько сильно прошлые шоки влияют на текущее значение;

  • q — порядок модели MA, то есть количество прошлых ошибок (шоков),
    учитываемых в модели;

  • varepsilon_t — гауссовский белый шум в момент t.

Вот в чём ключевая идея: модель MA не сглаживает данные, как привычная скользящая средняя на графике. Вместо этого она моделирует текущее значение через прошлые ошибки [5] прогноза. То есть, если вчера вы ожидали продажи на 500 единиц, а продали 600, то разница (+100) становится шумом varepsilon_{t-1}. И сегодня, в момент 𝑡, эта «ошибка» может всё ещё влиять на реальные продажи: например, из‑за остатков рекламной активности, переполненного склада или просто инерции спроса.

Именно поэтому в модели MA нет X_{t-1}, varepsilon_{t-1} — только varepsilon_{t-1}, varepsilon_{t-2} и прочие шумы. Это не авторегрессия, когда прошлое значение тянет за собой будущее. Это своеобразная «память» об ошибках: рынок (или система) «помнит», насколько мы вчера промахнулись, и корректирует текущий расчетный момент.

ARMA: когда прошлое и ошибки работают в паре

Представьте, что вы — дирижёр оркестра временных рядов. Слева — скрипки AR (авторегрессии): они повторяют мелодию прошлого, потому что «всё, что было, влияет на то, что будет». Справа — виолончели MA (скользящего среднего): они играют аккорды на основе того, насколько вы ошиблись в предыдущих тактах.

А теперь вы поднимаете дирижёрскую палочку. И звучит ARMA — гармония памяти [6] и коррекции.

Формально модель ARMA(p, q) выглядит так:

X_t=varepsilon_p + phi_1 X_{t-1} + dots + phi_p X_{t-p} + theta_1 varepsilon_{t-1} + dots + theta_q varepsilon_{t-q}

где:

  • p — порядок авторегрессионной части;

  • q — порядок части скользящего среднего.

Объединение этих методов работает потому, что реальные процессы редко бывают только инерционными (как AR) или только реактивными применительно к ошибкам (как MA). Чаще бывает и то, и другое. ARMA учитывает оба этих эффекта в одном уравнении. Важный нюанс: ARMA требует стационарности, которой наш ряд не обладает.

ARIMA не верит в Новый Год. SARIMA — да

ARMA работает только со стационарными рядами, а реальные данные растут и падают по сезонам. ARIMA(p,d,q), расширение ARMA, обходит это просто: вместо исходного ряда она моделирует порядок его разности порядка d (например, nabla y_t=y_t - y_{t-1}), делая его стационарным, а потом применяет обычную ARMA.

Но что, если помимо тренда у вас есть сезонность? Как видно из нашего графика, некоторая величина неуклонно падает каждый январь. Обычная ARIMAне предназначена для эффективного моделирования выраженной сезонности — для этого существует её сезонное расширение: SARIMA.

На помощь приходит SARIMA — Seasonal ARIMA. Она добавляет к обычным параметрам (p,d,q) сезонный блок (P, D, Q)_s, где s— период (например, 12 для месячных данных). Теперь модель учитывает не только то, что было вчера, но и то, что было ровно год назад, и делает это через сезонные разности и сезонные лаги.

Именно эта модель подходит в нашем случае:

  • она учитывает сезонность через дополнительные параметры (P, D, Q) и период s=12 (для месячных данных);

  • позволяет моделировать как тренд, так и сезонные циклы (например, январские спады и декабрьские пики).

Для автоматического подбора оптимальных параметров SARIMA воспользуемся функцией auto_arima из библиотеки Pmdarima (обёртка над Statsmodels):

from pmdarima import auto_arima
from statsmodels.tsa.statespace.sarimax import SARIMAX

stepwise_fit = auto_arima(y['Сумма'], # Данные для обучения
                          m = 12, # Длина сезона
                          seasonal = True, # Указываем на наличие сезонности
                          trace = True, # Вывод отладочной информации
                          error_action ='ignore', # Игнорим ошибки
                          suppress_warnings = True) # Подавление предупреждений

order = stepwise_fit.order
seasonal_order = stepwise_fit.seasonal_order
sarimax_model = SARIMAX(y_train['Сумма'],
                        order=order, # Передаем параметры после auto_arima
                        seasonal_order=seasonal_order) # Передаем параметры после auto_arima
sarimax_fit = sarimax_model.fit()
predictions = sarimax_fit.forecast(steps=29) # Прогноз

Затем визуализируем прогноз на фоне реальных данных:

От нестационарности к прогнозу: пайплайн анализа и моделирования временных рядов - 64

Результаты:

  • Модель точно воспроизвела сезонные паттерны: спады в январе и рост к концу года.

  • Прогноз незначительно завышен относительно предыдущего года, что соответствует бизнес‑требованию о повышении суммы за год не больше, чем на индекс инфляции.

  • Ошибки прогноза на тестовом периоде оказались в допустимых пределах (MAPE ≈ 8,2%).

Выводы:

  • Анализ структуры ряда (тренд, сезонность, стационарность) — обязательный этап перед выбором модели.

  • Статистические тесты (например, ADF) надёжнее визуальной оценки при определении стационарности.

  • SARIMA — мощный инструмент для рядов с выраженной сезонностью и трендом.

  • Автоматизация подбора параметров (auto_arima) значительно ускоряет эксперименты и снижает порог входа.

Эта статья — не истина в последней инстанции, а отчёт о моём пути. Буду рад вашим ценным советам, замечаниям и альтернативным подходам!

Автор: mikneue

Источник [7]


Сайт-источник BrainTools: https://www.braintools.ru

Путь до страницы источника: https://www.braintools.ru/article/20448

URLs in this post:

[1] зрения: http://www.braintools.ru/article/6238

[2] повторением: http://www.braintools.ru/article/4012

[3] потребность: http://www.braintools.ru/article/9534

[4] внимание: http://www.braintools.ru/article/7595

[5] ошибки: http://www.braintools.ru/article/4192

[6] памяти: http://www.braintools.ru/article/4140

[7] Источник: https://habr.com/ru/companies/sberbank/articles/954636/?utm_campaign=954636&utm_source=habrahabr&utm_medium=rss

www.BrainTools.ru

Rambler's Top100