【问题标题】:Forecasting time series with multiple seasonaliy by using auto_arima(SARIMAX) and Fourier terms使用 auto_arima(SARIMAX) 和傅里叶项预测具有多个季节性的时间序列
【发布时间】:2021-10-25 15:19:31
【问题描述】:

我正在尝试通过使用 auto_arima 并添加傅里叶项作为外生特征来预测 Python 中的时间序列。数据来自 kaggle 的Store item demand forecasting challenge。它由 10 家商店和 50 件商品的长格式时间序列组成,从而形成 500 个相互堆叠的时间序列。该时间序列的特殊性在于它具有具有每周和每年季节性的每日数据。

为了捕捉这两个季节性水平,我首先使用了 Rob J Hyndman 在Forecasting with daily data 中推荐的 TBATS,实际上效果很好。

我还关注了 TBATS python 库的创建者发布的 medium article,他将其与 SARIMAX + Fourier 项进行了比较(Hyndman 也推荐)。

但是现在,当我尝试使用第二种方法,将 pmdarima 的 auto_arima 和 Fourier 项作为外生特征时,我得到了意想不到的结果。

在下面的代码中,我只使用了我拆分为训练数据和测试数据(去年用于预测)的 train.csv 文件,并设置傅里叶项的最大阶数 K = 2。

我的问题是我获得了一个平滑的预测(见下图),它似乎没有捕捉到与article 结尾处的结果不同的每周季节性。 我的代码有问题吗?

完整代码:

# imports
import pandas as pd
from pmdarima.preprocessing import FourierFeaturizer
from pmdarima import auto_arima
import matplotlib.pyplot as plt

# Upload the data that consist in a long format time series of multiple TS stacked on top of each other
# There are 10 (stores) * 50 (items) = 500 time series
train_data = pd.read_csv('train.csv', index_col='date', parse_dates=True)

# Select only one time series for store 1 and item 1 for the purpose of the example
train_data = train_data.query('store == 1 and item == 1').sales

# Prepare the fourier terms to add as exogenous features to auto_arima
# Annual seasonality covered by fourier terms
four_terms = FourierFeaturizer(365.25, 2)
y_prime, exog = four_terms.fit_transform(train_data)
exog['date'] = y_prime.index # is exactly the same as manual calculation in the above cells
exog = exog.set_index(exog['date'])
exog.index.freq = 'D'
exog = exog.drop(columns=['date'])


# Split the time series as well as exogenous features data into train and test splits 
y_to_train = y_prime.iloc[:(len(y_prime)-365)]
y_to_test =  y_prime.iloc[(len(y_prime)-365):] # last year for testing

exog_to_train = exog.iloc[:(len(exog)-365)]
exog_to_test = exog.iloc[(len(exog)-365):]


# Fit model
# Weekly seasonality covered by SARIMAX
arima_exog_model = auto_arima(y=y_to_train, exogenous=exog_to_train, seasonal=True, m=7)

# Forecast
y_arima_exog_forecast = arima_exog_model.predict(n_periods=365, exogenous=exog_to_test)
y_arima_exog_forecast = pd.DataFrame(y_arima_exog_forecast , index = pd.date_range(start='2017-01-01', end= '2017-12-31'))


# Plots
plt.plot(y_to_test, label='Actual data')
plt.plot(y_arima_exog_forecast, label='Forecast')
plt.legend()

提前感谢您的回答!

