Получение компонент временного ряда
Рассмотрим способ разложения временного ряда на трендовую, сезонную и остаточную составляющие. Под трендом понимаем общую закономерность ряда (изменение среднего значения со временем), под сезонностью — повторяющуюся закономерность в определенных периодах (чтобы говорить о сезонности, в датасете должно быть несколько периодов).
Для демонстрации загрузим набор данных о статистике пассажирских перелетов с 1949 по 1960 из библиотеки pmdarima:
import pmdarima as pm import pandas as pd ts = pm.datasets.load_airpassengers(as_series=True) ts.index = pd.date_range('1949', '1961', freq='M') ts.head()
Разложение на перечисленные выше составляющие можно произвести с помощью функции seasonal_decompose из модуля statsmodels.tsa.seasonal:
from statsmodels.tsa.seasonal import seasonal_decompose parts = seasonal_decompose(ts, model="additive") _ = parts.plot()
В атрибутах возвращаемого seasonal_decompose объекта хранятся нужные нам тренд, сезонность и остатки. Проверим, что разложение в сумме (так как модель аддитивная) дает исходный ряд (для наглядности отобразил ряд с первого заполненного члена, так как в результате подсчета составляющих по умолчанию получаются пропуски):
(parts.trend.loc['1949-07':] + parts.seasonal.loc['1949-07':] + parts.resid.loc['1949-07':]).head(7)
ts.loc['1949-07':].head(7)
Тренд
Теперь подробнее рассмотрим составляющие разложения и попробуем получить их вручную. Тренд извлекается путем подсчета скользящих средних с центрированным окном. Сравните:
parts.trend.plot()
ts.rolling(center=True, window=14).mean().plot()
Ручной результат немного отличается ввиду использования в seasonal_decompose незначительно отличающейся функции свертки.
Сезонность
Автоматически полученная сезонность для всех компонентов такая:
parts.seasonal['1960'].plot()
Для получения сезонности вручную достаточно вычесть из исходного ряда тренд и получить среднее по "сезонным" периодам (в данном случае месяцам):
ts_detrend = ts - parts.trend ts_detrend.to_frame().assign(mon=ts_detrend.index.month).groupby('mon')[0].mean().plot()
Остатки
Остатки получаем как разность между исходным рядом с одной стороны и трендом и сезонностью с другой:
ts_seas = ts_detrend.to_frame().assign(mon=ts_detrend.index.month).groupby('mon')[0].transform('mean') ts_resid = ts_detrend - ts_seas ts_resid.plot()
А вот остатки, полученные с seasonal_decompose:
parts.resid.plot()