Разложение STL превосходно работает, когда сезонные закономерности со временем меняются.
Делиться

В первых двух частях этой серии мы исследовали тренд, сезонность и остаточные явления на примере данных о температуре. Мы начали с выявления закономерностей в данных с помощью метода season_decompose в Python. Затем мы составили наши первые прогнозы температуры, используя стандартные базовые модели, такие как сезонная наивная модель.
Далее мы пошли глубже и узнали, как season_decompose на самом деле вычисляет тренд, сезонность и остаточные компоненты.
Мы извлекли эти фрагменты, чтобы построить базовую модель на основе декомпозиции, а затем экспериментировали с пользовательскими базовыми показателями, адаптированными к нашим данным.
Наконец, мы оценили каждую модель с помощью средней абсолютной процентной ошибки (MAPE), чтобы увидеть, насколько эффективны наши подходы.
В первых двух частях мы работали с данными о температуре — относительно простым набором данных, в котором тенденция и сезонность были очевидны, а season_decompose хорошо справлялся с фиксацией этих закономерностей.
Однако во многих реальных наборах данных всё не так однозначно. Тенденции и сезонные закономерности могут меняться или становиться запутанными, и в таких случаях season_decompose может не так эффективно отражать базовую структуру.
Здесь мы обратимся к более продвинутому методу разложения, чтобы лучше понять данные: STL — сезонно-трендовая разложение с использованием LOESS.
LOESS означает локально оцененное сглаживание диаграмм рассеяния .
Чтобы лучше понять это на практике, мы воспользуемся набором данных о розничных продажах в универмагах из FRED (Федеральная резервная система экономических данных).
Вот как выглядят данные:

Набор данных, с которым мы работаем, отслеживает ежемесячные розничные продажи в универмагах США и поступает из надежного источника FRED (Федеральная резервная система экономических данных).
В нем всего два столбца:
- Дата_наблюдения – начало каждого месяца
- Retail_Sales – общий объем продаж за этот месяц, в миллионах долларов.
Временной ряд охватывает период с января 1992 года по март 2025 года , что дает нам для изучения данные о продажах за более чем 30 лет.
Примечание: несмотря на то, что каждая дата отмечает начало месяца (например, 01.01.1992), объем продаж представляет собой общий объем продаж за весь месяц.
Но прежде чем перейти к STL, мы запустим классический season_decompose на нашем наборе данных и посмотрим, что он нам покажет.
Код:
import pandas as pd import matplotlib.pyplot as plt from statsmodels.tsa.seasonal import season_decompose # Загрузка набора данных df = pd.read_csv(«C:/RSDSELDN.csv», parse_dates=['Observation_Date'], dayfirst=True) # Установка столбца даты в качестве индекса df.set_index('Observation_Date', inplace=True) # Установка ежемесячной частоты df = df.asfreq('MS') # MS = Начало месяца # Извлечение ряда series = df['Retail_Sales'] # Применение классической сезонной декомпозиции result = season_decompose(series, model='additive', period=12) # График с пользовательскими цветами fig, axs = plt.subplots(4, 1, figsize=(12, 8), sharex=True) axs[0].plot(result.observed, color='olive') axs[0].set_title('Наблюдаемый') axs[1].plot(result.trend, color='darkslateblue') axs[1].set_title('Тенденция') axs[2].plot(result.seasonal, color='darkcyan') axs[2].set_title('Сезонный') axs[3].plot(result.resid, color='peru') axs[3].set_title('Остаток') plt.suptitle('Классическая сезонная декомпозиция (аддитивная)', fontsize=16) plt.tight_layout() plt.show()
Сюжет:

Во второй части мы рассмотрели, как season_decompose вычисляет трендовые и сезонные компоненты в предположении фиксированной повторяющейся сезонной структуры.
Однако реальные данные не всегда следуют фиксированной схеме. Тенденции могут меняться постепенно, а сезонные колебания могут меняться из года в год. Именно поэтому нам нужен более адаптивный подход, и декомпозиция STL предлагает именно это.
Мы применим разложение STL к данным, чтобы увидеть, как они справляются с меняющимися тенденциями и сезонностью.
import pandas as pd import matplotlib.pyplot as plt from statsmodels.tsa.seasonal import STL # Загрузка набора данных df = pd.read_csv(«C:/RSDSELDN.csv», parse_dates=['Observation_Date'], dayfirst=True) df.set_index('Observation_Date', inplace=True) df = df.asfreq('MS') # Обеспечение ежемесячной частоты # Извлечение временного ряда series = df['Retail_Sales'] # Применение разложения STL stl = STL(series, season=13) result = stl.fit() # Построение графика и сохранение компонентов STL fig, axs = plt.subplots(4, 1, figsize=(10, 8), sharex=True) axs[0].plot(result.observed, color='sienna') axs[0].set_title('Наблюдается') axs[1].plot(result.trend, color='goldenrod') axs[1].set_title('Тенденция') axs[2].plot(result.seasonal, color='darkslategrey') axs[2].set_title('Сезонный') axs[3].plot(result.resid, color='rebeccapurple') axs[3].set_title('Остаток') plt.suptitle('STL-разложение розничных продаж', fontsize=16) plt.tight_layout() plt.show()
Сюжет:

После завершения этого шага и понимания того, что делает STL, мы углубимся в то, как он выявляет тенденции и сезонные закономерности за кулисами.
Чтобы лучше понять, как работает разложение STL, мы рассмотрим выборку из нашего набора данных, охватывающую период с января 2010 года по декабрь 2023 года.

