灰色預測模型

原文鏈接:https://www.jianshu.com/p/a35ba96d852b

一、前言

  本文的目的是用Python和類對灰色預測進行封裝

二、原理簡述

1.灰色預測概述

  灰色預測是用灰色模型GM(1,1)來進行定量分析的,通常分爲以下幾類:
    (1) 灰色時間序列預測。用等時距觀測到的反映預測對象特徵的一系列數量(如產量、銷量、人口數量、存款數量、利率等)構造灰色預測模型,預測未來某一時刻的特徵量,或者達到某特徵量的時間。
    (2) 畸變預測(災變預測)。通過模型預測異常值出現的時刻,預測異常值什麼時候出現在特定時區內。
    (3) 波形預測,或稱爲拓撲預測,它是通過灰色模型預測事物未來變動的軌跡。
    (4) 系統預測,對系統行爲特徵指標建立一族相互關聯的灰色預測理論模型,在預測系統整體變化的同時,預測系統各個環節的變化。
  上述灰色預測方法的共同特點是:
    (1)允許少數據預測;
    (2)允許對灰因果律事件進行預測,例如:
      灰因白果律事件:在糧食生產預測中,影響糧食生產的因子很多,多到無法枚舉,故爲灰因,然而糧食產量卻是具體的,故爲白果。糧食預測即爲灰因白果律事件預測。
      白因灰果律事件:在開發項目前景預測時,開發項目的投入是具體的,爲白因,而項目的效益暫時不很清楚,爲灰果。項目前景預測即爲灰因白果律事件預測。
    (3)具有可檢驗性,包括:建模可行性的級比檢驗(事前檢驗),建模精度檢驗(模型檢驗),預測的滾動檢驗(預測檢驗)。

2.GM(1,1)模型理論

  GM(1,1)模型適合具有較強的指數規律的數列,只能描述單調的變化過程。已知元素序列數據:x^{(0)}=(X^{(0)}(1),X^{(0)}(2),...,X^{(0)}(n))做一次累加生成(1-AGO)序列:x^{(1)}=(X^{(1)}(1),X^{(1)}(2),...,X^{(1)}(n))
其中,x^{(1)}(k)=\sum_{i=1}^kx^{(0)}(i), \quad k=1,...,n

Z^{(1)}X^{(1)}的緊鄰均值生成序列:Z^{(1)}=(z^{(1)}(2), z^{(1)}(3), ...,z^{(1)}(n))其中,z^{(1)}(k)=0.5x^{(1)}(k)+0.5x^{(1)}(k-1)建立GM(1,1)的灰微分方程模型爲:x^{(0)}(k)+az^{(1)}(k)=b其中,a爲發展係數,b爲灰色作用量。設\hat{a}爲待估參數向量,即\hat{a}=(a,b)^T,則灰微分方程的最小二乘估計參數列滿足\hat{a}=(B^TB)^{-1}B^TY_n其中B=\left[ \begin{matrix} -z^{(1)}(2) , 1 \\ -z^{(1)}(3) , 1 \\ ... , ... \\ -z^{(1)}(n) , 1 \end{matrix}\right], Y_n=\left[ \begin{matrix} x^{(0)}(2)\\ x^{(0)}(3) \\ ... , \\ x^{(0)}(n) \end{matrix}\right]
再建立灰色微分方程的白化方程(也叫影子方程):\frac{dx^{(1)}}{dt}+ax^{(1)}=b

白化方程的解(也叫時間響應函數)爲\hat{x}^{(1)}(t)=(x^{(1)}(0)-\frac{b}{a})e^{-at}+\frac{b}{a}那麼相應的GM(1,1)灰色微分方程的時間響應序列爲:\hat{x}^{(1)}(k+1) = [x^{(1)}(0)-\frac{b}{a}]e^{-at}+\frac{b}{a}x^{(1)}(0)=x^{(0)}(1),則\hat{x}^{(1)}(k+1)=[x^{(0)}(1)-\frac{b}{a}]e^{-ak}+\frac{b}{a},\quad k=1,....n-1再做累減還原可得\hat{x}^{(0)}(k+1)=\hat{x}^{(1)}(k+1)-\hat{x}^{(1)}(k)=[x^{(0)}(1)-\frac{b}{a}](1-e^a)e^{-ak}, \quad k=1,...,n-1即爲預測方程。