【问题讨论】:

  • 我没有尝试运行你的代码,但是我从pmdarima docs看到m应该是每个季节的周期数。您已将其设为 7,这是错误的,因为您有 1 年的数据。我假设这个数字应该是 52。
  • 既然有每日记录,为什么会是52?从文档中我了解到 m 是我的数据集中每个季节的记录数。在数据中,我们有每周的季节性和每年的季节性。因此我有两种可能性:m = 7 或 m = 365。但我不明白为什么它应该是 52。你能详细说明一下吗?而且因为 ARIMA 不处理长季节性,所以我将它用于每周的季节性。
  • 我假设是因为一年有 52 周。据我了解,365 表示每日季节性,1 表示年度季节性。这只是一个猜测,我现在正在运行您的代码进行检查。
  • 运行 25 分钟后,Colab 内存不足。抱歉,我帮不上什么忙。祝你好运!
  • 您的代码没有任何问题,但由于某种原因,auto_arima 发现每周的季节性差异对于您的数据来说并不是最佳的(即它返回 D=0 其中D 是季节差异)。可以直接在auto_arima调用中设置D=1,或者其他方式离开D=None,改变auto_arima的其他优化参数(如信息准则、迭代次数等),看最终是否返回D=1,在这种情况下,您的预测将符合预期。

标签: python time-series fft forecasting pmdarima


【解决方案1】:

如果有人感兴趣,这里是答案。 再次感谢 Flavia Giammarino。

# imports
import pandas as pd
from pmdarima.preprocessing import FourierFeaturizer
from pmdarima import auto_arima
import matplotlib.pyplot as plt

# Upload the data that consists long format time series of multiple TS stacked on top of each other
# There are 10 (stores) * 50 (items) time series
train_data = pd.read_csv('train.csv', index_col='date', parse_dates=True)

# Select only one time series for store 1 and item 1 for the purpose of the example
train_data = train_data.query('store == 1 and item == 1').sales

# Prepare the fourier terms to add as exogenous features to auto_arima
# Annual seasonality covered by fourier terms
four_terms = FourierFeaturizer(365.25, 1)
y_prime, exog = four_terms.fit_transform(train_data)
exog['date'] = y_prime.index # is exactly the same as manual calculation in the above cells
exog = exog.set_index(exog['date'])
exog.index.freq = 'D'
exog = exog.drop(columns=['date'])


# Split the time series as well as exogenous features data into train and test splits 
y_to_train = y_prime.iloc[:(len(y_prime)-365)]
y_to_test =  y_prime.iloc[(len(y_prime)-365):] # last year for testing

exog_to_train = exog.iloc[:(len(exog)-365)]
exog_to_test = exog.iloc[(len(exog)-365):]


# Fit model
# Weekly seasonality covered by SARIMAX
arima_exog_model = auto_arima(y=y_to_train, D=1, exogenous=exog_to_train, seasonal=True, m=7)

# Forecast
y_arima_exog_forecast = arima_exog_model.predict(n_periods=365, exogenous=exog_to_test)
y_arima_exog_forecast = pd.DataFrame(y_arima_exog_forecast , index = pd.date_range(start='2017-01-01', end= '2017-12-31'))


# Plots
plt.plot(y_to_test, label='Actual data')
plt.plot(y_arima_exog_forecast, label='Forecast')
plt.legend()

【讨论】:

  • 根据robjhyndman.com/hyndsight/longseasonality,当使用傅里叶项作为外生变量时,ARIMA模型不应该是季节性的。
  • 什么公关。 Hyndman 说,ARIMA 模型并不意味着能够捕捉到较长的季节性。这就是为什么将 seaonal 参数设置为 False 并且他使用傅立叶项的原因。但在我的情况下,它涉及两个季节性周期,其中有一个较小的周期(每周,m = 7)。在我发布的link 中,seasonal=FALSE 但是当您在下面加载更多 cmets 并查找“Nassim”(相关问题的海报)时,Pr。 Hyndman 确认您可以在 auto.arima 函数中使用 seaonal=TRUE 处理较小的季节性。希望这会有所帮助。
猜你喜欢
  • 2019-04-11
  • 2018-06-01
  • 2015-04-05
  • 2020-01-14
  • 2016-07-26
  • 2015-07-14
  • 2011-05-27
  • 2022-01-04
  • 1970-01-01
相关资源
最近更新 更多