Чтобы понять, как разложение STL работает с этими данными, нам сначала нужны грубые оценки тренда и сезонности.
Поскольку STL — это метод, основанный на сглаживании, он требует первоначального представления о том, что именно следует сглаживать, например, где находится тренд и как ведут себя сезонные закономерности.
Начнем с визуализации ряда розничных продаж (январь 2010 г. – декабрь 2023 г.) и применим процедуру STL Python для извлечения его трендовых, сезонных и остаточных частей.
Код:
import pandas as pd import matplotlib.pyplot as plt from statsmodels.tsa.seasonal import STL # Загрузка набора данных df = pd.read_csv(«C:/STL sample data.csv», parse_dates=['Observation_Date'], dayfirst=True) df.set_index('Observation_Date', inplace=True) df = df.asfreq('MS') # Обеспечение ежемесячной частоты # Извлечение временного ряда series = df['Retail_Sales'] # Применение разложения STL stl = STL(series, season=13) result = stl.fit() # Построение графика и сохранение компонентов STL fig, axs = plt.subplots(4, 1, figsize=(10, 8), sharex=True) axs[0].plot(result.observed, color='sienna') axs[0].set_title('Наблюдаемое') axs[1].plot(result.trend, color='goldenrod') axs[1].set_title('Тенденция') axs[2].plot(result.seasonal, color='darkslategrey') axs[2].set_title('Сезонный') axs[3].plot(result.resid, color='rebeccapurple') axs[3].set_title('Остаток') plt.suptitle('STL-разложение розничных продаж (2010-2023)', fontsize=16) plt.tight_layout() plt.show()
Сюжет:

Чтобы понять, как STL выводит свои компоненты, мы сначала оцениваем долгосрочную тенденцию данных, используя центрированное скользящее среднее.
Мы воспользуемся примером одного месяца, чтобы продемонстрировать, как рассчитать центрированную скользящую среднюю.
Мы рассчитаем центрированную скользящую среднюю за июль 2010 года .

Поскольку наши данные помесячные, естественный цикл охватывает двенадцать точек, что является чётным числом. Усреднение периода с января по декабрь 2010 года даёт значение, которое находится посередине между июнем и июлем.
Чтобы скорректировать это, мы формируем второе окно с февраля 2010 года по январь 2011 года, двенадцатимесячное среднее значение которого находится посередине между июлем и августом.
Затем мы вычисляем простое среднее значение каждого окна и усредняем эти два результата.
В первом окне июль — седьмой из двенадцати пунктов, поэтому среднее значение приходится на период между шестым и седьмым месяцами.
Во втором окне июль является шестым из двенадцати пунктов, поэтому его среднее значение также попадает между шестым и седьмым месяцами, но смещено вперед.
Усреднение обеих оценок возвращает результат к июлю 2010 года, давая истинно центрированное скользящее среднее значение за этот месяц.


Вот как мы вычисляем начальный тренд, используя центрированную скользящую среднюю.
В самом начале и конце нашего ряда у нас просто нет шести месяцев с обеих сторон для усреднения, поэтому нет «естественной» центрированной скользящей средней за январь-июнь 2010 года или июль-декабрь 2023 года.
Вместо того чтобы отбрасывать эти точки, мы переносим первое действительное значение за июль 2010 года назад, чтобы заполнить период январь–июнь, и переносим наше последнее действительное значение за декабрь 2023 года вперед, чтобы заполнить период июль–декабрь 2023 года.
Таким образом, каждый месяц будет иметь базовую тенденцию, прежде чем мы перейдем к уточнениям LOESS.
Далее мы будем использовать Python для вычисления первоначального тренда для каждого месяца.
Код:
import pandas as pd # Загрузка и подготовка данных df = pd.read_csv(«C:/STL sample data for part 3.csv», parse_dates=[«Observation_Date»], dayfirst=True, index_col=»Observation_Date») df = df.asfreq(«MS») # обеспечение непрерывного ежемесячного индекса # Извлечение ряда sales = df[«Retail_Sales»] # Вычисление двух 12-месячных скользящих средних n = 12 ma1 = sales.rolling(window=n, center=False).mean().shift(-n//2 + 1) ma2 = sales.rolling(window=n, center=False).mean().shift(-n//2) # Центрирование их путем усреднения T0 = (ma1 + ma2) / 2 # Заполнение краев так, чтобы каждый месяц имел значение T0 = T0.fillna(method=»bfill»).fillna(method=»ffill») # Присоединить к DataFrame df[«Initial_Trend»] = T0
Стол:

Мы извлекли начальный тренд с помощью центрированной скользящей средней, давайте посмотрим, как это выглядит на самом деле.
Мы отобразим ее на графике вместе с исходным временным рядом и окончательной линией тренда STL, чтобы сравнить, как каждая из них отражает общее движение данных.
Сюжет:

Глядя на график, мы видим, что линия тренда от скользящей средней практически совпадает с трендом STL на протяжении большей части лет.
Однако в январе-феврале 2020 года скользящая средняя линия резко упала. Это падение было вызвано внезапным влиянием COVID на продажи.
STL справляется с этим лучше: он не рассматривает это как долгосрочное изменение тренда, а вместо этого отмечает это как остаточное.
Это связано с тем, что STL рассматривает это как разовое неожиданное событие, а не повторяющуюся сезонную закономерность или изменение общей тенденции.
Чтобы понять, как STL это делает и как он обрабатывает меняющуюся сезонность, давайте продолжим развивать наше понимание шаг за шагом.
Теперь у нас есть начальный тренд с использованием скользящих средних, поэтому давайте перейдем к следующему шагу в процессе STL.
Источник: towardsdatascience.com



























