为了解决特定问题而进行的学习是提高效率的最佳途径。这种方法能够使我们专注于最相关的知识和技能,从而更快地掌握解决问题所需的能力。
(以下练习题来源于《统计学—基于Python》。请在Q群455547227下载原始数据。)
练习题
下表是某只股票连续35个交易日的收盘价格(前3行和后3行)。
(1)分别采用m=5和m=10对收盘价格进行平滑,并绘制实际值和平滑值的图形进行比较。
(2)分别采用以下方法进行预测,并绘制预测图和残差图,对结果进行比较。
- (a)简单指数平滑和Holt指数平滑;
- (b)一元线性回归和指数曲线;
- (c)二阶曲线和三阶曲线。
图形绘制与分析
鉴于篇幅原因,本文先就(1)题及(2a)题展开分析。
(1)计算m=5(5期移动平均)和m=10(10期移动平均)的移动平均
# 移动平均
import pandas as pd
df = pd.read_csv('exercise11_1.csv')
# df.head()
ma5 = df['收盘价'].rolling(window = 5, center = True).mean() # 5期移动平均
ma10 = df['收盘价'].rolling(window = 10, center = True).mean() # 10期移动平均
df_ma = pd.DataFrame({'时间':df['时间'], '收盘价':df['收盘价'], '5期移动平均':ma5, '10期移动平均':ma10})
round(df_ma,2)
显示前20行:
绘制实际值和平滑值的折线图
# 绘制实际值和平滑值的折线图
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['Songti SC']
plt.rcParams['axes.unicode_minus'] = Falseplt.figure(figsize = (8, 5.5))
l1, = plt.plot(df_ma['收盘价'], linestyle = '-', marker = 'o', linewidth = 0.8)
l2, = plt.plot(df_ma['5期移动平均'], linestyle = '-', marker = '+', linewidth = 0.8)
l3, = plt.plot(df_ma['10期移动平均'], linestyle = '-', marker = '*', linewidth = 0.8)
plt.xticks(range(0, 36, 2), df['时间'][::2])
plt.legend(handles = [l1, l2, l3], labels = ['收盘价','5期移动平均', '10期移动平均'], loc = 'best', prop = {'size':10})
plt.xlabel('时间', size = 12)
plt.ylabel('收盘价', size = 12)
分析:移动间隔越长,曲线就越平滑。在实际应用中,可根据数据的波动情况和分析目的合理选择移动间隔的长度。当数据量较大时,移动间隔可长一些。但如果数据是以固定长度的周期采集的,则移动间隔的长度最好与数据的采集周期一致,这样可以有效去除序列中的随机波动。比如,如果数据是按季节采集的,则移动间隔的长度应取4;如果数据是按月采集的,则移动间隔的长度应取12。
# 简单指数平滑
import pandas as pd
import matplotlib.pyplot as plt
from statsmodels.tsa.holtwinters import SimpleExpSmoothing
plt.rcParams['font.sans-serif'] = ['Songti SC']
plt.rcParams['axes.unicode_minus'] = False
df = pd.read_csv('exercise11_1.csv')# df.index = pd.date_range(start = '1', end = '35', freq = 'AS')# 拟合简单指数平滑模型(alpha = 0.3)
model = SimpleExpSmoothing(df['收盘价']).fit(smoothing_level = 0.3, optimized = True)
model.params # 输出模型参数
# 绘制实际值和拟合值图
df['price_ses'] = model.fittedvalues
plt.figure(figsize = (7, 4.5))
l1, = plt.plot(df['收盘价'], linestyle = '-', marker = 'o', linewidth = 0.8)
l2, = plt.plot(df['price_ses'], linestyle = '--', marker = '*', linewidth = 0.8)
plt.legend(handles = [l1, l2], labels = ['收盘价', '拟合值'], loc = 'best', prop = {'size': 10})
plt.xlabel('时间', size = 12)
plt.ylabel('收盘价', size = 12)
# 计算第36期的预测值
p_model = model.forecast(1) # 向后预测1期
p_model
计算结果:
绘制预测图和残差图
# 绘制预测图和残差图
import scipy
df = pd.read_csv('exercise11_1.csv')# 图(a)预测图
plt.subplots(1, 2, figsize = (11, 4))
plt.subplot(121)ax = df['收盘价'].plot(marker = 'o', linewidth = 0.8, color = 'black') # 绘制实际值
ax.set_ylabel('收盘价', size = 12)
ax.set_xlabel('时间', size = 12)
model.forecast(1).plot(ax = ax, style = '--', marker = 'o', color = 'red') # 绘制预测值# 计算置信区间并绘图
simulations = model.simulate(nsimulations = 2, repetitions = 1000, error = 'add', random_errors = scipy.stats.norm) # 重复模拟100次,模拟步长为2
random_errors = 'bootstrap'
low_CI_95 = p_model-1.96*simulations.std(axis = 1)
high_CI_95 = p_model+1.96*simulations.std(axis = 1)
low_CI_80 = p_model-1.28*simulations.std(axis = 1)
high_CI_80 = p_model+1.28*simulations.std(axis = 1)plt.fill_between([36], low_CI_95, high_CI_95, alpha = 0.3, color = 'grey', linewidth = 20)
plt.fill_between([36], low_CI_80, high_CI_80, alpha = 0.3, color = 'blue', linewidth = 20)
plt.xlim(-1, 36)
plt.title('(a)收盘价的简单指数平滑预测', size = 13)# 图(b)残差图
res = model.resid
plt.subplot(122)
plt.scatter(range(len(res)), res, marker = 'o')
plt.hlines(0, 0, 35, linestyle = '--', color = 'red')
plt.xticks(range(0, 35, 2), df['时间'][::2])
plt.xlabel('时间', size = 12)
plt.ylabel('残差', size = 12)
plt.title('(b)简单指数平滑预测残差', size = 13)plt.tight_layout()
分析:左图中的折线图是收盘价的实际值,红色圆点是第36期的预测值,蓝色和灰色区域是置信区间,其中的灰色区域是95%的置信区间,蓝色是区域是80%的置信区间。 右图的残差图显示,残差虽然围绕0轴波动,但是呈现出U型的形态,表明采用简单指数平滑预测模型可能是不合适的。
(2a)题的第二问要求进行Holt指数平滑预测
Holt指数平滑预测是以其提出者C.C.Holt 的名字命名的,通常简称 Holt 模型。当时间序列存在趋势成分时,简单指数平滑的预测值总是滞后于实际值。而Holt模型则改进了简单指数平滑模型,它将趋势成分也考虑造来,用平滑值对序列的线性趋势进行修正,建立线性平滑模型进行预测。
先建立模型并输出相关参数:
import pandas as pd
import matplotlib.pyplot as plt
from statsmodels.tsa.holtwinters import SimpleExpSmoothing, ExponentialSmoothing, Holt
plt.rcParams['font.sans-serif'] = ['Songti SC']
plt.rcParams['axes.unicode_minus'] = Falsedf = pd.read_csv('exercise11_1.csv')# 拟合Holt指数平滑模型(model_h)
model_h = Holt(df['收盘价']).fit(optimized = True)
model_h.params # 输出模型系数
注:smoothing_level为系统确定的平滑指数α(反映随机成分);smoothing_trend为系统确定的平滑系数β(反映趋势成分);initial_level为初始平滑值;initial_trend为初始趋势值。
绘制实际值和拟合值图
# 绘制实际值和拟合值图
df['收盘价_holt'] = model_h.fittedvalues
plt.figure(figsize = (7, 4.5))
l1, = plt.plot(df['收盘价'], linestyle = '-', marker = 'o', linewidth = 1) # 实际值
l2, = plt.plot(df['收盘价_holt'], linestyle = '--', marker = '^', linewidth = 1) # 拟合值
plt.legend(handles = [l1, l2], labels = ['收盘价', '拟合值'], loc = 'best', prop = {'size': 10})
plt.xlabel('时间', size = 12)
plt.ylabel('收盘价', size = 12)
plt.title('收盘价的Holt指数平滑拟合', size = 13)
上图展示了收盘价的Holt指数平滑的拟合效果,从拟合值和实际值的接近程度来看,预测模型比较理想。
# 计算第36期的预测值
model_h.forecast(1)
计算结果:
可以发现其计算结果与简单指数平滑预测是不同的。
接下来绘制预测图和残差图:
# 绘制预测图和残差图
import scipy
df = pd.read_csv('exercise11_1.csv')# 图(a)预测图
plt.subplots(1, 2, figsize = (11, 4))
plt.subplot(121)ax = df['收盘价'].plot(marker = 'o', linewidth = 0.8, color = 'black') # 绘制实际值
ax.set_ylabel('收盘价', size = 12)
ax.set_xlabel('时间', size = 12)
model_h.forecast(1).plot(ax = ax, style = '--', marker = 'o', color = 'red') # 绘制预测值# 计算置信区间并绘图
simulations = model_h.simulate(nsimulations = 2, repetitions = 1000, error = 'add', random_errors = scipy.stats.norm) # 重复模拟100次,模拟步长为2
#random_errors = 'bootstrap'
low_CI_95 = model_h.forecast(1)-1.96*simulations.std(axis = 1)
high_CI_95 = model_h.forecast(1)+1.96*simulations.std(axis = 1)
low_CI_80 = model_h.forecast(1)-1.28*simulations.std(axis = 1)
high_CI_80 = model_h.forecast(1)+1.28*simulations.std(axis = 1)plt.fill_between([36], low_CI_95, high_CI_95, alpha = 0.3, color = 'grey', linewidth = 20)
plt.fill_between([36], low_CI_80, high_CI_80, alpha = 0.3, color = 'blue', linewidth = 20)
plt.xlim(-1, 36)
plt.title('(a)收盘价的Holt指数平滑预测', size = 13)# 图(b)残差图
plt.subplot(122)
res = model_h.resid
plt.scatter(range(len(res)), res, marker = 'o')
plt.hlines(0, 0, 35, linestyle = '--', color = 'red')
plt.xticks(range(0, 35, 2), df['时间'][::2])
plt.xlabel('时间', size = 12)
plt.ylabel('残差', size = 12)
plt.title('(b)Holt指数平滑预测残差', size = 13)plt.tight_layout()
分析:左图中的折线是收盘价的实际值,红色圆点是第36期的预测值,灰色和蓝色区域是置信区间,其中灰色区域是95%的置信区间,蓝色区域是80%的置信区间。 右图的残差图显示,残差围绕0轴随机波动,无固定的模型,表明采用Holt指数平滑预测模型是合适的。
都读到这里了,不妨关注、点赞一下吧!