模型学习笔记03-灰色关联分析与预测模型

灰色预测是一种对含有不确定因素的系统进行预测的方法。灰色预测通过鉴别系统因素之间发展趋势的相异程度,即进行关联分析,并对原始数据进行生成处理来寻找系统变动的规律,生成有较强规律性的数据序列,然后建立相应的微分方程模型,从而预测事物未来发展趋势的状况。其用等时距观测到的反映预测对象特征的一系列数量值构造灰色预测模型,预测未来某一时刻的特征量,或达到某一特征量的时间。

灰色系统的应用范畴大致分为以下几方面:

(1)灰色关联分析。

(2)灰色预测:人口预测;灾变预测……等等。

(3)灰色决策。

(4)灰色预测控制。

灰色系统理论是人们认识客观系统改造客观系统的一个新型的理论工具。

灰色预测的概念

灰色系统,白色系统与黑色系统、

白色系统是指一个系统的内部特征是完全已知的,即系统的信息是完全充分的。

黑色系统是指一个系统的内部信息对外界来说是一无所知的,只能通过它与外界的联系来加以观测研究。

灰色系统内的一部分信息是已知的,另一部分信息是未知的,系统内各因素间有不确定的关系。

灰色预测法

灰色预测法是一种对含有不确定因素的系统进行预测的方法。

灰色预测是对既含有已知信息又含有不确定信息的系统进行预则,就是对在一定范围内变化的、与时间有关的灰色过程进行预测。

灰色预测通过鉴别系统因素之间发展趋势的相异程度,即进行关联分析,并可对原始数据进行生成处理来寻找系统变动的规律,生成有较强规律性的数据序列,然后建立相应的微分方程模型,从而预测事物未来发展趋势的状况。

灰色预测法用等时距观测到的反映预测对象特征的一系列数量值构造灰色预测模型,预测未来某一时刻的特征量,或达到某一特征量的时间。

灰色预测的四种常见类型

灰色时间序列预测

即用观察到的反映预测对象特征的时间序列来构造灰色预测模型,预测未来某一时刻的特征量,或达到某一特征量的时间。

畸变预测

即通过灰色模型预测异常值出现的时刻,预测异常值什么时候出现在特定时区内。

系统预测

通过对系统行为特征指标建立一组相互关联的灰色预测模型,预测系统中众多变量间的相互协调关系的变化。

拓扑预测

将原始数据做曲线,在曲线上按定值寻找该定值发生的所有时点,并以该定值为框架构成时点数列,然后建立模型预测该定值所发生的时点。

灰色关联度与优势分析

大千世界里的客观事物往往现象复杂,因素繁多。我们经常要对系统进行因素分析,这些因素中哪些对系统来讲是主要的,哪些是次要的,哪些需要发展,哪些需要抑制,哪些是潜在的,哪些是明显的。一般来讲,这些都是我们极为关心的问题。事实上,因素间关联性如何、关联程度如何量化等问题是系统分析的关键。

例如人们关心的人口问题构成一个系统,影响人口发展变化的因素有社会方面的诸如计划生育、社会治安、社会生活方式等;有经济方面的诸如国民收入、社会福利、社会保险等;还有医疗方面的诸如医疗条件、医疗水平等…..也就是说,人口是多种因素互相关联、互相制约的系统,对这些因素进行分析将有助于人们对人口的未来预测及人口控制工作。

因素分析的基本方法过去主要是采用回归分析等办法,但回归分析的办法有很多欠缺,如要求大量数据、计算量大以及可能出现反常情况等。为克服以上弊病,本节采用灰色关联度分析的办法来做系统分析。

灰色关联度一定是分析向量与向量之间以及矩阵与矩阵之间的关联度。既然计算关联度,一定是计算某一个待比较的数列与参照物(参考数列)之间的相关程度。

灰色关联度

选取参考数列

X_0=\{X_0(k)|k=1,2,...,n\}=(X_0(1),X_0(2),...,X_0(n))

其中k表示时刻

假设有m个比较数列

X_i=\{X_i(k)|k=1,2,...,n\}=(X_i(1),X_i(2),...,X_i(n)) \quad (i=1,2,...,m)

则称

ζ_i(k)=\frac{\min_{i}\min_{k}|X_0(k)-X_i(k)|+ρ\max_{i}\max_{k}|X_0(k)-X_i(k)|}{|X_0(k)-X_i(k)|+ρ\max_{i}\max_{k}|X_0(k)-X_i(k)|} \qquad (1)

为比较数列X_i对参考数列X_0在k时刻的关联系数,其中:

ρ∈[0,+∞)为分辨系数。一般来讲,分辨系数ρ∈[0,1],由公式(1)容易看出,ρ越大,分辨率越大,ρ越小,分辨率越小,在一般情况下,我们取ρ=0.5

|X_0(k)-X_i(k)|X_0X_i之间的绝对误差

\min_{i}\min_{k}|X_0(k)-X_i(k)|两级最小差

\max_{i}\max_{k}|X_0(k)-X_i(k)|两级最大差

公式(1)定义的关联系数时描述比较数列与参考数列在某时刻关联程度的一种指标,由于各个时刻都有一个关联系数,因此信息显得过于分散,不便于比较,为此我们给出

r_i=\frac{1}{n}\sum^n_{k=1}ζ_i(k) \qquad (2)

为比较数列X_i对参考数列X_0关联度

应该指出的是,公式(1)中的|X_0(k)-X_i(k)|不能区别因素关联是正关联还是负关联,可采取下面办法解决这个问题。记

δ_i=\sum^n_{k=1}kX_i(k)-\sum^n_{k=1}X_i(k)\sum^n_{k=1}\frac{k}{n}\\
δ_n=\sum^n_{k=1}k^2-\sum^n_{k=1}\frac{2}{n}

sign(\frac{δ_i}{δ_n})=sign(\frac{δ_j}{δ_n}),则X_iX_j正关联

sign(\frac{δ_i}{δ_n})=-sign(\frac{δ_j}{δ_n}),则X_iX_j负关联

案例分析

利用灰色关联分析对六位教师工作状况

1.分析指标包括:专业素质、外语水平、教学工作量、科研成果、论文、著作与出勤。
2.对原始数据经处理后得到以下数值,见下表:

编号专业外语教学量科研论文著作出勤
18987529
27875738
39796647
46888436
58669838
68957648

利用灰色关联分析对六位教师工作状况进行综合分析

import pandas as pd
import numpy as np

#导入数据
data=pd.read_excel(r'data.xlsx',index_col=0,sheet_name='灰色预测1')
x=np.array(data)
print(x)
[[8 9 8 7 5 2 9]
 [7 8 7 5 7 3 8]
 [9 7 9 6 6 4 7]
 [6 8 8 8 4 3 6]
 [8 6 6 9 8 3 8]
 [8 9 5 7 6 4 8]]

3.确定参考数据列

\{x_0\}=\{9,9,9,9,8,9,9\}

4.计算绝对误差|X_0(k)-X_i(k)|

#重复代码已省略

#确定参考数列
x_0=np.array([9,9,9,9,8,9,9])

#计算绝对误差
abs_err=abs(x-x_0)
print(abs_err)
[[1 0 1 2 3 7 0]
 [2 1 2 4 1 6 1]
 [0 2 0 3 2 5 2]
 [3 1 1 1 4 6 3]
 [1 3 3 0 0 6 1]
 [1 0 4 2 2 5 1]]

5.求最值

\min^n_{i=1}\min^m_{k=1}|x_0(k)-x_i(k)|=\min(0,1,0,1,0,0)=0\\
\max^n_{i=1}\max^m_{k=1}|x_0(k)-x_i(k)|=\max(7,6,5,6,6,5)=7
#重复代码已省略

#计算两级最小差和两级最大差
min_err_list=[]
max_err_list=[]
for row in abs_err:
    min_err_list.append(min(row))
    max_err_list.append(max(row))
print(min_err_list)
print(max_err_list)
min_err=min(min_err_list)
max_err=max(max_err_list)
print(min_err)
print(max_err)
[0, 1, 0, 1, 0, 0]
[7, 6, 5, 6, 6, 5]
0
7

如果追求简化的话,其实也可以直接这么写:

min_err=min(abs_err.min(axis=1))      #两级最小差
max_err=max(abs_err.max(axis=1))      #两级最大差

4.根据公式(1),取ρ=0.5,可以得到关联系数:

#重复代码已省略

