python時間序列數據預測教程之 arima

最近接觸時間序列較多,在借鑑很多人的知識之後,特此總結一下。目前關於時間序列數據分析預測大致有三種主流方法:
1、ARIMA系列方法
2、facebook開源的Prophet模型
3、LSTM時間序列預測

本系列希望在項目和實踐的角度,用python實現上述三種方法並做出對比總結。如有不足之處,感謝指出,虛心改正。
所需環境: win10/ubuntu均可,python3.6.x,pandas,numpy,tensorflow2.0,statsmodels,pmdarima…

(1)項目簡介
本文項目中所用數據爲近一段時間內,間隔30分鐘採樣的氣象數據(包括溫度、溼度、風速、風向等數據)。在本文的理解中,arima方法僅支持單變量預測,也就是需要單獨取出某列進行該列的預測。若想要多變量輸入多變量輸出,arima方法要擬合多個模型,再整合輸入輸出。
(2)數據介紹
數據是我從某氣象網站爬取到的氣象數據,已經做好數據清洗,提供一組測試數據放在文末,需要自取哈,舉例如下:

表中單位:溫度(華氏度),溼度%,風向可轉換角度,風速(mph)
讀取csv數據,數據清洗:

    def read_temperature():
        # 以首列的日期時間作爲時間戳 此處要轉換成pandas的DatetimeIndex格式
        data = pd.read_csv('data/test_2020.csv', usecols=[0, 1, 2, 3, 4])
        data['Date Time'] = pd.to_datetime(data['Date Time'])
        data.set_index('Date Time', inplace=True)
        data = data['Temperature']  # DatetimeIndex對象
        # 華氏度轉攝氏度
        for i in range(len(data)):
            data[i] = round(5 / 9 * (data[i] - 32))
        # 時間頻率重採樣
        data = data.resample('30T').mean()  # 更新頻率30分鐘,缺省值用臨近的後一值補充
        data = data.fillna(data.bfill())
		# 顯示數據
        data.plot(figsize=(15, 12))
        plt.show()
        return data

當前數據圖:

(3) 平穩性檢驗
arima時間序列數據預測首先要保證數據的平穩性,肉眼可以看到當前數據可能有一定上升趨勢。但還是要有數據量化這個平穩性,這裏我們使用ADF平穩性檢驗,基於statsmodels庫實現:

    def adf_stability_test(self, data):
        # ADF單位根檢驗:不存在單位根,表示數據平穩。且不能是白噪聲數據(隨機數)
        x = data
        res = ts.adfuller(x)
        lb_res = lb_test(x, None, True)[1]

        tag = False
        for i in range(len(lb_res)):
            if lb_res[i] < 0.05:
                continue
            else:
                print('序列爲白噪聲!')
                tag = True
                break
        if res[0] < res[4]['1%'] and res[0] < res[4]['5%'] and res[0] < res[4]['10%'] and res[1] < 0.05 and not tag:
            print('平穩序列!非白噪聲')
            # plt.plot(lb_test(x, None, True)[1])
            # plt.ylabel('p-Value')
            # plt.show()
            return True
        else:
            return False

adfuller方法返回值:

(-6.260374222209651, 4.237742412839035e-08, 5, 906, {'1%': -3.4375883271133243, '5%': -2.8647353885968214, '10%': -2.568471435365895}, 1615.9162778870964)

ADF單位根檢驗,如果序列平穩,就不存在單位根;否則,就會存在單位根。該檢驗先假設序列不平穩,存在單位根。如果得到的顯著性檢驗統計量小於三個置信度(10%,5%,1%),則對應有(90%,95,99%)的把握來拒絕原假設 ->序列平穩。
adfuller的返回值意義:

adf:Test statistic,T檢驗,假設檢驗值。
p-value:假設檢驗結果。
usedlag:使用的滯後階數。
nobs:用於ADF迴歸和計算臨界值用到的觀測值數目。
icbest:如果autolag不是None的話,返回最大的信息準則值。
resstore:將結果合併爲一個dummy。

具體原理我也不是很懂,只需知道adf值,也就是本文的-6.260374222209651 均小於(10%,5%,1%)三個置信度的值,且p-value < 0.05時能夠較好的拒絕原假設,表示序列平穩。
若序列不平穩,需要對序列連續差分,直到序列平穩,對差分後的序列進行預測,再將預測結果逆差分,得到我們需要的值。

    def stability_test(self):
        count = 0
        flag = False
        if not self.adf_stability_test(self.data):
            while not self.adf_stability_test(self.data):
                count += 1
                self.data = self.data.diff(count)			# 差分
                self.data = self.data.fillna(self.data.bfill())	# 缺省值用後一值補充
                flag = True
        print('經過{}次差分,序列平穩'.format(count)) 
        return count, flag

count是讓序列變平穩的差分次數,也就是ARIMA(p,d,q)中的d,記錄count值便於後面調pdq參數。flag記錄是否經過差分,便於後期作逆差分還原數據。
差分操作很容易理解:原數列的後一項減去前一項得到的一組差數列。
相當於序列整體後移,空出第一位是空NAN,再與原序列做差

0 1 2 3 4 5 6
3 6 7 8 9 10 12 
# 一階差分
0 	 1 2 3 4 5 6
NAN  3 1 1 1 1 2  

