Эта статья проиллюстрирует вам базовое понимание проблем временных рядов и способ решения с использованием алгоритма пророка с Python.

В этой статье я не буду рассматривать такие темы, как стационарность, нестационарность, тенденция, сезонность, поскольку я чувствую, что вы все это знаете. Я покажу вам, как использовать алгоритм пророка с гипернастройкой с различными типами данных временных рядов.

Прежде чем приступать к проблемам временных рядов, важно знать разницу между аддитивным и мультипликативным режимами сезонности. Теоретически в аддитивном анализе все компоненты будут складываться, а в мультипликативном режиме компоненты будут умножаться.

Аддитивное значение: y(t) = Уровень + Тренд + Сезонность + Шум

Мультипликатив = y(t) = Уровень * Тренд * Сезонность * Шум

Но практически понять:

Аддитивное: если прогнозы данных выглядят постоянными, так что даже если тренд увеличивается, пики данных не будут сильно различаться и будут постоянными на протяжении всего ряда.

Мультипликативный. Если ряд имеет высокие пики, так что данные выглядят преувеличенными с изменением тренда. Здесь ряд данных выглядит как переход от высоких точек данных к более низким точкам данных.

В отличие от подходов машинного обучения, таких как разделение данных при обучении и тестировании (80%-20% или 75%-25%), мы должны разделить эти данные временных рядов в форме циклов. Если мы не можем разделить данные, есть большая вероятность упустить закономерности, что приведет к неправильному обучению и неправильному прогнозу.

Например: рассмотрим ряд данных, состоящий из 27 точек данных (10,20,30,60,80,100,40,30,20,15,25,35,65,85,105,45,35,25, 30,40). ,50,80,100,120,50,40,30).

Здесь группа из 9 точек принадлежит одному циклу и в каждом цикле 3 сезона(A,B,C)отмечен пунктирными стрелками. Поэтому нам нужно разделить первые 18 точек данных на обучающий набор, а следующие 9 точек данных на тестовый набор.

Давайте перейдем к коду о том, как использовать пророка:

Импорт пакетов:

from prophet import Prophet
from matplotlib import pyplot as plt 
import pandas as pd
import numpy as np
from sklearn.metrics import mean_absolute_percentage_error,mean_squared_error,median_absolute_error,mean_absolute_error

Некоторая базовая функция для получения графиков и частоты ошибок

def getReport(train_df,test_df,forecast):
    
    train_predictions = forecast[["yhat"]][train_df.index.start:train_df.index.stop]
    test_predictions = forecast[["yhat"]][test_df.index.start:test_df.index.stop]
    
    print("Training")
    print("Mean Absolute Percentage Error: ",np.round(mean_absolute_percentage_error(train_df[["y"]],train_predictions)*100,2))
    print("Mean Absolute Error: ",np.round(mean_absolute_error(train_df[["y"]],train_predictions),2))

    plt.figure(figsize=(20,7))
    plt.plot(train_df[["y"]],label="trainig actuals",marker="o")
    plt.plot(train_predictions,label="training predictions",marker="*")
    plt.legend()
    plt.show()
    print("Testing")
    print("Mean Absolute Percentage Error: ",np.round(mean_absolute_percentage_error(test_df[["y"]],test_predictions)*100,2))
    print("Mean Absolute Error: ",np.round(mean_absolute_error(test_df[["y"]],test_predictions),2))

    plt.figure(figsize=(20,7))
    plt.plot(test_df[["y"]],label="testing actuals",marker="o",c="blue")
    plt.plot(test_predictions,label="testing predictions",marker="*",c="orange")
    plt.plot(forecast[["yhat"]][test_df.index.stop:],label="Forecast",marker="*",c="green")
    plt.legend()
    plt.show()

Давайте возьмем образцы данных и подгоним к ним FB Prophet:

data = [10,20,30,60,80,100,40,30,20,15,25,35,65,85,105,45,35,25,30,40,50,80,100,120,50,40,30]
df = pd.DataFrame({"y":data})
df["ds"] = pd.date_range("2022-01-01", periods=len(data), freq="m")

train_df = df[:18] 
test_df = df[18:]
plt.figure(figsize=(20,7))
plt.plot(train_df[["y"]],label="Training data",marker="o")

Здесь мы берем первые 2 цикла данных для обучения и 1 цикл для тестирования. Для работы с Prophet вам нужно передать 2 параметра data и values. Итак, я создал один столбец даты (ds) с месяцем в качестве частоты и один столбец значений (y).

Здесь пророк будет иметь параметры по умолчанию, такие как yearly_seasonality, weekly-seasonality и т. д., которые не помогут нам в подгонке наших данных. Итак, нам нужно перейти к пользовательским функциям сезонности.

Здесь мы можем использовать метод add_seasonality:

m.add_seasonality(
    name,
    period,
    fourier_order,
    prior_scale=None,
    mode=None,
    condition_name=None,
)
name: name of the seasonality
periods: number of days,
fourier order:Number of fourier components to use
mode: additive/multiplicative

у нас есть 9*30 дней в каждом цикле, и режим является аддитивным. Нам нужно продолжать изменять значения порядка Фурье и проверять наилучшее соответствие. Высокое значение порядка Фурье приводит к переобучению, тогда как более низкое значение приводит к подгонке.

m = Prophet()
m.add_seasonality("cycle1",9*30,6,mode="additive")
m.fit(train_df)
future = m.make_future_dataframe(periods=len(test_df)+18, freq="m")
forecast = m.predict(future)
getReport(train_df,test_df,forecast)