#计算关联系数
rho=0.5                         #分辨系数
zeta=np.zeros(abs_err.shape)    #关联系数矩阵

zeta=(self.min_err+self.rho*self.max_err)/(self.abs_err+self.rho*self.max_err)

print(zeta)
[[0.77777778 1.         0.77777778 0.63636364 0.53846154 0.33333333
    1.        ]
 [0.63636364 0.77777778 0.63636364 0.46666667 0.77777778 0.36842105
    0.77777778]
 [1.         0.63636364 1.         0.53846154 0.63636364 0.41176471
    0.63636364]
 [0.53846154 0.77777778 0.77777778 0.77777778 0.46666667 0.36842105
    0.53846154]
 [0.77777778 0.53846154 0.53846154 1.         1.         0.36842105
    0.77777778]
 [0.77777778 1.         0.46666667 0.63636364 0.63636364 0.41176471
    0.77777778]]

5.分别计算每个人各个指标关联系数的均值(关联度):

#重复代码已省略

#分别计算每个人各个指标关联系数的均值(关联度)
r=[]

for zeta_i in zeta:
    r.append(np.mean(zeta_i))
    
print(r)
[0.7233877233877234, 0.6344497607655503, 0.6941881647764001, 0.6064777327935224, 0.7144142407300302, 0.6723877429759783]

尝试构建灰色关联类

我们可以把以上的步骤作为属性和方法封装在名为“灰色关联”的类中,代码如下所示:

import pandas as pd
import numpy as np

#导入数据
data=pd.read_excel(r'data.xlsx',index_col=0,sheet_name='灰色预测1')
x=np.array(data)

#确定参考数列
x_0=np.array([9,9,9,9,8,9,9])

#构建灰色关联类
class GreyRelation:
    #计算关联系数
    def calZeta(self):
        zeta=np.zeros(self.abs_err.shape)        
        zeta=(self.min_err+self.rho*self.max_err)/(self.abs_err+self.rho*self.max_err)
        return zeta
    #计算关联度
    def calR(self):
        r=[]
        for zeta_i in self.zeta:
            r.append(np.mean(zeta_i))
        return r
    def __init__(self,X,X_0,Rho=0.5):
        self.x=X                                        #原始数据
        self.x_0=X_0                                    #参考数据列
        self.abs_err=abs(X-X_0)                         #绝对误差
        self.min_err=min(self.abs_err.min(axis=1))      #两级最小差
        self.max_err=max(self.abs_err.max(axis=1))      #两级最大差
        self.rho=Rho                                    #分辨系数
        self.zeta=self.calZeta()                        #关联系数
        self.r=self.calR()                              #关联度
    def __str__(self):
        s1=f'x:\n{self.x}\n'
        s2=f'x_0:{self.x_0}\n'
        s3=f'abs_err:\n{self.abs_err}\n'
        s4=f'min_err:{self.min_err}\n'
        s5=f'max_err:{self.max_err}\n'
        s6=f'rho:{self.rho}\n'
        s7=f'zeta:\n{self.zeta}\n'
        s8=f'r:{self.r}'
        return(s1+s2+s3+s4+s5+s6+s7+s8)
    
teacher=GreyRelation(x,x_0)
print(teacher)
x:
[[8 9 8 7 5 2 9]
 [7 8 7 5 7 3 8]
 [9 7 9 6 6 4 7]
 [6 8 8 8 4 3 6]
 [8 6 6 9 8 3 8]
 [8 9 5 7 6 4 8]]
x_0:[9 9 9 9 8 9 9]
abs_err:
[[1 0 1 2 3 7 0]
 [2 1 2 4 1 6 1]
 [0 2 0 3 2 5 2]
 [3 1 1 1 4 6 3]
 [1 3 3 0 0 6 1]
 [1 0 4 2 2 5 1]]
min_err:0
max_err:7
rho:0.5
zeta:
[[0.77777778 1.         0.77777778 0.63636364 0.53846154 0.33333333
  1.        ]
 [0.63636364 0.77777778 0.63636364 0.46666667 0.77777778 0.36842105
  0.77777778]
 [1.         0.63636364 1.         0.53846154 0.63636364 0.41176471
  0.63636364]
 [0.53846154 0.77777778 0.77777778 0.77777778 0.46666667 0.36842105
  0.53846154]
 [0.77777778 0.53846154 0.53846154 1.         1.         0.36842105
  0.77777778]
 [0.77777778 1.         0.46666667 0.63636364 0.63636364 0.41176471
  0.77777778]]
r:[0.7233877233877234, 0.6344497607655503, 0.6941881647764001, 0.6064777327935224, 0.7144142407300302, 0.6723877429759783]

灰色生成数列

灰色系统理论认为,尽管客观表象复杂,但总是有整体功能的,因此必然蕴含某种内在规律。关键在于如何选择适当的方式去挖掘和利用它。灰色系统是通过对原始数据的整理来寻求其变化规律的,这是一种就数据寻求数据的现实规律的途径,即为灰色序列的生成。一切灰色序列都能通过某种生成弱化其随机性,显现其规律性。数据生成的常用方式有累加生成累减生成加权累加生成

累加生成

数学解释

把数列各项(时刻)数据依次累加的过程称为累加生成过程由累加生成过程所得的数列称为累加生成数列。设原始数列为:

x^{(0)}=(x^{(0)}(1),x^{(0)}(2),...,x^{(0)}(n))

令:

x^{(1)}(k)=\sum^k_{i=1}x^{(0)}(i),k=1,2,...,n\\
x^{(1)}=(x^{(1)}(1),x^{(1)}(2),...,x^{(1)}(n))

我们称所得到的新数列x^{(1)}为数列x^{(0)}的1次累加生成数列。类似的,有:

x^{(r)}(k)=\sum^k_{i=1}x^{(r-1)}(i),k=1,2,...,n,r\geq1\\
x^{(r)}=(x^{(r)}(1),x^{(r)}(2),...,x^{(r)}(n))

称为x^{(0)}的r次累加数列。

计算示例

例题:x^{(0)}=(3.2,3.3,3.4,3.6,3.8),求x^{(r)}(k)

我们可以使用递归函数来完成这个算法:

import numpy as np

#累加生成函数
def cumAddGen(arr,r=1):
    if r==1:
        res=[]
        for i in range(1,len(arr)+1):
            res.append(sum(arr[0:i]))
        return np.array(res)
    else:
        res=[]
        for i in range(1,len(arr)+1):
            res.append(sum(arr[0:i]))
        return cumAddGen(np.array(res),r-1)

x_0=np.array([3.2,3.3,3.4,3.6,3.8])

print(cumAddGen(x_0))
print(cumAddGen(cumAddGen(x_0)))
print(cumAddGen(x_0,2))
print(cumAddGen(cumAddGen(cumAddGen(x_0))))
print(cumAddGen(x_0,3))
[ 3.2  6.5  9.9 13.5 17.3]
[ 3.2  9.7 19.6 33.1 50.4]
[ 3.2  9.7 19.6 33.1 50.4]
[  3.2  12.9  32.5  65.6 116. ]
[  3.2  12.9  32.5  65.6 116. ]

累加生成的特点

一般经济数列都是非负数列。累加生成能使任意非负数列、摆动的与非摆动的,转化为非减的、递增的。

import numpy as np
from matplotlib import pyplot as plt

#累加生成函数
def cumAddGen(arr,r=1):
    if r==1:
        res=[]
        for i in range(1,len(arr)+1):
            res.append(sum(arr[0:i]))
        return np.array(res)
    else:
        res=[]
        for i in range(1,len(arr)+1):
            res.append(sum(arr[0:i]))
        return cumAddGen(np.array(res),r-1)

x_0=np.array([3.2,1.3,4.4,3.6,5.8])
plt.figure(figsize=(8,8),dpi=80)
plt.plot(np.arange(1,6),x_0) 
plt.show()
plt.plot(np.arange(1,6),cumAddGen(x_0)) 
plt.show()

累减生成

数学解释

对于原始数据列依次作前后相邻的两个数据相减的运算过程称为累减生成过程。如果原始数据列为:

x^{(1)}=(x^{(1)}(1),x^{(1)}(2),...,x^{(1)}(n))

令:

x^{(0)}(k)=x^{(1)}(k)-x^{(1)}(k-1),k=2,3,...,n

我们称所得到的新数列x^{(0)}为数列x^{(1)}的1次累减生成数列。