注1:原始序列數據不一定要全部使用,相應建立的模型也會不同,即ab不同;

注2:原始序列數據必須要等時間間隔、不間斷。

3.算法步驟

(1) 數據的級比檢驗
  爲了保證灰色預測的可行性,需要對原始序列數據進行級比檢驗。
  對原始數據列X^{(0)}=(x^{(0)}(1),x^{(0)}(2),...,x^{(0)}(3)),計算序列的級比:\lambda(k)=\frac{x^{0}(k-1)}{x^{(0)}(k)}, \quad k=2,...,n  若所有的級比\lambda(k)都落在可容覆蓋\Theta=(e^{-2/(n+1)},e^{2/(n+2)})內,則可進行灰色預測;否則需要對X^{(0)}做平移變換,Y^{(0)}=X^{(0)}+c,使得Y^{(0)}滿足級比要求。

(2) 建立GM(1,1)模型,計算出預測值列。

(3) 檢驗預測值:

① 相對殘差檢驗,計算\varepsilon(k)=\frac{x^{(0)}(k-1)}{x^{(0)}(k)}, \quad k=2,...,n  若\varepsilon(k)<0.2 ,則認爲達到一般要求,若\varepsilon(k)<0.1 ,則認爲達到較高要求;

② 級比偏差值檢驗

  根據前面計算出來的級比\lambda(k), 和發展係數a, 計算相應的級比偏差:\rho(k)=1-(\frac{1-0.5a}{1+0.5a})]\lambda(k)  若\rho(k)<0.2, 則認爲達到一般要求,若\rho(k)<0.1, 則認爲達到較高要求。

(4) 利用模型進行預測。

三、程序實現

1.引入需要的包

import pandas as pd
import numpy as np

%matplotlib inline
import matplotlib.pyplot as plt
import matplotlib
matplotlib.rcParams['font.sans-serif'] = ['FangSong'] # 指定默認字體
matplotlib.rcParams['axes.unicode_minus'] = False # 解決保存圖像是負號'-'顯示爲方塊的問題

  下面的matplotlib是作圖用的,如果不做圖可以不用引入

2.構建大致框架

  灰色建模的主體應該分爲幾步:
    (1)引入、整理數據
    (2)校驗數據是否合格
    (3)GM(1,1)建模
    (4)將最新的預測數據當做真實值繼續預測
    (5)輸出結果
  因此打好的框架如下:

class GrayForecast():
#初始化
    def __init__(self, data, datacolumn=None):
        pass
#級比校驗
    def level_check(self):
        pass
#GM(1,1)建模
    def GM_11_build_model(self, forecast=5):
        pass
#預測
    def forecast(self, time=5, forecast_data_len=5):
        pass
#打印日誌
    def log(self):
        pass
#重置
    def reset(self):
        pass
#作圖
    def plot(self):
        pass

3.__init__

  作爲初始化的方法,我們希望它能將數據格式化存儲,並且可使用的類型越多越好,在這裏我先實現能處理三種類型:一維列表、DataFrame、Series。如果處理DataFrame可能會出現不止一維的情況,於是設定一個參數datacolumn,用於處理傳入DataFrame不止一列數據到底用哪個的問題

def __init__(self, data, datacolumn=None):

    if isinstance(data, pd.core.frame.DataFrame):
        self.data=data
        try:
            self.data.columns = ['數據']
        except:
            if not datacolumn:
                raise Exception('您傳入的dataframe不止一列')
            else:
                self.data = pd.DataFrame(data[datacolumn])
                self.data.columns=['數據']
    elif isinstance(data, pd.core.series.Series):
        self.data = pd.DataFrame(data, columns=['數據'])
    else:
        self.data = pd.DataFrame(data, columns=['數據'])

    self.forecast_list = self.data.copy()

    if datacolumn:
        self.datacolumn = datacolumn
    else:
        self.datacolumn = None
    #save arg:
    #        data                DataFrame    數據
    #        forecast_list       DataFrame    預測序列
    #        datacolumn          string       數據的含義

