做者:天琼,「数据游戏」优胜队伍成员html
本文整理记录了参与的一次小型数据分析竞赛「数据游戏」,竞赛目标是预测2019年5月15日A股闭市时招商银行600036的股价。算法
主要思路是利用ARIMA算法作时间序列预测。api
使用的数据是公开的数据集 tushare。安全
拿到题目和数据以后,首先结合既往经历,以为想要预测准股价,自己是一个不可能的事情,尤为是A股。微信
影响股价的因素很是复杂并且不透明,以及金融投资领域具备的反身性理论,使得此次预测更多偏向于实验性质,同时对竞争结果不要有太高的指望。异步
预测得准,是你的运气;预测的烂,也不会影响你从中学到什么。学习第一,比赛第二吧。函数
鉴于以上,本次预测只使用了close的时间序列。更多的数据其实并无什么用。post
初学的小伙伴们可能对这个过程并不熟悉,不要紧,先背下来。学习
对Python不熟悉的小伙伴们, 我给你们总结了几句车轱辘话,你们先强行了解下。测试
data = pd.read_excel(‘600036.xlsx’, index=None) train = data[‘close’]
adf_data = sts.adfuller(train) diff = train.diff(1)
sm.tsa.arma_order_select_ic(train, max_ar=8, max_ma=8, ic=[‘aic’, ‘bic’, ‘hqic’])
ARMAModel = sm.tsa.ARIMA(train, order=(4,1,2)).fit()
train_shift = train.shift(1) pred_recover = predicts.add(train_shift) np.sqrt( sum( (pred_recover -train) ** 2)/train.size )
f = ARMAModel.forecast(3)
以上,是本文的核心代码,你们若是一时看不懂,能够跳过。
能够看看下面更详细的步骤。
# 导入必须的模块 import tushare as ts #使用的公开的数据 import numpy as np import pandas as pd import matplotlib.pyplot as plt import statsmodels.api as sm import statsmodels.tsa.stattools as sts import wconfig as wc #自定义打印输出控制模块,与本次竞赛无关 wc.dispy() # 导入数据,从去年1月开始便可 data = ts.get_hist_data('600036', start='2018-01-08', end='2019-05-10').sort_index() #data = ts.get_hist_data('600036', start='2018-04-01').sort_index().reset_index() # 数据安全和源数据备份 #data.to_excel('600036.xlsx') # 源数据可靠性检查。12日发现tushare的数据有错误,须要手工矫正(13日该数据恢复正常)。 print('tushare中5月10日close股价为: %.2f元,与实际不符!' % (data['close']['2019-05-10'])) data['close']['2019-05-10']=33.61 print('当日实际收盘价应为: %.2f元' % data['close']['2019-05-10']) # 只取 close 字段做为训练数据 train = data['close'] train.index = pd.to_datetime(train.index) # 将字符串索引转换成时间索引 train.tail() train.tail().index
tushare中5月10日close股价为: 33.48元,与实际不符!
当日实际收盘价应为: 33.61元
ARIMA算法要求时间序列稳定,因此在建模以前,要先检验时间序列的稳定性。
adfuller就是用来干这个的。
adfuller 全称 Augmented Dickey–Fuller test, 即扩展迪基-福勒检验,用来测试平稳性。
def tagADF(t): result = pd.DataFrame(index=["Test Statistic Value", "p-value", "Lags Used", "Number of Observations Used", "Critical Value(1%)", "Critical Value(5%)", "Critical Value(10%)"], columns=['value'] ) result['value']['Test Statistic Value']=t[0] result['value']['p-value']=t[1] result['value']['Lags Used']=t[2] result['value']['Number of Observations Used'] = t[3] result['value']['Critical Value(1%)']=t[4]['1%'] result['value']['Critical Value(5%)']=t[4]['5%'] result['value']['Critical Value(10%)']=t[4]['10%'] print('t is:', t) return result
adfuller检验是检查时间序列平稳性的统计测试之一。 这里的零假设是:序列 train 是非平稳的。
测试结果包括测试统计和差别置信水平的一些关键值。 若是测试结果中的 P-value 小于临界值,咱们能够拒绝原假设并说该序列是平稳的。
adf_data = sts.adfuller(train) tagADF(adf_data)
检验结果显示,p-value=0.414, 远远大于5%的临界值,说明零假设是成立的,即序列 train 是非平稳的。
t is: (-1.7325346908056185, 0.4144323576685054, 0, 322, {‘1%’: -3.4508226600665037, ‘5%’: -2.870558121868621, ‘10%’: -2.571574731684734}, 523.9067372199033)
为了让时间序列平稳,须要对 train 序列作差分运算:
# df.diff 差分运算,默认是后一行减前一行 # http://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.diff.html?highlight=diff#pandas.DataFrame.diff diff = train.diff(1).dropna() # df.dropna 删除有缺失值的行 plt.figure(figsize=(11,6)) # 指定显示大小 plt.plot(diff, label='Diff') # 绘制趋势图 plt.legend(loc=0) # 显示图例,loc指定图例位置,0为最佳位置。
关于时间序列稳定性的判断标准,可参考这篇博客:https://blog.csdn.net/u012735...
检验差分后数据的平稳性,和第一次验证方法相同
adf_Data1 = sts.adfuller(diff) tagADF(adf_Data1) # p-value很小,零假设不成立,所以,diff数据序列符合平稳性要求。
在这里,先简单解释下本身对 ARIMA 算法的理解。不正确的地方请你们指点。
ARIMA算法认为时间序列上 t 时刻的值由2部分构成,第一部分是由以前p项历史值决定的,好比15日的收盘价是12日,13日,14日的收盘价的线性回归,用AR§表示。
可是这个线性回归的输出值确定和15日的实际收盘价有一个偏差,假设是e。
因此第二部分就是若是表示这个 e 。
这个e 能够认为是以前q项偏差的线性回归,用MA(q)表示。
两部分合起来就是ARMA。
而后为了让时间序列平稳,再加个d 阶差分操做功能,就变成了ARIMA算法。
因此在使用 ARIMA 算法以前,须要先肯定(p, d, q)的值。
肯定ARIMA的阶数p,q # ARMA(p,q)是AR(p)和MA(q)模型的组合,关于p和q的选择,一种方法是观察自相关图ACF和偏相关图PACF, # 另外一种方法是经过借助AIC、BIC等统计量自动肯定。 ic = sm.tsa.arma_order_select_ic( train, max_ar=8, max_ma=8, ic=['aic', 'bic', 'hqic'] ) ic
注意ARIMA的参数中,输入数据 应该是原始数据 train,ARIMA 会根据 d 的值,对原始数据作 d 阶差分运算。
d的含义是,输入序列须要先通过一个d阶的差分,变成一个平稳序列后才能进行数据拟合。
ARMAModel = sm.tsa.ARIMA(train, order=(4,1,2)).fit() # order=(p,d,q) # fittedvalues和diff对比 plt.figure(figsize=(11, 6)) plt.plot(diff, 'r', label='Orig') plt.plot(ARMAModel.fittedvalues, 'g',label='ARMA Model') plt.legend()
# 样本内预测 predicts = ARMAModel.predict() # 由于预测数据是根据差分值算的,因此要对它一阶差分还原 train_shift = train.shift(1) # shift是指series日后平移1个时刻 pred_recover = predicts.add(train_shift).dropna() #这里add是指两列相加,按index对齐 # 模型评价指标 1:计算 score delta = ARMAModel.fittedvalues - diff score = 1 - delta.var()/train.var() print('score:\n', score) # 模型评价指标 2:使用均方根偏差(RMSE)来评估模型样本内拟合的好坏。 #利用该准则进行判别时,须要剔除“非预测”数据的影响。 train_vs = train[pred_recover.index] # 过滤没有预测的记录 plt.figure(figsize=(11, 6)) train_vs.plot(label='Original') pred_recover.plot(label='Predict') plt.legend(loc='best') plt.title('RMSE: %.4f'% np.sqrt(sum((pred_recover-train_vs)**2)/train_vs.size)) plt.show()
# 局部数据观察 train_t = train_vs.tail(15) pred_t = pred_recover.tail(15) plt.figure(figsize=(11, 6)) train_t.plot(label='Original') pred_t.plot(label='Predict') plt.legend(loc='best') plt.title('RMSE: %.4f'% np.sqrt(sum((pred_t-train_t)**2)/train_t.size)) plt.show()
使用 forecast 对样本外的时间序列进行预测。
关于foreast和predict的区别:
predict 能够对样本内和样本外的进行预测,结果是同样的。
举例说明:forecast(10),表示对将来10个点进行预测,可是能够用model.fittedvalues查看样本内点的拟合值;
而predict(start,end)里面的参数0表示样本内的第一个数,以此类推。
若是想要预测样本外的数,须要将start设置为len(data)+1,即数据长度+1,才表示预测样本外的第一个数字。
而 forecast函数,是对样本外的数据进行预测。
可是这两个函数的预测结果是同样的。
另外,须要提到的是,ARIMA算法通常只能预测一点点,越长越不许确,即使是简单的正弦函数也不能准确预测。
# 预测15日close股价,即10日以后的第三个交易日的收盘价 # 可是经过上面的局部数据观察发现,预测的数据趋势会延迟1个交易日,因此就取f[0][1] f = ARMAModel.forecast(3) # 样本外预测 print('5月15日close时的股价为:%.2f 元' % f[0][1]) #----结束-----
5月15日close时的股价为:33.82 元。
顺便安利下此次参与的小竞赛「数据游戏」,是由我的发起、异步社区(邮电出版社)赞助的活动。
整体来讲,这个比赛氛围适中,没有太大的心理负担。
因此感受比较适合打算初入的小伙伴,参与以后,这样的经验对之后的学习进阶和职业发展都有好处。
学习更多数据科学知识请关注微信公众号:read_csv
参与数据科学活动请加 QQ 群:759677734