我們知道arima或者是seasonal arima最重要的是(p,d,q)和(P,D,Q,S)這四個參數的確定。
而相對準確的方法是計算acf,pacf圖,通過觀察自相關圖和偏相關圖,確定拖尾還是截尾等什麼鬼的…我到現在還似懂非懂。總之瞭解下來是要肉眼觀察參數,不能自動化調參…
但我們的數據是實時變化的,參數可能變動很大。自動化訓練參數很有必要:
(4) 模型參數訓練
目前瞭解兩種自動確定參數方法:
1)網格搜索的方法:
感覺就是暴力搜索,用一些模型評價標準確定最優的參數,計算量很大複雜度高。不在乎效率也值得一試,此處用AIC準則評價模型,找出AIC評分最小的模型參數

    def temp_param_optimization(self, data):
        paramBest = []
        warnings.filterwarnings("ignore")
        p = q = range(0, 3)		# 限制pq範圍
        pdq = [(x[0], self.d, x[1]) for x in list(itertools.product(p, q))]	# 此處的d我們已經得到
        seasonal_pdq = [(x[0], self.d, x[1], s) for x in list(itertools.product(p, q))]		# 此處的s是季節性參數,可根據數據指定,變化不大
        
        for param in pdq:
            for param_seasonal in seasonal_pdq:
                try:
                    mod = sm.tsa.statespace.SARIMAX(data,
                                                    order=param,
                                                    seasonal_order=param_seasonal,
                                                    enforce_stationarity=False,
                                                    enforce_invertibility=False)

                    results = mod.fit()
                    paramBest.append([param, param_seasonal, results.aic])
                    print('ARIMA{}x{}12 - AIC:{}'.format(param, param_seasonal, results.aic))
                except:
                    continue
        # print('paramBest:', paramBest)
        minAic = sys.maxsize
        for i in np.arange(len(paramBest)):
            if paramBest[i][2] < minAic:
                minAic = paramBest[i][2]
        # print("minAic:", minAic)
        for j in np.arange(len(paramBest)):
            if paramBest[j][2] == minAic:
                return paramBest[j][0], paramBest[j][1]

2)pmdarima
瞭解過時間序列發現,很多用R語言作時間序列預測的。因爲R提供自動訓練參數的庫auto_arima。也是在項目後期才發現python也有類似的開源庫pmdarima,貌似就是模仿R而來的。
直接pip install pmdarima -i https://pypi.tuna.tsinghua.edu.cn/simple
,pip安裝要記得換源

    def auto_parameters(self, data, s_num):
        kpss_diff = arima.ndiffs(data, alpha=0.05, test='kpss', max_d=s_num)
        adf_diff = arima.ndiffs(data, alpha=0.05, test='adf', max_d=s_num)
        d = max(kpss_diff, adf_diff)
        D = arima.nsdiffs(data, s_num)
        
        stepwise_model = auto_arima(data, start_p=1, start_q=1,
                                    max_p=9, max_q=9, max_d=3, m=s_num,
                                    seasonal=True, d=d, D=D, trace=True,
                                    error_action='ignore',
                                    suppress_warnings=True,
                                    stepwise=True)
        print("AIC: ", stepwise_model.aic())
        print(stepwise_model.order)		# (p,d,q)
        print(stepwise_model.seasonal_order)	# (P,D,Q,S)
        print(stepwise_model.summary())		# 詳細模型
        return stepwise_model.order, stepwise_model.seasonal_order

十分方便的庫,arima.ndiffs(),arima.nsdiffs()可以方便的求出最合適的d,和D參數。代碼中s_num是自己指定的季節性參數。

auto_arima(data, start_p=1, start_q=1,
		     max_p=9, max_q=9, max_d=3, m=s_num,
		     seasonal=True, d=d, D=D, trace=True,
		     error_action='ignore',
		     suppress_warnings=True,
		     stepwise=True)

start_p,start_q設置初始p,q參數,max_p,max_q最大值。stepwise=True貌似會直接選擇最優參數。參數還有很多,請查閱官方api:https://alkaline-ml.com/pmdarima/index.html
對比網格搜索的方法,本文覺得兩種方法確定的參數幾乎相同,但auto_arima訓練參數特別快。同樣的數據auto_arima要快一半以上。
(5)模型預測

    def model_prediction(self, data, param, s_param, n_steps, flag):
        mod = sm.tsa.statespace.SARIMAX(data,
                                        order=param,
                                        seasonal_order=s_param,
                                        enforce_stationarity=False,
                                        enforce_invertibility=False)
        results = mod.fit()

        pred_uc = results.get_forecast(steps=n_steps)		# n_steps可指定預測的步數(多少時間間隔)
        pred_ci = pred_uc.conf_int()

        if flag:  # 還原差分
            pred_res = pd.Series([data[0]], index=[data.index[0]]).append(pred_uc.predicted_mean).cumsum()
            print("預測結果(℃):  ", pred_res)
            return pred_res
        else:
            print("預測結果(℃):  ", pred_uc.predicted_mean)
            return pred_uc.predicted_mean

傳入參數param,s_param分別對應(pdq)(PDQS)
測試數據預測結果:


4.21更新
感覺對於數據的季節性參數設置可能有點迷。比如文中提到的網格搜索的參數s和auto_arima的參數m都是要自己設置的,當然要根據所用數據的頻率間隔來設置季節性。我的理解是數據規律的週期性呈現方式。比如,我用的是每30分鐘的觀測數據,一個月的數據。就可以設置爲日週期性30*48=1440,一天共48個間隔,m或者s參數設置爲48可能比較合適,但還是要針對數據具體分析。
如果各位同學可以科學上網的話,https://robjhyndman.com/hyndsight/seasonal-periods/ 這位博主關於季節性參數寫的比較好!
針對一般情況的設置:
在這裏插入圖片描述

測試數據:鏈接:測試數據
提取碼: 77rj
但有些數據單位需要轉換哈,像溫度是華氏度

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