python--时间序列预测(time series prediction)

题外话:前段时间做了一点时间序列预测,积累了一点经验,写出来与大家分享一下。能力有限,若是有错误,请指正。本文理论内容不会特别多。

1.时间序列预测

时间序列预测,主要就是依靠过去和现在的数据,分析两者之间的关系,然后利用得到的这个关系去预测未来的数据。现在主要运用在股票和人口等的预测上。个人觉得时间序列预测与其他预测不同的,通常时间序列预测只有1维数据,所以很多机器学习方法不能直接使用。

2.时间序列预测模型

下面来说一下我做时间序列预测时用到的模型,首先是很简单的灰色预测模型,之后是非常普遍的ARIMA系列模型。

2.1灰色模型

灰色预测模型(Gray Model),这种模型的思想很简单,常用来对数据进行预测,在时间序列预测上的应用不是特别多。关于这个模型具体可以参考参考:https://blog.csdn.net/qq547276542/article/details/77865341

关于这个模型,我想说明的只有一点,就是这个模型的预测结果,无论是对训练集的拟合结果,还是预测的结果,除了第一组数据能够准确预测意外,其余的预测数据都呈单调递增或递减趋势。这点可以用理论证明:

由上述内容可以知道,灰色模型的预测值计算表达式为:

$$x^{(0)}(k+1)=x^{(1)}(k+1)-x^{(1)}(k)$$

$$=[(x^{(0)}(1)-\frac{b}{a}){\rm e}^{-ak}+\frac{b}{a}]-[(x^{(0)}(1)-\frac{b}{a}){\rm e}^{-a(k-1)}+\frac{b}{a}]$$

$$=(x^{(0)}(1)-\frac{b}{a})(1-{\rm e}^{a})({\rm e}^{-ak})$$

为了方便我把上式改写成了常见的方程形式如下:

$$y=(m-\frac{b}{a})(1-{\rm e}^{a}){\rm e}^{-ax}$$

对上式求一阶导数得到:

$$y^{'}=-a(m-\frac{b}{a})(1-{\rm e }^a) {\rm e }^{-ax}$$

简单的分析就可以知道,上式的符号不会发生变化,所以可以得到上面递增递减的结论。

2.2  ARMA、ARIMA模型

ARMA模型全程为自动回归积分滑动平均模型(Autoregressive Integrated Moving Average Model),网上对这个模型的介绍以及使用介绍的都非常多了,这里就省略了。因为刚开始做ARIMA实验的时候,对这个模型不太了解,所以我的实验参考了这篇博客。https://www.cnblogs.com/foley/p/5582358.html

在具体的实验里,我们采用的拟合方式是把时间序列划分成3个序列(trend,seasonal,residual),然后对其中的各个部分分别进行拟合。但我们在实验中发现seasonal序列是一个有规律的序列(如下图,从图中可以看出从1949-01-01到1949-12-01所对应的数据与195-01-01到1950-12-01的数据一一对应,这12个数据重复出现。),所以对这个序列并没有进行拟合而直接使用。

对剩余的两部分分别拟合后。在把拟合后得到的三个序列相加就可以得到最终的拟合结果。并使用最终的模型预测未来5个月的数据。具体代码如下:

from statsmodels.tsa.arima_model import ARMA
import statsmodels as sm
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
from statsmodels.tsa.seasonal import seasonal_decompose
from statsmodels.tsa.stattools import adfuller

if __name__ == '__main__':
    mpl.rcParams['font.sans-serif'] = 'SimHei'
    mpl.rcParams['axes.unicode_minus'] = False
    data=pd.read_csv('AirPassengers.csv',header=0,names=['date','peo_num'])
    data = pd.Series(data["peo_num"].values, \
                           index=pd.DatetimeIndex(data["date"].values, freq='MS'))
    decomposition=seasonal_decompose(data,model='additive')
    trend=decomposition.trend
    seasonal=decomposition.seasonal
    residual=decomposition.resid
    #对三部分分别进行拟合
    trend.dropna(inplace=True)
    trend_diff=trend.diff(periods=2)
    trend_diff.dropna(inplace=True)
    #分别对三部分进行拟合z
    #order_trend=sm.tsa.stattools.arma_order_select_ic(trend_diff)['bic_min_order']
    order_trend=(3,2)
    model_trend=ARMA(trend_diff,order_trend)
    result_trend=model_trend.fit()
    predict_trend=result_trend.predict()+trend.shift(2)
    forecast_trend,_,_=result_trend.forecast(11)
    forecast_trend=pd.Series(forecast_trend,\
                             index=pd.DatetimeIndex(start='1960-07-01',end='1961-05-01',freq='MS'))
    trend_predict=pd.concat([predict_trend,forecast_trend],axis=0)
    for i in range(11,0,-1):
        trend_predict.values[-i]+=trend_predict[-i-2]
    value_seasonal=[]
    for i in range(5):
        value_seasonal.append(seasonal.values[i])
    forecast_seasonal=pd.Series(value_seasonal,\
                                index=pd.DatetimeIndex(start='1961-01-01',end='1961-05-01',freq='MS'))
    seasonal_predict=pd.concat([seasonal,forecast_seasonal],axis=0)

    residual.dropna(inplace=True)
    order_residual=sm.tsa.stattools.arma_order_select_ic(residual)['bic_min_order']
    model_residual=ARMA(residual,order_residual)
    result_residual=model_residual.fit()
    predict_residual=result_residual.predict()
    forecast_residual,_,_=result_residual.forecast(11)
    forecast_residual=pd.Series(forecast_residual,\
                                index=pd.DatetimeIndex(start='1960-07-01',end='1961-05-01',freq='MS'))
    residual_predict=pd.concat([predict_residual,forecast_residual],axis=0)
    test_data=trend_predict+seasonal_predict+residual_predict
    print(test_data)
    data.plot(label='train_data',legend=True)
    test_data.plot(label='forecast_data',legend=True)
    plt.show()

关于代码中的几点需要做如下说明:

首先:在使用ARMA模型做预测的时候,从最开始的做差分,再到训练模型时选定的AR、MR阶数都导致了最终模型得到了训练数据的缺失。AirPassenger中的数据日期是从1949-01-01到1960-12-01,而在trend部分拟合时,result_trend.predict()输出的数据是从1949-07-01到1960-06-01,所以在trend部分预测时,不知预测了从1961-01-01到1961-05-01这部分的数据,还补充预测了1960-07-01到1960-12-01这部分的数据。

其次,我们投入到ARMA模型的数据是做了差分运算的数据,并且result_trend.predict()和result_trend.forecast()给出的数据也都是差分的数据。所以我们要对得到的数据进行还原。

接着,代码中并没有给出判断序列是否已经是平稳序列的代码,这部分可以参考上述博客。

最后,在上述代码中,确定ARMA的阶数时使用了python中已经写好的函数arma_order_select_ic,在这个函数运行时可能会抛出一些异常,如下:

目前这些异常是因为阶数选择不合适而导致的。在这个实验中,我们选择的是不会引起这些异常阶数对,所以在trend部分拟合是,我们没有选择arma_order_select_ic函数给出的‘bic_min_order’(在实验中为(2,2)),而选择了(3,2)

最终预测结果图如下。从图片显示的结果来看,预测的效果还不错。

最后要说明的时,由于ARMA模型的预测结果收敛速度很快(跟ARMA的阶数有关),所以这里只预测了5个月。预测的月份越多,结果越不准确。

猜你喜欢

转载自blog.csdn.net/yeshang_lady/article/details/80888963
今日推荐