前言
由于课程大作业需要,故做此文章,顺便记录过程
思路
首先作为一个预测,必须要先找到数据。我觉得最难的也就是这一步,最开始想的是想用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
这里直接引用X就是引用self.data
X.append(Y)
最后输出一下
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行就能实现能改变预测时间,预测数据的指数平滑预测法
结语
指数平滑预测法比较复杂,手算的话很难计算结果,如果单纯面向过程计算也略显复杂,这次突发奇想不仅加深了我对面向对象的理解,还掌握了一个预测方法,一举两得。