一、前言
本文的目的是用Python和類對灰色預測進行封裝
二、原理簡述
1.灰色預測概述
灰色預測是用灰色模型GM(1,1)來進行定量分析的,通常分爲以下幾類:
(1) 灰色時間序列預測。用等時距觀測到的反映預測對象特徵的一系列數量(如產量、銷量、人口數量、存款數量、利率等)構造灰色預測模型,預測未來某一時刻的特徵量,或者達到某特徵量的時間。
(2) 畸變預測(災變預測)。通過模型預測異常值出現的時刻,預測異常值什麼時候出現在特定時區內。
(3) 波形預測,或稱爲拓撲預測,它是通過灰色模型預測事物未來變動的軌跡。
(4) 系統預測,對系統行爲特徵指標建立一族相互關聯的灰色預測理論模型,在預測系統整體變化的同時,預測系統各個環節的變化。
上述灰色預測方法的共同特點是:
(1)允許少數據預測;
(2)允許對灰因果律事件進行預測,例如:
灰因白果律事件:在糧食生產預測中,影響糧食生產的因子很多,多到無法枚舉,故爲灰因,然而糧食產量卻是具體的,故爲白果。糧食預測即爲灰因白果律事件預測。
白因灰果律事件:在開發項目前景預測時,開發項目的投入是具體的,爲白因,而項目的效益暫時不很清楚,爲灰果。項目前景預測即爲灰因白果律事件預測。
(3)具有可檢驗性,包括:建模可行性的級比檢驗(事前檢驗),建模精度檢驗(模型檢驗),預測的滾動檢驗(預測檢驗)。
2.GM(1,1)模型理論
GM(1,1)模型適合具有較強的指數規律的數列,只能描述單調的變化過程。已知元素序列數據:做一次累加生成(1-AGO)序列:
其中,
令爲的緊鄰均值生成序列:其中,建立GM(1,1)的灰微分方程模型爲:其中,爲發展係數,爲灰色作用量。設爲待估參數向量,即,則灰微分方程的最小二乘估計參數列滿足其中
再建立灰色微分方程的白化方程(也叫影子方程):
白化方程的解(也叫時間響應函數)爲那麼相應的GM(1,1)灰色微分方程的時間響應序列爲:取,則再做累減還原可得即爲預測方程。
注1:原始序列數據不一定要全部使用,相應建立的模型也會不同,即和不同;
注2:原始序列數據必須要等時間間隔、不間斷。
3.算法步驟
(1) 數據的級比檢驗
爲了保證灰色預測的可行性,需要對原始序列數據進行級比檢驗。
對原始數據列,計算序列的級比: 若所有的級比都落在可容覆蓋內,則可進行灰色預測;否則需要對做平移變換,,使得滿足級比要求。
(2) 建立GM(1,1)模型,計算出預測值列。
(3) 檢驗預測值:
① 相對殘差檢驗,計算 若 ,則認爲達到一般要求,若 ,則認爲達到較高要求;
② 級比偏差值檢驗
根據前面計算出來的級比, 和發展係數, 計算相應的級比偏差: 若, 則認爲達到一般要求,若, 則認爲達到較高要求。
(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函數