累减生成可以视作累加生成的逆运算,可以将当前数列还原为原始数列。实际运用中在数列x^{(1)}的基础上预测出\hat{x}^{(1)},通过累减生成得到预测数列\hat{x}^{(0)}

计算示例

例题:x^{(1)}=(5,9,14,24,35,46),求x^{(0)}(k)

同样可以用递归来实现:

import numpy as np

#累减生成函数
def cumMinusGen(arr,r=1):
    if r==1:
        res=[arr[0]]
        for i in range(1,len(arr)):
            res.append(arr[i]-arr[i-1])
        return np.array(res)
    else:
        res=[arr[0]]
        for i in range(1,len(arr)):
            res.append(arr[i]-arr[i-1])
        return cumMinusGen(np.array(res),r-1)
    
x_0=np.array([5,9,14,24,35,46])
print(cumMinusGen(x_0))
print(cumMinusGen(cumMinusGen(x_0)))
print(cumMinusGen(x_0,2))
[ 5  4  5 10 11 11]
[ 5 -1  1  5  1  0]
[ 5 -1  1  5  1  0]

不难看出,累减生成具有求导性质,这是因为

\frac{dx(k)}{dt}=\lim_{Δt→0} \frac{x(k)-x(k-Δt)}{Δt}

α^{(1)}(x(k))=x(k)-x(k-1),相当于Δt=1

加权邻值生成

设原始数列为:

x^{(0)}=(x^{(0)}(1),x^{(0)}(2),...,x^{(0)}(n))

则称x^{(0)}(k-1),x^{(0)}(k)为数列x^{(0)}的任意一对邻值。x^{(0)}(k-1)为后邻值,x^{(0)}(k)为前邻值。对于常数α∈[0,1],令:

z^{(0)}=αx^{(0)}(k)+(1-α)x^{(0)}(k-1),k=2,3,...,n

由此得到的数列z^{(0)}称为数列x^{(0)}在权α下的邻值生成数,权α也称为生成系数

特别的,当生成系数α=0.5时,则称

z^{(0)}=0.5x^{(0)}(k)+0.5x^{(0)}(k-1),k=2,3,...,n

均值生成数,也称等权均值生成数

灰色模型GM(1,1)

模型建立

灰色系统理论是基于关联空间、光滑离散函数等概念定义灰导数与灰微分方程,进而用离散数据列建立微分方程形式的动态模型,即灰色模型是利用离散随机数经过生成变为随机性被显著削弱而且较有规律的生成数,建立起的微分方程形式的模型,这样便于对其变化过程进行研究和描述。

G表示grey(灰色),M表示model(模型)

x^{(0)}=(x^{(0)}(1),x^{(0)}(2),…,x^{(0)}(n))为原始数列,其1次累加生成数列为:

x^{(1)}(k)=\sum^k_{i=1}x^{(0)}(i),k=1,2,...,n\\
x^{(1)}=(x^{(1)}(1),x^{(1)}(2),...,x^{(1)}(n))

定义x^{(1)}的灰导数为

d(k)=x^{(0)}(k)=x^{(1)}(k)-x^{(1)}(k-1)

z^{(1)}为数列x^{(1)}的邻值生成数列,即

z^{(1)}=αx^{(1)}(k)+(1-α)x^{(1)}(k-1),k=2,3,...,n

于是定义GM(1,1)的灰微分方程模型为

d(k)+az^{(1)}(k)=b,\\
即或x^{(0)}(k)+az^{(1)}(k)=b

在该模型中,x^{(0)}(k)称为灰导数,a称为发展系数z^{(1)}(k)称为白化背景值,b称为灰作用量

将时刻表k=2,3,…,n代入模型有:

\left\{
\begin{aligned}
&x^{(0)}(2)+az^{(1)}(2)=b,\\
&x^{(0)}(3)+az^{(1)}(3)=b,\\
&..................................\\
&x^{(0)}(n)+az^{(1)}(n)=b,\\
\end{aligned}\right.

引入矩阵向量记号:

u=\left[
\begin{aligned}
&a\\
&b
\end{aligned}\right]\quad
Y=\left[
\begin{aligned}
&x^{(0)}(2)\\
&x^{(0)}(3)\\
&..........\\
&x^{(0)}(n)
\end{aligned}\right]\quad
B=\left[
\begin{aligned}
&-z^{(1)}(2)\quad1\\
&-z^{(1)}(3)\quad1\\
&....................\\
&-z^{(1)}(n)\quad1
\end{aligned}\right]

于是GM(1,1)模型可以被表示为Y=Bu。

现在问题归结在于求a,b的值。用一元线性回归,即最小二乘法求它们的估计值为:

\hat{u}=\left[
\begin{aligned}
&\hat{a}\\
&\hat{b}
\end{aligned}\right]=(B^TB)^{-1}B^TY

GM(1,1)的白化型

对于GM(1,1)的灰微分方程,如果将灰导数x^{(0)}(k)的时刻k=2,3,…,n视为连续变量t,则x^{(1)}视为时间t函数x^{(1)}(t),于是x^{(0)}(k)对应于导数量级\frac{dx^{(1)}(t)}{dt},白化背景值z^{(1)}(k)对应于导数x^{(1)}(t)。于是GM(1,1)的灰微分方程对应于的白微分方程为:

\frac{dx^{(1)}(t)}{dt}+ax^{(1)}(t)=b
\int^k_{k-1}\frac{dx^{(1)}}{dt}dt+\int^k_{k-1}ax^{(1)}dt=\int^k_{k-1}bdt

在上式中的左边第二个式子:

\int^k_{k-1}ax^{(1)}dt≈\frac{a}{2(x^{(1)}(k)+x^{(1)}(k-1))}=az^{(1)}

白化方程化为:

x^{(0)}+az^{(1)}=b

GM(1,1)灰色预测的步骤

数据的检验与处理

为了保证GM(1,1)建模方法的可行性,需要对已知数据作必要的检验处理。

设原始数据列为x^{(0)}=(x^{(0)}(1),x^{(0)}(2),…,x^{(0)}(n)),我们需要计算该数列的级比

λ(k)=\frac{x^{(0)}(k-1)}{x^{(0)}(k)},k=2,3,...,n

如果所有的级比都落在可容覆盖区间X=(e^{-\frac{2}{n+1}},e^{\frac{2}{n+1}})内,则数据列x^{(0)}可以建立GM(1,1)模型且可以进行灰色预测。否则对数据作适当的变换处理,如平移变换,取c使得数据列:

y^{(0)}(k)=x^{(0)}(k)+c,k=1,2,...,n

的级比都落在可容覆盖内。

建立GM(1,1)模型

不妨设x^{(0)}=(x^{(0)}(1),x^{(0)}(2),…,x^{(0)}(n))满足上面的要求,以它为数据列建立GM(1,1)模型

x^{(0)}(k)+az^{(1)}(k)=b

用回归分析求得a,b的估计值,于是相应的白化模型为:

\frac{dx^{(1)}(t)}{dt}+ax^{(1)}(t)=b

解为:

x^{(1)}(t)=(x^{(0)}(1)-\frac{b}{a})e^{-a(t-1)}+\frac{b}{a}

于是得到预测值:

\hat{x}^{(1)}(k+1)=(x^{(0)}(1)-\frac{b}{a})e^{-ak}+\frac{b}{a},k=1,2,...,n-1

从而相应的得到预测值:

\hat{x}^{(0)}(k+1)=\hat{x}^{(1)}(k+1)-\hat{x}^{(1)}(k),k=1,2,...,n-1

检验预测值

(1)残差检验:计算相对残差

ε(k)=\frac{x^{(0)}(k)-\hat{x}^{(0)}(k)}{x^{(0)}(k)},k=1,2,...,n

如果对所有的|ε(k)|<0.1,则认为达到较高的要求;否则,若对所有的|ε(k)|<0.2,则认为达到一般要求。

(2)级比偏差值检验:计算

ρ(k)=1-\frac{1-0.5a}{1+0.5a}λ(k)

如果对所有的|ρ(k)|<0.1,则认为达到较高的要求;否则,若对所有的|ρ(k)|<0.2,则认为达到一般要求。

灰色预测计算实例

北方某城市1986-1992年道路交通噪声平均声级数据见下表

年份分贝数
198671.1
198772.4
198872.4
198972.1
199071.4
199172.0
199271.6
某市近年来交通噪声数据(dB)

级比检验

import numpy as np
import pandas as pd
import math

#导入数据
data=pd.read_excel(r'data.xlsx',index_col=0,sheet_name='灰色预测2')
x_0=np.squeeze(np.array(data).T)    #建立数据时间序列
print(f'x_0={x_0}')

#计算级比
def calClassRatio(arr):
    cr=[]
    for i in range(0,len(arr)-1):
        cr.append(arr[i]/arr[i+1])
    return(np.array(cr))

#判断级比是否落在落在可容覆盖区间内
def checkClassRatio(cr):
    range_pow=2/(len(cr)+2)
    for i in cr:
        if i>=math.pow(math.e,range_pow) or i<=math.pow(math.e,-range_pow):
            return False
    return True

#累加生成函数
def cumAddGen(arr,r=1):
    if r==1:
        res=[]
        for i in range(1,len(arr)+1):
            res.append(sum(arr[0:i]))
        return np.array(res)
    else:
        res=[]
        for i in range(1,len(arr)+1):
            res.append(sum(arr[0:i]))
        return cumAddGen(np.array(res),r-1)

print('\n级比检验:')
class_ratio=calClassRatio(x_0)
print(f'λ={class_ratio}')
print(f'λ check res={checkClassRatio(class_ratio)}')
x_0=[71.1 72.4 72.4 72.1 71.4 72.  71.6]

级比检验:
λ=[0.9820442  1.         1.00416089 1.00980392 0.99166667 1.00558659]
λ check res=True

GM(1,1)建模

#重复代码已省略

#累加生成函数
def cumAddGen(arr,r=1):
    if r==1:
        res=[]
        for i in range(1,len(arr)+1):
            res.append(sum(arr[0:i]))
        return np.array(res)
    else:
        res=[]
        for i in range(1,len(arr)+1):
            res.append(sum(arr[0:i]))
        return cumAddGen(np.array(res),r-1)
    
#累减生成函数
def cumMinusGen(arr,r=1):
    if r==1:
        res=[arr[0]]
        for i in range(1,len(arr)):
            res.append(arr[i]-arr[i-1])
        return np.array(res)
    else:
        res=[arr[0]]
        for i in range(1,len(arr)):
            res.append(arr[i]-arr[i-1])
        return cumMinusGen(np.array(res),r-1)
    
#构造数据矩阵
def constructDataMatrix(arr_0,arr_1):
    B=np.ones((len(arr_0)-1,2))
    for i in range(0,len(arr_0)-1):
        B[i,0]=-0.5*(arr_1[i]+arr_1[i+1])
    return B

#构造数据向量
def constructDataVector(arr):
    return(np.array([arr[1:]]).T)

#估计发展系数与灰作用量
def estimateU(B,Y):
    return(np.matmul(np.matmul(np.linalg.inv(np.matmul(B.T,B)),B.T),Y))

#对模型灰微分方程进行求解
def solveModel(arr,a,b):
    res=[]
    for k in range(0,len(arr)):
        res.append(((arr[0]-b/a))*math.pow(math.e,-a*k)+b/a)
    return(np.array(res))

print('\nGM(1,1)建模')
x_1=cumAddGen(x_0)              #原始数据一次累加
print(f'x_1={x_1}')

#构造数据矩阵B以及数据向量Y
Y=constructDataVector(x_0)      #数据向量
B=constructDataMatrix(x_0,x_1)  #数据矩阵
print(f'B=\n{B}')
print(f'Y=\n{Y}')

#计算u^
u_estimate=estimateU(B,Y)
a=u_estimate[0,0]
b=u_estimate[1,0]
print(f'u^=\n{u_estimate}')
print(f'a={a}')
print(f'b={b}')

#建立模型并求解
x_1_estimate=solveModel(x_0,a,b)
x_0_estimate=cumMinusGen(x_1_estimate)
print(f'x_1^={x_1_estimate}')
print(f'x_0^={x_0_estimate}')
GM(1,1)建模
x_1=[ 71.1 143.5 215.9 288.  359.4 431.4 503. ]
B=
[[-107.3     1.  ]
 [-179.7     1.  ]
 [-251.95    1.  ]
 [-323.7     1.  ]
 [-395.4     1.  ]
 [-467.2     1.  ]]
Y=
[[72.4]
 [72.4]
 [72.1]
 [71.4]
 [72. ]
 [71.6]]
u^=
[[2.34378648e-03]
 [7.26572696e+01]]
a=0.0023437864785236795
b=72.65726960367881
x_1^=[ 71.1        143.50574144 215.741978   287.8091065  359.70752283
 431.43762195 502.9997979 ]
x_0^=[71.1        72.40574144 72.23623656 72.0671285  71.89841633 71.73009912
 71.56217595]

模型检验

#重复代码已省略

#计算相对残差
def calRelativeResiduals(arr,arr_e):
    res=[]
    for i in range(0,len(arr)):
        res.append((arr[i]-arr_e[i])/arr[i])
    return(np.array(res))

#计算级比偏差
def calRatioDeviation(cr,a):
    return(1-(1-0.5*a)/(1+0.5*a)*cr)
    
print('\n模型检验:')
relative_residuals=calRelativeResiduals(x_0,x_0_estimate)   #相对残差
ratio_deviation=calRatioDeviation(class_ratio,a)            #级比偏差
print(f'relative_residuals={relative_residuals}')
print(f'ratio_deviation={ratio_deviation}')
模型检验:
relative_residuals=[ 2.03868802e-14 -7.93016634e-05  2.26192594e-03  4.55915376e-04
 -6.98062087e-03  3.74862332e-03  5.28268865e-04]
ratio_deviation=[ 0.02025481  0.00234104 -0.0018101  -0.00743993  0.01065487 -0.00323247]

尝试构建灰色预测类

将以上步骤和变量作为方法与属性封装在“灰色预测类”中,同时提供了一些用于预测的接口函数:

import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
import math

#导入数据
data=pd.read_excel(r'data.xlsx',index_col=0,sheet_name='灰色预测2')
x_0=np.squeeze(np.array(data).T)    #建立数据时间序列

#构造灰色预测类
class GreyModel:
    #级比的计算与检查
    def __calClassRatio(self):
        cr=[]
        for i in range(0,len(self.x_0)-1):
            cr.append(self.x_0[i]/self.x_0[i+1])
        return(np.array(cr))
    def __checkClassRatio(self):
        range_pow=2/(len(self.class_ratio)+2)
        for i in self.class_ratio:
            if i>=math.pow(math.e,range_pow) or i<=math.pow(math.e,-range_pow):
                print("模型级比检查不通过,请检查数据后再试!")
                return False
        return True
    #累加生成函数与累减生成函数
    def __cumAddGen(self,arr,r=1):
        res=[]
        for i in range(1,len(arr)+1):
            res.append(sum(arr[0:i]))
        if r==1:
            return np.array(res)
        else:
            return self.__cumAddGen(arr=np.array(res),r=r-1)   
    def __cumMinusGen(self,arr,r=1):
        res=[arr[0]]
        for i in range(1,len(arr)):
            res.append(arr[i]-arr[i-1])
        if r==1:            
            return np.array(res)
        else:            
            return self.__cumMinusGen(arr=np.array(res),r=r-1)
    #邻值生成函数
    def __neighborGen(self,arr,a=0.5):
        res=[]
        for i in range(1,len(arr)):
            res.append(a*arr[i]+(1-a)*arr[i-1])
        return np.array(res)
    #构造数据向量Y与数据矩阵B
    def __constructDataVector(self):
        return(np.array([self.x_0[1:]]).T)
    def __constructDataMatrix(self):
        B=np.ones((len(self.x_0)-1,2))
        for i in range(0,len(self.x_0)-1):
            B[i,0]=-self.z_1[i]
        return B
    #估计发展系数与灰作用量
    def __estimateU(self):
        return(np.matmul(np.matmul(np.linalg.inv(np.matmul(self.B.T,self.B)),self.B.T),self.Y))
    #对模型灰微分方程进行求解
    def __solveModel(self):
        res=[]
        for k in range(0,len(self.x_0)):
            res.append(((self.x_0[0]-self.b/self.a))*math.pow(math.e,-self.a*k)+self.b/self.a)
        return(np.array(res))
    #计算相对残差与级比偏差
    def __calRelativeResiduals(self):
        res=[]
        for i in range(0,len(self.x_0)):
            res.append((self.x_0[i]-self.x_0_estimate[i])/self.x_0[i])
        return(np.array(res))
    def __calRatioDeviation(self):
        return(1-(1-0.5*self.a)/(1+0.5*self.a)*self.class_ratio)
    #检查模型
    def __checkModel(self):
        if max(abs(self.relative_residuals))<0.2 and max(abs(self.ratio_deviation))<0.2:
            if max(abs(self.relative_residuals))>=0.1 and max(abs(self.ratio_deviation))>=0.1:
                print("模型精确度仅达到一般要求,认为通过,但还是建议检查数据!")
            return True
        else:
            print("模型精确度不足,请检查数据后重试!")
            return False
    def __init__(self,X_0,weight=0.5):
        self.x_0=X_0                                                    #初始数据列(灰导数)
        self.class_ratio=self.__calClassRatio()                         #级比
        self.cr_accepted=self.__checkClassRatio()                       #级比是否完全落在可容覆盖区间内
        self.x_1=self.__cumAddGen(arr=self.x_0)                         #经过一次累加生成的数据列
        self.weight=weight                                              #权重
        self.z_1=self.__neighborGen(arr=self.x_1,a=self.weight)         #白化背景值
        self.B=self.__constructDataMatrix()                             #数据向量
        self.Y=self.__constructDataVector()                             #数据矩阵
        self.a=self.__estimateU()[0,0]                                  #发展系数
        self.b=self.__estimateU()[1,0]                                  #灰作用量
        self.x_1_estimate=self.__solveModel()                           #x_1的模型估计值
        self.x_0_estimate=self.__cumMinusGen(arr=self.x_1_estimate)     #x_0的模型估计值
        self.relative_residuals=self.__calRelativeResiduals()           #相对残差
        self.ratio_deviation=self.__calRatioDeviation()                 #级比偏差
        self.model_accepted=self.__checkModel()                         #模型精度是否通过检测
    def __str__(self):
        s=''
        s+=f'x_0={self.x_0}\n'
        s+=f'class_ratio={self.class_ratio},accepted={self.cr_accepted}\n'
        s+=f'x_1={self.x_1}\n'
        s+=f'weight={self.weight}\n'
        s+=f'z_1={self.z_1}\n'
        s+=f'B=\n{self.B}\n'
        s+=f'Y=\n{self.Y}\n'
        s+=f'a={self.a},b={self.b}\n'
        s+=f'x_1_estimate={self.x_1_estimate}\n'
        s+=f'x_0_estimate={self.x_0_estimate}\n'
        s+=f'relative_residuals={self.relative_residuals}\n'
        s+=f'ratio_deviation={self.ratio_deviation}\n'
        s+=f'model_accepted={self.model_accepted}'
        return s
    #接口:通过图像检查原始值与模型预测值之间的关系
    def showModel(self):
        #原始值图像曲线
        x1=np.arange(1,len(self.x_0)+1)
        y1=self.x_0
        #模型预测值图像曲线
        x2=np.arange(1,len(self.x_0_estimate)+1)
        y2=self.x_0_estimate
        plt.plot(x1,y1,x2,y2,marker='o')                       
        plt.xlabel("K - Time")
        plt.ylabel("Value")
        plt.legend(labels=["Estimated Value","Real Value"],loc="lower right") 
        plt.show()
    #接口:通过模型进行预测,返回从k=1开始直到k=target的完整预测结果数列
    def forecast(self,target):
        res=[]
        for k in range(0,target):
            res.append(((self.x_0[0]-self.b/self.a))*math.pow(math.e,-self.a*k)+self.b/self.a)
        return(self.__cumMinusGen(arr=(np.array(res))))
    #接口:通过模型进行预测,并绘制预测结果图
    def forecastAndShow(self,start=1,end=None):
        forecast_data=self.forecast(target=end)
        x=np.arange(start,end+1)
        y=forecast_data
        plt.plot(x,y,marker='o')
        plt.xlabel("K - Time")
        plt.ylabel("Value")
        for a,b in zip(x,y):
            plt.text(a,b+0.02,'%.4f'%b,ha='center',va='bottom')
        plt.show()
            
A=GreyModel(x_0)
print(A)
A.showModel()
print(A.forecast(target=12))
A.forecastAndShow(end=12)
x_0=[71.1 72.4 72.4 72.1 71.4 72.  71.6]
class_ratio=[0.9820442  1.         1.00416089 1.00980392 0.99166667 1.00558659],accepted=True
x_1=[ 71.1 143.5 215.9 288.  359.4 431.4 503. ]
B=
[[-107.3     1.  ]
 [-179.7     1.  ]
 [-251.95    1.  ]
 [-323.7     1.  ]
 [-395.4     1.  ]
 [-467.2     1.  ]]
Y=
[[72.4]
 [72.4]
 [72.1]
 [71.4]
 [72. ]
 [71.6]]
a=0.0023437864785236795,b=72.65726960367881
x_1_estimate=[ 71.1        143.50574144 215.741978   287.8091065  359.70752283
 431.43762195 502.9997979 ]
x_0_estimate=[71.1        72.40574144 72.23623656 72.0671285  71.89841633 71.73009912
 71.56217595]
relative_residuals=[ 2.03868802e-14 -7.93016634e-05  2.26192594e-03  4.55915376e-04
 -6.98062087e-03  3.74862332e-03  5.28268865e-04]
ratio_deviation=[ 0.02025481  0.00234104 -0.0018101  -0.00743993  0.01065487 -0.00323247]
model_accepted=True
[71.1 72.40574144 72.23623656 72.0671285 71.89841633 71.73009912 71.56217595 71.39464589 71.22750803 71.06076145 70.89440522 70.72843845]

灰色预测综合实战:SARS疫情对某些经济指标影响

问题提出

2003年的SARS疫情对中国部分行业的经济发展产生了一定的影响,特别是对帮分疫情较严重的省市的相关行业所造成的影响是明显的,经济影响主要分为直接经济影响和间接影响。直接经济影响涉及到商品零售业、旅游业、综合服务等行业。很多方面难以进行定量地评估,现仅就SARS疫情较重的某市商品零售业、旅游业和综合服务业的影响进行定量的评估分析。

究竟SARS疫情对商品零售业、旅游业和综合服务业的影响有多大,已知该市从1997年1月到2003年10月的商品零售额、接待旅游人数和综合服务收入的统计数据如下表1、表2、表3。

年代1月2月3月4月5月6月7月8月9月10月11月12月
199783.079.878.185.186.688.290.386.793.392.590.996.9
1998101.785.187.891.693.494.597.499.5104.2102.3101.0123.5
199992.2114.093.3101.0103.5105.2109.5109.2109.6111.2121.7131.3
2000105.0125.7106.6116.0117.6118.0121.7118.7120.2127.8121.8121.9
2001139.3129.5122.5124.5135.7130.8138.7133.7136.8138.9129.6133.7
2002137.5135.3133.0133.4142.8141.6142.9147.3159.6162.1153.5155.9
2003163.2159.7158.4145.2124144.1157162.6171.8180.7173.5176.5
表1:商品的零售额(单位:亿元)
年代1月2月3月4月5月6月7月8月9月10月11月12月
19979.411.316.819.820.318.820.924.924.724.319.418.6
19989.611.715.819.919.517.817.823.321.424.520.115.9
199910.112.917.721.021.020.421.925.829.329.823.616.5
200011.426.019.625.927.624.323.027.827.328.532.818.5
200111.526.420.426.128.928.025.230.828.728.122.220.7
200213.729.723.128.929.027.426.032.231.432.629.222.9
200315.417.123.511.61.782.618.816.220.124.926.521.8
表2 接待海外旅游人数(单位:万人)
年代2月3月4月5月6月7月8月9月10月11月12月
199796144194276383466554652747832972
199811116923540045956569580588110111139
199915123833542554164173986697510871238
20001642633765316007119131038117312961497
200118231844557670885610001145129214351667
200221636150464281897911421305147916441920
2003241404584741923111412981492168418852218
表3 综合服务业累计数据(单位:亿元)

试根据这些历史数据建立预测评估模型,评估2003年SARS疫情给该市的商品零售业、旅游业和综合服务业所造成的影响。

模型的分析与假设

根据所掌握的历史统计数据可以看出,在正常情况下,全年的平均值较好地反映了相关指标的变化规律,这样可以把预测评估分成两部分:

(1)利用灰色理论建立灰微分方程模型,由1997~2002年的平均值预测2003年平均值;

(2)通过历史数据计算每个月的指标值与全年总值的关系,从而可预测出正常情况下2003年每个月的指标值,再与实际值比较可以估算出SARS疫情实际造成的影响。

给出下面两条假设:

(1)假设该市的统计数据都是可靠准确的;

(2)假设该市在SARS疫情流行期间和结束之后,数据的变化只与SARS疫情的影响有关,不考虑其他随机因素的影响。

数据导入与初始化

导入必要库

import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
import math

导入数据

file='./data/灰色预测_SARS.xlsx'

retail_data_read=pd.read_excel(file,sheet_name='商品零售额',index_col=0)
tourist_data_read=pd.read_excel(file,sheet_name='接待海外旅游人数',index_col=0)
service_data_read=pd.read_excel(file,sheet_name='综合服务业累计数据',index_col=0)

print(f'表1:商品的零售额(单位:亿元)\n{retail_data_read}\n\n')
print(f'表2 接待海外旅游人数(单位:万人)\n{tourist_data_read}\n\n')
print(f'表3 综合服务业累计数据(单位:亿元)\n{service_data_read}\n\n')
表1:商品的零售额(单位:亿元)
         1月     2月     3月     4月     5月     6月     7月     8月     9月    10月  \
年代                                                                           
1997   83.0   79.8   78.1   85.1   86.6   88.2   90.3   86.7   93.3   92.5   
1998  101.7   85.1   87.8   91.6   93.4   94.5   97.4   99.5  104.2  102.3   
1999   92.2  114.0   93.3  101.0  103.5  105.2  109.5  109.2  109.6  111.2   
2000  105.0  125.7  106.6  116.0  117.6  118.0  121.7  118.7  120.2  127.8   
2001  139.3  129.5  122.5  124.5  135.7  130.8  138.7  133.7  136.8  138.9   
2002  137.5  135.3  133.0  133.4  142.8  141.6  142.9  147.3  159.6  162.1   
2003  163.2  159.7  158.4  145.2  124.0  144.1  157.0  162.6  171.8  180.7   

        11月    12月  
年代                  
1997   90.9   96.9  
1998  101.0  123.5  
1999  121.7  131.3  
2000  121.8  121.9  
2001  129.6  133.7  
2002  153.5  155.9  
2003  173.5  176.5  


表2 接待海外旅游人数(单位:万人)
        1月    2月    3月    4月     5月     6月    7月    8月    9月   10月   11月   12月
年代                                                                            
1997   9.4  11.3  16.8  19.8  20.30  18.80  20.9  24.9  24.7  24.3  19.4  18.6
1998   9.6  11.7  15.8  19.9  19.50  17.80  17.8  23.3  21.4  24.5  20.1  15.9
1999  10.1  12.9  17.7  21.0  21.00  20.40  21.9  25.8  29.3  29.8  23.6  16.5
2000  11.4  26.0  19.6  25.9  27.60  24.30  23.0  27.8  27.3  28.5  32.8  18.5
2001  11.5  26.4  20.4  26.1  28.90  28.00  25.2  30.8  28.7  28.1  22.2  20.7
2002  13.7  29.7  23.1  28.9  29.00  27.40  26.0  32.2  31.4  32.6  29.2  22.9
2003  15.4  17.1  23.5  11.6   1.78   2.61   8.8  16.2  20.1  24.9  26.5  21.8


表3 综合服务业累计数据(单位:亿元)
       2月   3月   4月   5月   6月    7月    8月    9月   10月   11月   12月
年代                                                               
1997   96  144  194  276  383   466   554   652   747   832   972
1998  111  169  235  400  459   565   695   805   881  1011  1139
1999  151  238  335  425  541   641   739   866   975  1087  1238
2000  164  263  376  531  600   711   913  1038  1173  1296  1497
2001  182  318  445  576  708   856  1000  1145  1292  1435  1667
2002  216  361  504  642  818   979  1142  1305  1479  1644  1920
2003  241  404  584  741  923  1114  1298  1492  1684  1885  2218


建立数据矩阵

#剔除了2003年的数据
retail_data=np.array(retail_data_read[:-1])      #零售业数据
tourist_data=np.array(tourist_data_read[:-1])    #旅游业数据
service_data=np.array(service_data_read[:-1])    #服务业数据

#2003年的数据
retail_2003=np.squeeze(np.array(retail_data_read[-1:]))
tourist_2003=np.squeeze(np.array(tourist_data_read[-1:]))
service_2003=np.squeeze(np.array(service_data_read[-1:]))

print(tourist_data)
print(tourist_data)
print(service_data)
print(tourist_2003)
print(tourist_2003)
print(service_2003)
[[ 9.4 11.3 16.8 19.8 20.3 18.8 20.9 24.9 24.7 24.3 19.4 18.6]
 [ 9.6 11.7 15.8 19.9 19.5 17.8 17.8 23.3 21.4 24.5 20.1 15.9]
 [10.1 12.9 17.7 21.  21.  20.4 21.9 25.8 29.3 29.8 23.6 16.5]
 [11.4 26.  19.6 25.9 27.6 24.3 23.  27.8 27.3 28.5 32.8 18.5]
 [11.5 26.4 20.4 26.1 28.9 28.  25.2 30.8 28.7 28.1 22.2 20.7]
 [13.7 29.7 23.1 28.9 29.  27.4 26.  32.2 31.4 32.6 29.2 22.9]]
[[ 9.4 11.3 16.8 19.8 20.3 18.8 20.9 24.9 24.7 24.3 19.4 18.6]
 [ 9.6 11.7 15.8 19.9 19.5 17.8 17.8 23.3 21.4 24.5 20.1 15.9]
 [10.1 12.9 17.7 21.  21.  20.4 21.9 25.8 29.3 29.8 23.6 16.5]
 [11.4 26.  19.6 25.9 27.6 24.3 23.  27.8 27.3 28.5 32.8 18.5]
 [11.5 26.4 20.4 26.1 28.9 28.  25.2 30.8 28.7 28.1 22.2 20.7]
 [13.7 29.7 23.1 28.9 29.  27.4 26.  32.2 31.4 32.6 29.2 22.9]]
[[  96  144  194  276  383  466  554  652  747  832  972]
 [ 111  169  235  400  459  565  695  805  881 1011 1139]
 [ 151  238  335  425  541  641  739  866  975 1087 1238]
 [ 164  263  376  531  600  711  913 1038 1173 1296 1497]
 [ 182  318  445  576  708  856 1000 1145 1292 1435 1667]
 [ 216  361  504  642  818  979 1142 1305 1479 1644 1920]]
[15.4  17.1  23.5  11.6   1.78  2.61  8.8  16.2  20.1  24.9  26.5  21.8 ]
[15.4  17.1  23.5  11.6   1.78  2.61  8.8  16.2  20.1  24.9  26.5  21.8 ]
[ 241  404  584  741  923 1114 1298 1492 1684 1885 2218]

导入模型类

#构造灰色预测类
class GreyModel:
    #级比的计算与检查
    def __calClassRatio(self):
        cr=[]
        for i in range(0,len(self.x_0)-1):
            cr.append(self.x_0[i]/self.x_0[i+1])
        return(np.array(cr))
    def __checkClassRatio(self):
        range_pow=2/(len(self.class_ratio)+2)
        for i in self.class_ratio:
            if i>=math.pow(math.e,range_pow) or i<=math.pow(math.e,-range_pow):
                print("模型级比检查不通过,请检查数据后再试!")
                return False
        return True
    #累加生成函数与累减生成函数
    def __cumAddGen(self,arr,r=1):
        res=[]
        for i in range(1,len(arr)+1):
            res.append(sum(arr[0:i]))
        if r==1:
            return np.array(res)
        else:
            return self.__cumAddGen(arr=np.array(res),r=r-1)   
    def __cumMinusGen(self,arr,r=1):
        res=[arr[0]]
        for i in range(1,len(arr)):
            res.append(arr[i]-arr[i-1])
        if r==1:            
            return np.array(res)
        else:            
            return self.__cumMinusGen(arr=np.array(res),r=r-1)
    #邻值生成函数
    def __neighborGen(self,arr,a=0.5):
        res=[]
        for i in range(1,len(arr)):
            res.append(a*arr[i]+(1-a)*arr[i-1])
        return np.array(res)
    #构造数据向量Y与数据矩阵B
    def __constructDataVector(self):
        return(np.array([self.x_0[1:]]).T)
    def __constructDataMatrix(self):
        B=np.ones((len(self.x_0)-1,2))
        for i in range(0,len(self.x_0)-1):
            B[i,0]=-self.z_1[i]
        return B
    #估计发展系数与灰作用量
    def __estimateU(self):
        return(np.matmul(np.matmul(np.linalg.inv(np.matmul(self.B.T,self.B)),self.B.T),self.Y))
    #对模型灰微分方程进行求解
    def __solveModel(self):
        res=[]
        for k in range(0,len(self.x_0)):
            res.append(((self.x_0[0]-self.b/self.a))*math.pow(math.e,-self.a*k)+self.b/self.a)
        return(np.array(res))
    #计算相对残差与级比偏差
    def __calRelativeResiduals(self):
        res=[]
        for i in range(0,len(self.x_0)):
            res.append((self.x_0[i]-self.x_0_estimate[i])/self.x_0[i])
        return(np.array(res))
    def __calRatioDeviation(self):
        return(1-(1-0.5*self.a)/(1+0.5*self.a)*self.class_ratio)
    #检查模型
    def __checkModel(self):
        if max(abs(self.relative_residuals))<0.2 and max(abs(self.ratio_deviation))<0.2:
            if max(abs(self.relative_residuals))>=0.1 and max(abs(self.ratio_deviation))>=0.1:
                print("模型精确度仅达到一般要求,认为通过,但还是建议检查数据!")
            return True
        else:
            print("模型精确度不足,请检查数据后重试!")
            return False
    def __init__(self,X_0,weight=0.5):
        self.x_0=X_0                                                    #初始数据列(灰导数)
        self.class_ratio=self.__calClassRatio()                         #级比
        self.cr_accepted=self.__checkClassRatio()                       #级比是否完全落在可容覆盖区间内
        self.x_1=self.__cumAddGen(arr=self.x_0)                         #经过一次累加生成的数据列
        self.weight=weight                                              #权重
        self.z_1=self.__neighborGen(arr=self.x_1,a=self.weight)         #白化背景值
        self.B=self.__constructDataMatrix()                             #数据向量
        self.Y=self.__constructDataVector()                             #数据矩阵
        self.a=self.__estimateU()[0,0]                                  #发展系数
        self.b=self.__estimateU()[1,0]                                  #灰作用量
        self.x_1_estimate=self.__solveModel()                           #x_1的模型估计值
        self.x_0_estimate=self.__cumMinusGen(arr=self.x_1_estimate)     #x_0的模型估计值
        self.relative_residuals=self.__calRelativeResiduals()           #相对残差
        self.ratio_deviation=self.__calRatioDeviation()                 #级比偏差
        self.model_accepted=self.__checkModel()                         #模型精度是否通过检测
    def __str__(self):
        s=''
        s+=f'x_0={self.x_0}\n'
        s+=f'class_ratio={self.class_ratio},accepted={self.cr_accepted}\n'
        s+=f'x_1={self.x_1}\n'
        s+=f'weight={self.weight}\n'
        s+=f'z_1={self.z_1}\n'
        s+=f'B=\n{self.B}\n'
        s+=f'Y=\n{self.Y}\n'
        s+=f'a={self.a},b={self.b}\n'
        s+=f'x_1_estimate={self.x_1_estimate}\n'
        s+=f'x_0_estimate={self.x_0_estimate}\n'
        s+=f'relative_residuals={self.relative_residuals}\n'
        s+=f'ratio_deviation={self.ratio_deviation}\n'
        s+=f'model_accepted={self.model_accepted}'
        return s
    #接口:通过图像检查原始值与模型预测值之间的关系
    def showModel(self):
        #原始值图像曲线
        x1=np.arange(1,len(self.x_0)+1)
        y1=self.x_0
        #模型预测值图像曲线
        x2=np.arange(1,len(self.x_0_estimate)+1)
        y2=self.x_0_estimate
        plt.plot(x1,y1,x2,y2,marker='o')                       
        plt.xlabel("K - Time")
        plt.ylabel("Value")
        plt.legend(labels=["Estimated Value","Real Value"],loc="lower right") 
        plt.show()
    #接口:通过模型进行预测,返回从k=1开始直到k=target的完整预测结果数列
    def forecast(self,target):
        res=[]
        for k in range(0,target):
            res.append(((self.x_0[0]-self.b/self.a))*math.pow(math.e,-self.a*k)+self.b/self.a)
        return(self.__cumMinusGen(arr=(np.array(res))))
    #接口:通过模型进行预测,并绘制预测结果图
    def forecastAndShow(self,start=1,end=None):
        forecast_data=self.forecast(target=end)
        x=np.arange(start,end+1)
        y=forecast_data
        plt.plot(x,y,marker='o')
        plt.xlabel("K - Time")
        plt.ylabel("Value")
        for a,b in zip(x,y):
            plt.text(a,b+0.02,'%.4f'%b,ha='center',va='bottom')
        plt.show()

计算均值与占比

#计算1997-2002年各行业的年均值
retail_year_avg=np.average(retail_data,axis=1)          #零售业年均值
tourist_year_avg=np.average(tourist_data,axis=1)        #旅游业年均值
service_year_avg=np.average(service_data,axis=1)        #服务业年均值

#计算各行业每月在年内的总值占比
retail_month_sum=np.sum(retail_data,axis=0)             #零售业月总值
tourist_month_sum=np.sum(tourist_data,axis=0)           #旅游业月总值
service_month_sum=np.sum(service_data,axis=0)           #服务业月总值
retail_share=retail_month_sum/np.sum(retail_data)       #零售业月占比
tourist_share=tourist_month_sum/np.sum(tourist_data)    #旅游业月占比
service_share=service_month_sum/np.sum(service_data)    #服务业月占比

print('各行业年均值:')
print(retail_year_avg)
print(tourist_year_avg)
print(service_year_avg)
print('各行业月占比:')
print(retail_share)
print(tourist_share)
print(service_share)
各行业年均值:
[ 87.61666667  98.5        108.475      118.41666667 132.80833333
 145.40833333]
[19.1        18.10833333 20.83333333 24.39166667 24.75       27.175     ]
[ 483.27272727  588.18181818  657.81818182  778.36363636  874.90909091
 1000.90909091]
各行业月占比:
[0.07941215 0.08070214 0.07490325 0.07855619 0.08193184 0.08177511
 0.08445152 0.0838005  0.08724848 0.08858669 0.08662158 0.09201056]
[0.04074924 0.07318737 0.07033431 0.08782485 0.09073994 0.08478571
 0.08360727 0.10221423 0.10097376 0.10407492 0.09136017 0.07014824]
[0.01908001 0.03096354 0.04332407 0.05910656 0.07277365 0.08747771
 0.1045875  0.12051516 0.13577917 0.15149944 0.17489319]

零售业分析

构建模型

retail=GreyModel(retail_year_avg,weight=0.4)   #零售业模型,权重根据提示取0.4
print(retail)
retail.showModel()
x_0=[ 87.61666667  98.5        108.475      118.41666667 132.80833333
 145.40833333]
class_ratio=[0.88950931 0.90804333 0.91604504 0.89163582 0.91334747],accepted=True
x_1=[ 87.61666667 186.11666667 294.59166667 413.00833333 545.81666667
 691.225     ]
weight=0.4
z_1=[127.01666667 229.50666667 341.95833333 466.13166667 603.98      ]
B=
[[-127.01666667    1.        ]
 [-229.50666667    1.        ]
 [-341.95833333    1.        ]
 [-466.13166667    1.        ]
 [-603.98          1.        ]]
Y=
[[ 98.5       ]
 [108.475     ]
 [118.41666667]
 [132.80833333]
 [145.40833333]]
a=-0.09929682908861885,b=85.59852467721248
x_1_estimate=[ 87.61666667 186.75590989 296.24470193 417.16347429 550.70544909
 698.18841378]
x_0_estimate=[ 87.61666667  99.13924322 109.48879204 120.91877236 133.5419748
 147.48296469]
relative_residuals=[ 0.         -0.00648978 -0.00934586 -0.02112968 -0.00552406 -0.01426762]
ratio_deviation=[ 0.01755093 -0.00291961 -0.01175737  0.01520223 -0.00877795]
model_accepted=True

进行预测

retail_2003_est_sum=retail.forecast(target=len(retail.x_0)+1)[len(retail.x_0)]*12   #2003年预计总值
retail_2003_est=retail_2003_est_sum*retail_share                                    #2003年预计月分布值

print(f'2003年预计零售业总值为:{retail_2003_est_sum}')
print('按月分布如下:')
print(retail_2003_est)

和实际值进行比较

#时间横轴
x=['JAN','FEB','MAR','APR','MAY','JUN','JUL','AUG','SEPT','OCT','NOV','DEC']

#数据纵轴
y1=retail_2003_est              #预测值
y2=retail_2003                  #实际值

plt.figure(figsize=(16,8),dpi=80)
plt.title("Retail Compared by Month")
plt.xlabel("Month")
plt.ylabel("Retail")
plt.plot(x,y1,x,y2,marker='o')
plt.legend(labels=["Estimated Value","Real Value"],loc="lower right")  
for a,b,c in zip(x,y1,y2):
    plt.text(a,b,'%.1f'%b,ha='center',va='bottom')
    plt.text(a,c,'%.1f'%c,ha='center',va='bottom')    
plt.show()

旅游业分析

构建模型

tourist=GreyModel(tourist_year_avg,weight=0.5)   #旅游业模型,权重根据提示取0.5
print(tourist)
tourist.showModel()
x_0=[19.1        18.10833333 20.83333333 24.39166667 24.75       27.175     ]
class_ratio=[1.054763   0.8692     0.85411684 0.98552189 0.91076357],accepted=True
x_1=[ 19.1         37.20833333  58.04166667  82.43333333 107.18333333
 134.35833333]
weight=0.5
z_1=[ 28.15416667  47.625       70.2375      94.80833333 120.77083333]
B=
[[ -28.15416667    1.        ]
 [ -47.625         1.        ]
 [ -70.2375        1.        ]
 [ -94.80833333    1.        ]
 [-120.77083333    1.        ]]
Y=
[[18.10833333]
 [20.83333333]
 [24.39166667]
 [24.75      ]
 [27.175     ]]
a=-0.0938147658004182,b=16.267060982951907
x_1_estimate=[ 19.1         38.03314302  58.82847944  81.66916767 106.75638089
 134.31107892]
x_0_estimate=[19.1        18.93314302 20.79533642 22.84068823 25.08721322 27.55469803]
relative_residuals=[ 1.86005952e-16 -4.55486252e-02  1.82385173e-03  6.35864068e-02
 -1.36247764e-02 -1.39723286e-02]
ratio_deviation=[-0.15858538  0.04524295  0.06181077 -0.08252873 -0.0004118 ]
model_accepted=True

进行预测

tourist_2003_est_sum=tourist.forecast(target=len(tourist.x_0)+1)[len(tourist.x_0)]*12   #2003年预计总值
tourist_2003_est=tourist_2003_est_sum*tourist_share                                     #2003年预计月分布值

print(f'2003年预计旅游业总值为:{tourist_2003_est_sum}')
print('按月分布如下:')
print(tourist_2003_est)
2003年预计旅游业总值为:363.1785055027333
按月分布如下:
[14.79924816 26.58008041 25.54390779 31.8960965  32.95479461 30.79234739
 30.36436305 37.12201061 36.67150077 37.79777537 33.18004953 25.47633131]

和实际值进行比较

#时间横轴
x=['JAN','FEB','MAR','APR','MAY','JUN','JUL','AUG','SEPT','OCT','NOV','DEC']

#数据纵轴
y1=tourist_2003_est              #预测值
y2=tourist_2003                  #实际值

plt.figure(figsize=(16,8),dpi=80)
plt.title("Tourist Compared by Month")
plt.xlabel("Month")
plt.ylabel("Tourist")
plt.plot(x,y1,x,y2,marker='o')
plt.legend(labels=["Estimated Value","Real Value"],loc="lower right")  
for a,b,c in zip(x,y1,y2):
    plt.text(a,b,'%.1f'%b,ha='center',va='bottom')
    plt.text(a,c,'%.1f'%c,ha='center',va='bottom')    
plt.show()

服务业分析

构建模型

service=GreyModel(service_year_avg,weight=0.5)   #旅游业模型,权重根据提示取0.5
print(service)
service.showModel()
x_0=[ 483.27272727  588.18181818  657.81818182  778.36363636  874.90909091
 1000.90909091]
class_ratio=[0.82163833 0.89414041 0.84512964 0.88965087 0.87411444],accepted=True
x_1=[ 483.27272727 1071.45454545 1729.27272727 2507.63636364 3382.54545455
 4383.45454545]
weight=0.5
z_1=[ 777.36363636 1400.36363636 2118.45454545 2945.09090909 3883.        ]
B=
[[-7.77363636e+02  1.00000000e+00]
 [-1.40036364e+03  1.00000000e+00]
 [-2.11845455e+03  1.00000000e+00]
 [-2.94509091e+03  1.00000000e+00]
 [-3.88300000e+03  1.00000000e+00]]
Y=
[[ 588.18181818]
 [ 657.81818182]
 [ 778.36363636]
 [ 874.90909091]
 [1000.90909091]]
a=-0.13431667138233638,b=481.2013067810485
x_1_estimate=[ 483.27272727 1067.76037284 1736.27101464 2500.88337484 3375.41255182
 4375.65963283]
x_0_estimate=[ 483.27272727  584.48764557  668.51064179  764.6123602   874.52917698
 1000.24708101]
relative_residuals=[-4.70487289e-16  6.28066441e-03 -1.62544306e-02  1.76669047e-02
  4.34232460e-04  6.61408619e-04]
ratio_deviation=[ 6.00567844e-02 -2.28846195e-02  3.31830391e-02 -1.77486503e-02
  2.48185771e-05]
model_accepted=True

进行预测

service_2003_est_sum=service.forecast(target=len(service.x_0)+1)[len(service.x_0)]      #2003年预计均值
service_2003_est=12*service_2003_est_sum*service_share                                  #2003年预计月分布值

print(f'2003年预计服务业总值为:{service_2003_est_sum}')
print('按月分布如下:')
print(service_2003_est)
2003年预计服务业总值为:1144.0375568949603
按月分布如下:
[ 261.93899847  425.0814399   594.7723563   811.44146264  999.06950611
 1200.93336471 1435.82431443 1654.48643489 1864.03763366 2079.85259109
 2401.01258052]

和实际值进行比较

#时间横轴
x=['FEB','MAR','APR','MAY','JUN','JUL','AUG','SEPT','OCT','NOV','DEC']

#数据纵轴
y1=service_2003_est              #预测值
y2=service_2003                  #实际值

plt.figure(figsize=(16,8),dpi=80)
plt.title("Service Compared by Month")
plt.xlabel("Month")
plt.ylabel("Service")
plt.plot(x,y1,x,y2,marker='o')
plt.legend(labels=["Estimated Value","Real Value"],loc="lower right")  
for a,b,c in zip(x,y1,y2):
    plt.text(a,b,'%.1f'%b,ha='center',va='bottom')
    plt.text(a,c,'%.1f'%c,ha='center',va='bottom')    
plt.show()

模型的结果分析

根据该市的统计报告显示,2003年4、5、6三个月的实际商品零售额分别为145.2,124、144.1亿元.在这之前,根据统计部门的估计4、5、6三个月份SARS疫情对该市的商品零售业的影响最为严重,这三个月估计大约损失62亿元左右。从我们的模型预测结果来计算,4、5、6三个月的损失为60.3亿元,这个数据基本与专家的估算值相符,8月份基本恢复正常,这也说明了模型的正确性和可靠性。

对于旅游业来说是受影响最严重的行业之一,最严重的4、5、6、7四个月就损失100多万人,按最新统计数据,平均每人消费1002美元计算,大约损失10亿美元.全年大约损失160万人,约合16亿美元,到年底基本恢复正常。

对于综合服务业中的部分行业影响较大,如航空交通运输、宾馆餐饮等,但有些行业影响不大,如电信、通讯等,总平均来看,影响还不算太大,5、6、7、8四个月大约损失70亿元。

从预测结果可以看出,虽然下半年没有发生疫情,但人们一直担心SARS会卷土重来,所以,对这些行业还是有一定的影响,即SARS影响的延续性的作用。该模型虽是就某经济指标的发展规律进行评估预测而建立的,但类似的也适用于其他方面的一些数据规律的评估预测问题,即该模型具有很广泛的应用性。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