前言

由于课程大作业需要,故做此文章,顺便记录过程

思路

首先作为一个预测,必须要先找到数据。我觉得最难的也就是这一步,最开始想的是想用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计算公式如下
a计算公式
b计算公式如下
b计算公式
c计算公式如下

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行就能实现能改变预测时间,预测数据的指数平滑预测法

结语

指数平滑预测法比较复杂,手算的话很难计算结果,如果单纯面向过程计算也略显复杂,这次突发奇想不仅加深了我对面向对象的理解,还掌握了一个预测方法,一举两得。