时间序列预测是基于时间数据的预测任务。它包括构建模型以在天气、工程、经济、金融或商业预测等应用中进行观察并推动未来决策。本文重点关注时间序列预测,并描述任何时间序列的两种主要模式(趋势和季节性)。并根据这些模式分解时间序列。最后,使用称为Holt-Winters季节性方法的预测模型来预测具有趋势和/或季节性成分的时间序列数据。为了涵盖所有这些,我们将使用一个时间序列数据集,该数据集包含1981年至1991年间墨尔本(澳大利亚)的温度。该数据集可以从这个Kaggle下载,也可以从本文末尾的GitHub下载,其中包含本文的数据和代码。该数据集由澳大利亚政府气象局托管,并根据该局的默认使用条款(开放访问许可)获得许可。导入库和数据首先,导入运行代码所需的以下库。除了最典型的库之外,代码还基于statsmomodels库提供的函数,它提供了用于估计许多不同统计模型的类和函数,例如统计测试和预测模型。fromdatetimeimportdatetime,timedeltaimportmatplotlib.pyplotaspltimportnumpyasnpiportpandasaspdimportstatsmodels.apiassmfromstatsmodels.tsa.stattoolsimportadfuller,kpssfromstatsmodels.tsa.apiimportExponentialSmoothing%matplotlibinline下面是导入数据的代码数据由两列组成,一列包含日期,另一列包含1981年至1991年间墨尔本(澳大利亚)的温度。#datenumdays=365*10+2base='2010-01-01'base=datetime.strptime(base,'%Y-%m-%d')date_list=[base+timedelta(days=x)forxinrange(numdays)]date_list=np.array(date_list)print(len(date_list),date_list[0],date_list[-1])#tempx=np.linspace(-np.pi,np.pi,365)temp_year=(np.sin(x)+1.0)*15x=np.linspace(-np.pi,np.pi,366)temp_leap_year=(np.sin(x)+1.0)temp_s=[]foriinrange(2010,2020):如果i==2010:temp_s=temp_year+np.random.rand(365)*20elifiin[2012,2016]:temp_s=np.concatenate((temp_s,temp_leap_year*15+np.random.rand(366)*20+i%2010))else:temp_s=np.concatenate((temp_s,temp_year+np.random.rand(365)*20+i%2010))print(len(temp_s))#dfdata=np.concatenate((date_list.reshape(-1,1),temp_s.reshape(-1,1)),axis=1)df_orig=pd.DataFrame(data,columns=['date','temp'])df_orig['date']=pd.to_datetime(df_orig['date'],format='%Y-%m-%d')df=df_orig.set_index('date')df.sort_index(inplace=True)可视化df数据集在我们开始分析时间序列的模式之前,让我们可视化数据,每条垂直虚线对应于年初ax=df_orig.plot(x='date',y='temp',figsize=(12,6))xcoords=['2010-01-01','2011-01-01','2012-01-01','2013-01-01','2014-01-01','2015-01-01','2016-01-01','2017-01-01','2018-01-01','2019-01-01']forxcinxcoords:plt.axvline(x=xc,color='black',linestyle='--')ax.set_ylabel('temperature')在进入下一节之前,让我们花点时间看一下数据。数据似乎有季节性变化,冬季气温升高,夏季气温降低(南半球)。而且温度似乎并没有随着时间的推移而升高,因为无论哪一年平均温度都是一样的。时间序列模式时间序列预测模型使用数学方程式来寻找一系列历史数据中的模式。然后使用这些方程式来预测数据中的历史时间模式[时间序列模式有四种类型:趋势:数据的长期增加或减少。趋势可以是任何函数,例如线性或指数函数,并且可以随时间改变方向。季节性:以固定频率(一天中的小时、周、月、年等)在一系列中重复的时期。季节性模式具有固定的已知周期性:在数据上升和下降时发生,但没有固定的频率和持续时间,例如经济条件造成的。噪声:序列中的随机变化。大多数时间序列数据将包含一种或多种模式,但可能不是全部。以下是我们可以识别这些时间序列模式的一些示例:维基百科年度受众(左图):在此图中,我们可以识别出受众每年线性增长的增长趋势。美国用电量季节性图(中):每条线对应一年,因此我们可以观察到年复一年的用电量季节性。ibex35的每日收盘价(右图):该时间序列随着时间的推移呈上升趋势,并且具有周期性模式,因为ibex35有时会因经济原因而下跌。如果我们假设这些模式的加法分解,我们可以写成:Y[t]=t[t]+S[t]+e[t]其中Y[t]是数据,t[t]是趋势周期component,S[t]是季节成分,e[t]是噪声,t是时间段。另一方面,乘法分解可以写成:Y[t]=t[t]S[t]e[t]当季节性波动不随时间序列水平变化时,加法分解是最合适的方法。相反,当季节性成分的变化与时间序列的水平成正比时,乘法分解更为合适。分解的数据平稳时间序列被定义为独立于观察序列的时间。因此,具有趋势或季节性的时间序列不是平稳的,而白噪声序列是平稳的。在数学意义上,如果时间序列的均值和方差是常数并且协方差与时间无关,则时间序列是平稳的。有不同的例子来比较平稳和非平稳时间序列。一般来说,固定时间序列不会有长期可预测的模式。为什么稳定性很重要?平稳性已成为时间序列分析中许多实践和工具的共同假设。这些包括趋势估计、预测和因果推理。因此,在许多情况下,有必要确定数据是否由平稳过程生成,并将其转换为具有该过程生成的样本的属性。如何检验时间序列的平稳性?我们可以用两种方法来测试。一方面,我们可以通过检查时间序列的均值和方差来手动检查。另一方面,我们可以使用测试函数来评估平稳性。有些情况可能会令人困惑。例如,没有趋势和季节性但具有周期性行为的时间序列是平稳的,因为周期的长度不固定。查看趋势要分析时间序列中的趋势,我们首先使用具有30天窗口的滚动平均值方法分析随时间变化的平均值。defanalyze_stationarity(timeseries,title):fig,ax=plt.subplots(2,1,figsize=(16,8))rolmean=pd.Series(timeseries).rolling(window=30).mean()rolstd=pd.Series(timeseries).rolling(window=30).std()ax[0].plot(timeseries,label=title)ax[0].plot(rolmean,label='rollingmean');ax[0].plot(rolstd,label='rollingstd(x10)');ax[0].set_title('30天窗口')ax[0].legend()rollmean=pd.Series(timeseries).rolling(window=365).mean()rolstd=pd.Series(timeseries).rolling(window=365).std()ax[1].plot(timeseries,label=title)ax[1].plot(rolmean,label='滚动平均值');ax[1].plot(rolstd,label='rollingstd(x10)');ax[1].set_title('365天窗口')pd.options.display.float_format='{:.8f}'.formatanalyze_stationarity(df['temp'],'原始数据')ax[1].legend()在上图中,我们可以看到滚动平均值在使用30天窗口时如何随时间波动,这是由数据的季节性模式引起的。此外,当使用365天窗口时,滚动平均值随时间增加,表明随时间略有增加的趋势。这也可以通过Dickey-Fuller(ADF)和Kwiatkowski,Phillips,SchmidtandShin(KPSS)等检验来评估:ADF检验的结果(p值低于0.05)表明零假设的存在可以发现在95%的Confidencelevel被拒绝。因此,如果p值低于0.05,则时间序列是平稳的。defADF_test(timeseries):print("Dickey-Fuller测试结果:")dftest=adfuller(timeseries,autolag="AIC")dfoutput=pd.Series(dftest[0:4],index=["测试统计","p-value","LagsUsed","NumberofObservationsUsed",],)forkey,valueindftest[4].items():dfoutput["临界值(%s)"%key]=valueprint(dfoutput)ADF_test(df)ResultsofDickey-FullerTest:TestStatistic-3.69171446p-value0.00423122LagsUsed30.00000000NumberofObservationsUsed3621.00000000临界值(1%)-3.43215722临界值(5%??)-28.8536233(10%)-2.56719507dtype:float64KPSS检验的结果(p值高于0.05)表明在95%的置信水平下不能拒绝原假设。因此,如果p值低于0.05,则时间序列不稳定。defKPSS_test(timeseries):print("KPSS测试结果:")kpsstest=kpss(timeseries.dropna(),regression="c",nlags="auto")kpss_output=pd.Series(kpsstest[0:3],index=["TestStatistic","p-value","LagsUsed"])forkey,valueinkpsstest[3].items():kpss_output["CriticalValue(%s)"%key]=valueprint(kpss_output)KPSS_test(df)ResultsofKPSSTest:TestStatistic1.04843270p-value0.01000000LagsUsed37.00000000CriticalValue(10%)0.34700000CriticalValue(5%)0.46300000CriticalValue(2.5%)0.57CriticalValue(010CriticalValue)0.73900000dtype:float64虽然这些测试看起来是为了检查数据的平稳性,但这些测试对于分析时间序列的趋势而不是季节性很有用。统计结果也显示了时间序列平稳性的影响。而两个检验的零假设是相反的。ADF检验表明时间序列是平稳的(p值>0.05),而KPSS检验表明时间序列不是平稳的(p值>0.05)。但是这个数据集是用轻微的趋势创建的,所以事实证明KPSS测试对于分析这个数据集更准确。为了减少数据集的趋势,我们可以使用以下方法去除趋势:df_detrend=(df-df.rolling(window=365).mean())/df.rolling(window=365).std()analyze_stationarity(df_detrend['temp'].dropna(),'detrendeddata')ADF_test(df_detrend.dropna())检查季节性正如之前从滑动窗口中观察到的那样,我们的时间序列中存在季节性模式。因此,应使用差分方法去除时间序列中潜在的季节性或周期性模式。由于样本数据集有12个月的季节性,我使用了365个滞后差异:df_365lag=df-df.shift(365)analyze_stationarity(df_365lag['temp'].dropna(),'12lagdifferentiateddata')ADF_test(df_365lag.dropna())现在,滑动均值和标准差随时间或多或少保持不变,因此我们有一个平稳的时间序列。上述方法组合的代码如下:df_365lag_detrend=df_detrend-df_detrend.shift(365)analyze_stationarity(df_365lag_detrend['temp'].dropna(),'12lagdifferentiatedde-trendeddata')ADF_test(df_365lag_detrend.dropna())分解模式基于上述模式的分解可以使用'statmodels'包中的一个有用的Python函数seasonal_decomposition来实现:defseasonal_decompose(df):decomposition=sm.tsa.seasonal_decompose(df,model='additive',freq=365)trend=decomposition.trendseasonal=decomposition.seasonalresidual=decomposition.residfig=decomposition.plot()fig.set_size_inches(14,7)plt.show()returntrend,seasonal,residualseasonal_decompose(df)四节后,可以说我们的时间序列中有很强的年度季节性成分,以及随时间增加的趋势模式。时间序列建模时间序列数据的适当模型将取决于数据的特定特征,例如,数据集是否具有总体趋势或季节性。请务必选择最适合您的数据的模型。我们可以使用以下模型:向量自回归(VAR)向量自回归移动平均(VARMA)向量自回归移动平均与外生回归(VARMAX)简单指数平滑(SES)HoltWinter指数平滑(HWES)适用于具有趋势和/或季节性成分的时间序列数据。这种方法使用指数平滑对大量过去的值进行编码,并用它们来预测现在和未来的“典型”值。指数平滑是指使用指数加权移动平均值(EWMA)来“平滑”时间序列。在实施之前,让我们创建训练和测试数据集:y=df['temp'].astype(float)y_to_train=y[:'2017-12-31']y_to_val=y['2018-01-01':]predict_date=len(y)-len(y[:'2017-12-31'])以下是使用均方根误差(RMSE)作为评估模型误差的指标的实现。defholt_win_sea(y,y_to_train,y_to_test,seasonal_period,predict_date):fit1=ExponentialSmoothing(y_to_train,seasonal_periods=seasonal_period,trend='add',seasonal='add').fit(use_boxcox=True)fcast1=fit1.forecast(预测日期).rename('Additive')mse1=((fcast1-y_to_test.values)**2).mean()print('加性趋势的均方根误差,'+'期的加性季节season_length={}andBox-Cox变换{}'.format(seasonal_period,round(np.sqrt(mse1),2)))y.plot(marker='o',color='black',legend=True,figsize=(10,5))fit1.fittedvalues.plot(style='--',color='red',label='train')fcast1.plot(style='--',color='green',label='test')plt.ylabel('temp')plt.title('Additivetrendandseasonal')plt.legend()plt.show()holt_win_sea(y,y_to_train,y_to_val,365,predict_date)加法的均方根误差趋势,周期season_length=365和Box-Coxtr的附加季节性Ansformation6.27从图中我们可以观察到模型是如何捕捉时间序列的季节性和趋势的,异常值的预测存在一些误差。综上所述,在本文??中,我们通过一个基于温度数据集和季节的实际例子来介绍趋势。除了检查趋势和季节性之外,我们还看到了如何减少它并创建一个基本模型,该模型使用这些模式来推断未来几天的温度。了解主要的时间序列模式并学习如何实施时间序列预测模型至关重要,因为它们有很多应用。本文数据及代码:https://avoid.overfit.cn/post/51c2316b0237445fbb3dbf6228ea3a52作者:JavierFernandez