4.level_check

  按照級比校驗的步驟進行,最終返回是否成功的bool類型值

def level_check(self):
    # 數據級比校驗
    n = len(self.data)
    lambda_k = np.zeros(n-1)
    for i in range(n-1):
        lambda_k[i] = self.data.ix[i]["數據"]/self.data.ix[i+1]["數據"]
        if lambda_k[i] < np.exp(-2/(n+1)) or lambda_k[i] > np.exp(2/(n+2)):
            flag = False
    else:
        flag = True

    self.lambda_k = lambda_k

    if not flag:
        print("級比校驗失敗,請對X(0)做平移變換")
        return False
    else:
        print("級比校驗成功,請繼續")
        return True

#save arg:
#        lambda_k            1-d list

5.GM_11_build_model

  按照GM(1,1)的步驟進行一次預測並增長預測序列(forecast_list)
  傳入的參數forecast爲使用forecast_list末尾數據的數量,因爲灰色預測爲短期預測,過多的數據反而會導致數據精準度變差

def GM_11_build_model(self, forecast=5):
    if forecast > len(self.data):
        raise Exception('您的數據行不夠')
    X_0 = np.array(self.forecast_list['數據'].tail(forecast))
#       1-AGO
    X_1 = np.zeros(X_0.shape)
    for i in range(X_0.shape[0]):
        X_1[i] = np.sum(X_0[0:i+1])
#       緊鄰均值生成序列
    Z_1 = np.zeros(X_1.shape[0]-1)
    for i in range(1, X_1.shape[0]):
        Z_1[i-1] = -0.5*(X_1[i]+X_1[i-1])

    B = np.append(np.array(np.mat(Z_1).T), np.ones(Z_1.shape).reshape((Z_1.shape[0], 1)), axis=1)
    Yn = X_0[1:].reshape((X_0[1:].shape[0], 1))

    B = np.mat(B)
    Yn = np.mat(Yn)
    a_ = (B.T*B)**-1 * B.T * Yn

    a, b = np.array(a_.T)[0]

    X_ = np.zeros(X_0.shape[0])
    def f(k):
        return (X_0[0]-b/a)*(1-np.exp(a))*np.exp(-a*(k))

    self.forecast_list.loc[len(self.forecast_list)] = f(X_.shape[0])

6.forecast

  預測函數只要調用GM_11_build_model就可以,傳入的參數time爲向後預測的次數,forecast_data_len爲每次預測所用末尾數據的條目數

def forecast(self, time=5, forecast_data_len=5):
    for i in range(time):
        self.GM_11_build_model(forecast=forecast_data_len)

7.log

  打印當前預測序列

def log(self):
    res = self.forecast_list.copy()
    if self.datacolumn:
        res.columns = [self.datacolumn]
    return res

8.reset

  初始化序列

def reset(self):
    self.forecast_list = self.data.copy()

9.plot

  作圖

def plot(self):
    self.forecast_list.plot()
    if self.datacolumn:
        plt.ylabel(self.datacolumn)
        plt.legend([self.datacolumn])

四、使用

  首先讀入數據,最近11年的電影票房(電影票房.csv):

年份,票房
2007,33.27
2008,43.41
2009,62.06
2010,101.72
2011,131.15
2012,170.73
2013,217.69
2014,296.39
2015,440.69
2016,457.12
2017,559.11
f = open("電影票房.csv", encoding="utf8")
df = pd.read_csv(f)
df.tail()

11條數據

 

  構建灰色預測對象,進行10年預測輸出結果並作圖

gf = GrayForecast(df, '票房')
gf.forecast(10)
gf.log()

原來的11條數據+10條預測結果

gf.plot()

全體數據作圖

五、總結

  我們看數據的後面幾條,高達數千萬,或許10年前我們也想不到現在的電影票房是曾經原來的20倍。
  這麼快的增長已經接近指數增長了,然而它很可能就像人口一樣,它並非是指數增長,而是沒有達到增速減少的閾值罷了,用灰色預測很難看到如此長遠的情況,或許可以將數據改爲用sigmoid函數擬合,或許能達到更加準確的結果。

 

sigmoid函數

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章