前言
由于课程大作业需要,故做此文章,顺便记录过程
思路
首先作为一个预测,必须要先找到数据。我觉得最难的也就是这一步,最开始想的是想用Intel的年度财报来进行预测,但是没想到百度了半天也没能找到数据来源。无奈之下只能随便找了一个数据源
实现过程
数据来源
本文章数据来源于产业信息网
算法学习
本次预测使用的算法为指数平滑预测法,指数平滑法的特点就是数据复用,并且数据关联性强,在这里给出公式,这里直接引用老师的ppt
一次指数平滑

二次指数平滑

三次指数平滑

我的理解
这里给出公式比较难以理解,这里用我的理解说一次
先给定一排数据,如下
年份 |
X1 |
S1 |
S2 |
0 |
|
0.78 |
0.78 |
1 |
0.78 |
0.78 |
0.78 |
2 |
0.89 |
S11 |
S12 |
如果要求S11,就把S11左边的0.89作为data1,上面的数据0.78作为data2,data1对应第一张图中的Xt,data2对应第一张图中的St,然后代入公式,取α=0.3,就有S11 = 0.3 * 0.89 + (1 - 0.3) * 0.78
同理S12也是S12 = 0.3 * data1 + (1 - 0.3) * data2
代码实现
知道了原理以后,很快就可以得出一个思路转换成代码,我这里使用python,面向对象的设计,最大程度的实现代码复用
首先创建一个main.py作为主程序运行,然后同文件夹下GetData.py作为图将结果输出,还有个counter.py作为算法主核心计算
GetData.py中的内容
这里使用了经典两件套numpy + matplotlib.pyplot画图,由于这里主要介绍算法实现部分,这部分直接略过
import matplotlib.pyplot as plt import numpy as np
class GetData(): def __init__(self): self.Amount = [0.78, 0.89, 1.39, 1.00, 1.16, 1.58] # 定义获取到的数据 self.year = [2016, 2017, 2018, 2019, 2020, 2021] # 定义年份 count = 0 Amount = [0.78, 0.89, 1.39, 1.00, 1.16, 1.58] year = [2016, 2017, 2018, 2019, 2020, 2021] def run(self): self.count += 1 plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False
x = np.arange(len(self.Amount)) # 索引列表
plt.bar(x, self.Amount, width=0.5)
plt.title('2016-2021年中国制造半导体器件或集成电路用的机器及装置进口情况统计图 ', fontsize=10) plt.xlabel('年份', fontsize=14) plt.ylabel('数量/万台', fontsize=14) plt.xticks(x, self.year)
plt.tick_params(axis='both', labelsize=7, color='red') plt.show()
|
counter.py中的内容
重量级来了,刚才介绍到左边作为data1,上面作为data2然后套公式可以直接返回平滑结果,那么这里就先写一个数组
X = self.data S1 = [] S2 = [] S3 = [] # 添加0数据 # 将三个数组结合方便遍历计算 for i in range(len(self.data)): if i == 0: S1.append(self.data[0]) S2.append(self.data[0]) S3.append(self.data[0]) continue S1.append(0) S2.append(0) S3.append(0) # 设第一行数据为X1[0] # a取0.3 TotalS = [X,S1,S2,S3]
|
在脑子里把他拼凑成一个表格,TotalS就是表格
然后回想一下公式
# S1[t] = a * X[t] + (1 - a) * S1[t - 1] # 定义S1公式 # S2[t] = a * S1[t] + (1 - a) * S2[t - 1] # 定义S2公式 # S3[t] = a * S2[t] + (1 - a) * S3[t - 1] # 定义S3公式
|
很快就能想出来程序应该怎么写,写个遍历循环+函数即可
for i in range(1,len(self.data)): for j in range(3): TotalS[j + 1][i] = self.getS(TotalS[j][i],TotalS[j][i - 1]) # j 对应S1,S2,S3;i对应行数,照着公式画瓢取数组即可
|
getS函数内容如下
def getS(self,data1,data2): # 左边的数据为data1,上边的数据为data2 return self.a * data1 + (1 - self.a) * data2
|
这里就最后得到了四个数组,计算出了所有结果
接下来进行预测

如果要求下一年的预测值,就先把a,b,c算出来
a计算公式如下

b计算公式如下

c计算公式如下

也没什么好说的,主要就是这个St的取值,这里应该取每个平滑最后一个S值,即S1[-1],S2[-1],S3[-1]
贴代码
a1 = 3 * S1[-1] - 3 * S2[-1] + S3[-1] b = a / (2 * (1 - a)**2) * ((6 - 5 * a)* S1[-1] - 2 * (5 - 4 * a) * S2[-1] + (4 - 3 * a) * S3[-1]) c = a / (2 * (1 - a)**2) * (S1[-1] - 2 * S2[-1] + S3[-1])
|
这样就可以算出Y
Y = a1 + b * T + c * T **2 # T为年份,即 Y = a1 + b * 1 + c * 1 **2
|
然后为了代码复用,将它加入X数组里,之前我是对数组浅拷贝
这里直接引用X就是引用self.data
最后输出一下
print("{}年的预测值为:{:.2f}".format(self.year,Y)) # print(X)
|
main.py中的内容
from counter import counter from GetData import GetData
if __name__ == "__main__": MyData = GetData() Acounter = counter() Acounter.data = MyData.Amount # 预测年数只需要改变循环次数,然后输出图像 Acounter.times = 5 # 只需要改变这个次数就能改变预测年数 for i in range(Acounter.times): Acounter.year = MyData.year[-1] + 1 Acounter.run() MyData.year.append(MyData.year[-1] + 1) # MyData.Amount = Acounter.data MyData.run()
|
效果


ok,至此短短100行就能实现能改变预测时间,预测数据的指数平滑预测法
结语
指数平滑预测法比较复杂,手算的话很难计算结果,如果单纯面向过程计算也略显复杂,这次突发奇想不仅加深了我对面向对象的理解,还掌握了一个预测方法,一举两得。