Давайте посмотрим, как изменение порядка Фурье соответствует обученным данным.

Порядок Фурье 7 приводит к переоснащению, поэтому мы рассмотрели порядок Фурье как 6.

ПРИМЕР 2:

Рассмотрим данные: [10,100,29,199,51,300,68,404,91,501,111,602,128,701,152,800,171,900,191,1000,211,1101,231,1202]

Здесь вы можете наблюдать, как данные меняются при высоких амплитудах. Таким образом, мы можем рассматривать это как мультипликативное.

вы можете наблюдать разницу в сезонном разложении между аддитивным и мультипликативным на графике ниже.

Здесь каждый цикл имеет 2 точки данных. Итак, мы рассматриваем 9 циклов, то есть 18 точек данных, в качестве обучающего набора данных.

df = pd.DataFrame({"y":data})
df["ds"] = pd.date_range("2018-01-01", periods=len(data), freq="m")
train_df = df[:18]
test_df = df[18:]

m = Prophet()
m.add_seasonality(“cycle1”,2*30,3,mode=”multiplicative”)
m.fit(train_df)
future = m.make_future_dataframe(periods=len(test_df)+2, freq="m")
forecast = m.predict(future)
getReport(train_df,test_df,forecast)

Таким образом, мы получили коэффициент ошибок 4,3.

ПРИМЕР 3:

Рассмотрим другой ряд данных:

data = [70, 20,22,25,23, 150,160,170, 200,220,240,260,
 280, 20,25,23,25, 200,211,220, 250,270,285,310,
 330, 20,21,22,24, 250,260,270, 300,320,340,360,
 380, 20,24,21,23, 300,310,320, 350,370,390,410]
plt.figure(figsize=(20,7))
plt.plot(data,marker="o")

Сезонная декомпозиция

# seasonal decomposition for all data points
df = pd.DataFrame({"y":data})
df.index = pd.date_range("2018–01–01", periods=len(data), freq="m")
from statsmodels.tsa.seasonal import seasonal_decompose
result = seasonal_decompose(df, model='additive')
fig = result.plot()
fig.set_size_inches((15, 10))
plt.show()


# seasonal decomposition by removing 1st data point 
df = pd.DataFrame({“y”:data})
df.index = pd.date_range(“2018–01–01”, periods=len(data), freq=”m”)
from statsmodels.tsa.seasonal import seasonal_decompose
result = seasonal_decompose(df[1:], model=’additive’)
fig = result.plot()
fig.set_size_inches((15, 10))
plt.show()

Когда мы построили сезонную декомпозицию для приведенного выше ряда данных, мы получили один график с именем PLOT A. как вы можете видеть на изображении ниже, есть одна точка, которая находится вне циклов сезонности. Удалив эту точку данных, когда мы построили другую декомпозицию, мы получили полные циклы, которые можно наблюдать на ГРАФИКЕ B с началом и концом сезонности.

Итак, мы должны рассмотреть данные из 1 индекса.

data =data[1:]
df = pd.DataFrame({"y":data})
df["ds"] = pd.date_range("2020-01-01", periods=len(data), freq="m")
train_df = df[:36]
test_df = df[36:]
plt.figure(figsize=(20,7))
plt.plot(train_df[["y"]],label="Training data",marker="o")

Здесь в каждом сезоне насчитывается 12 точек данных. Как было предложено ранее, обучающие данные должны иметь полные циклы данных. Теперь мы разделяем 12 * 3 = 36 точек данных в качестве набора данных для обучения и оставляем в качестве набора данных для тестирования.

Здесь 12 месяцев в сезоне. так что вы можете просто установить yearly_seasonality как true в пророке.

m = Prophet(yearly_seasonality=True)
m.fit(train_df)
future = m.make_future_dataframe(periods=len(test_df)+24, freq=”m”)
forecast = m.predict(future)
getReport(train_df,test_df,forecast)

Это выглядит очень высоким уровнем ошибок 121. Итак, теперь мы перейдем к пользовательской сезонности, установив пользовательские параметры.

m = Prophet()
m.add_seasonality(“cycle1”,12*30,3,mode=”additive”)
m.fit(train_df)
future = m.make_future_dataframe(periods=len(test_df)+24, freq=”m”)
forecast = m.predict(future)
getReport(train_df,test_df,forecast)

Здесь вы можете заметить, что частота ошибок упала до 9,6. Итак, это лучший подход.

Как мы видим, 4 точки данных лежат в одном диапазоне от 20 до 25, тогда как другие 8 точек данных находятся в другом диапазоне, и они постоянно увеличиваются по мере увеличения тренда. Итак, мы упомянем еще 2 пользовательских сезонности с 4, 8 точками данных и посмотрим, как они работают.

m = Prophet()
m.add_seasonality("cycle1",12*30,1,mode="additive")
m.add_seasonality("cycle2",4*30,1,mode="additive")
m.add_seasonality("cycle3",8*30,1,mode="additive")
m.fit(train_df)
future = m.make_future_dataframe(periods=len(test_df)+12, freq="m")

forecast = m.predict(future)
getReport(train_df,test_df,forecast)

Итак, теперь частота ошибок упала до 2,5, что очень хорошо.

Таким образом, мы можем использовать алгоритм пророка, основанный на различных типах рядов данных.

Вы можете найти подробную документацию для FB Prophet по адресу https://facebook.github.io/prophet/docs/quick_start.html.