季節性時間序列分析-SARIMAX模型的python實現

0 SARIMAX模型時間序列分析步驟

1. 用pandas處理時序數據

2. 檢驗時序數據的平穩性

3. 將時序數據平穩化

4. 確定order 的 p.d.q值

5. 確定season_order的四個值

6. 應用SARIMAX模型對時序數據進行預測

其實SARIMAX比ARIMA模型就多了個season_order參數的確定,但也是這裏最費時間的一個步驟

1 將數據轉化成爲時序數據

先一股腦導入一下工具包

import pandas as pd
import datetime
import matplotlib.pyplot as plt
from pylab import mpl
mpl.rcParams['font.sans-serif']=['SimHei']
import seaborn as sns
import statsmodels.tsa.stattools as ts
import statsmodels.api as sm
from statsmodels.tsa.arima_model import ARIMA
from statsmodels.stats.diagnostic import unitroot_adf
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
import itertools
import warnings
import numpy as np
from statsmodels.tsa.seasonal import seasonal_decompose
#讀取數據
data = pd.read_csv('factor.csv')
data.index = pd.to_datetime(data['date'])
data.drop(['date'], axis=1, inplace=True)
data = data.result
data.head()
#數據大致情況展示
data.plot(figsize=(12,8))
plt.legend(bbox_to_anchor=(1.25, 0.5))
plt.title('result')
sns.despine()
plt.show()

 

2 序列平穩性檢測

#數據平穩性檢測 因爲只有平穩數據才能做時間序列分析
def judge_stationarity(data_sanya_one):
    dftest = ts.adfuller(data_sanya_one)
    print(dftest)
    dfoutput = pd.Series(dftest[0:4], index=['Test Statistic','p-value','#Lags Used','Number of Observations Used'])
    stationarity = 1
    for key, value in dftest[4].items():
        dfoutput['Critical Value (%s)'%key] = value 
        if dftest[0] > value:
                stationarity = 0
    print(dfoutput)
    print("是否平穩(1/0): %d" %(stationarity))
    return stationarity
stationarity = judge_stationarity(data)

3 序列平穩化 

#若不平穩進行一階差分
if stationarity == 0:
    data_diff = data.diff()
    data_diff = data_diff.dropna()
    plt.figure()
    plt.plot(data_diff)
    plt.title('一階差分')
    plt.show()

#再次進行平穩性檢測
stationarity = judge_stationarity(data_diff)

 4 做一下季節性分解看看有沒有季節性

#季節性分解
decomposition = seasonal_decompose(data,freq=28)
trend = decomposition.trend
seasonal = decomposition.seasonal
residual = decomposition.resid

plt.figure(figsize=[15, 7])
decomposition.plot()
print("test: p={}".format(ts.adfuller(seasonal)[1]))

#季節平穩性檢測
stationarity = judge_stationarity(residual)

 5 對order參數p、q定階,下面兩種都可以,但圖我還是看不來,下一種傻瓜式(稍微費時間)

#畫ACF圖和PACF圖來確定p、q值
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf

def draw_acf_pacf(ts,lags):
    f = plt.figure(facecolor='white')
    ax1 = f.add_subplot(211)
    plot_acf(ts,ax=ax1,lags=lags)  #lags 表示滯後的階數,值爲30,顯示30階的圖像
    ax2 = f.add_subplot(212)
    plot_pacf(ts,ax=ax2,lags=lags)  
    plt.subplots_adjust(hspace=0.5)
    plt.show()
draw_acf_pacf(myts_diff,30)
#對模型p,q進行定階
warnings.filterwarnings("ignore") # specify to ignore warning messages
from statsmodels.tsa.arima_model import ARIMA 

pmax = int(5)    #一般階數不超過 length /10
qmax = int(5)
bic_matrix = []
for p in range(pmax +1):
    temp= []
    for q in range(qmax+1):
        try:
            temp.append(ARIMA(data, (p, 1, q)).fit().bic)
        except:
            temp.append(None)
        bic_matrix.append(temp)
 
bic_matrix = pd.DataFrame(bic_matrix)   #將其轉換成Dataframe 數據結構
p,q = bic_matrix.stack().idxmin()   #先使用stack 展平, 然後使用 idxmin 找出最小值的位置
print(u'BIC 最小的p值 和 q 值:%s,%s' %(p,q))  #  BIC 最小的p值 和 q 值:0,1

6  通過網格搜索對seasonal_order進行定階

#通過網格搜索對seasonal_order進行定階,目前就是pdq=011,seasonal_order=2, 2, 1, 52效果比較好,RMSE=202.4582
def get_ARIMA_params(data, pdq, m=12):
    p = d = q = range(0, 3)
    seasonal_pdq = [(x[0], x[1], x[2], m) for x in list(itertools.product(p, d, q))]
    score_aic = 1000000.0
    warnings.filterwarnings("ignore") # specify to ignore warning messages
    for param_seasonal in seasonal_pdq:
        mod = sm.tsa.statespace.SARIMAX(data,
                                        order=pdq,
                                        seasonal_order=param_seasonal,
                                        enforce_stationarity=False,
                                        enforce_invertibility=False)
        results = mod.fit()
        print('x{}12 - AIC:{}'.format(param_seasonal, results.aic))
        if results.aic < score_aic:
            score_aic = results.aic
            params = param_seasonal, results.aic
    param_seasonal, results.aic = params
    print('x{}12 - AIC:{}'.format(param_seasonal, results.aic))
pdq = [0, 1, 1]
get_ARIMA_params(data, pdq, m=52)

這上面最關鍵的是這個m值怎麼設定,我設置默認爲12。m是季節週期,參考別人代碼的時候,月度數據的m爲12,那我這裏以周爲單位,季節週期應該是52吧。一年12個月,52個星期,這個邏輯應該沒有問題。

 7  根據定階參數進行模型擬合

mod = sm.tsa.statespace.SARIMAX(data,
                                order=(0, 1, 1),
                                seasonal_order=(2, 1, 2, 52),
                                enforce_stationarity=False,
                                enforce_invertibility=False)
results = mod.fit()
print(results.summary().tables[1])
results.plot_diagnostics(figsize=(15, 12))
plt.show()

這裏模型擬合的時候用的數據的原始數據,而不是差分後的數據,因爲order參數中已經設置了d爲1,在擬合的時候會自動進行一階差分,並在預測的時候對預測結果進行差分還原。

8  對預測值和真實值作圖,並計算RMSE值作爲評估參數

predict_ts = results.predict(tpy='levels')  #tpy='levels'直接預測值,沒有的話預測的是差值
myts = data[predict_ts.index]  # 過濾沒有預測的記錄

predict_ts.plot(color='blue', label='Predict',figsize=(12,8))

myts.plot(color='red', label='Original',figsize=(12,8))

plt.legend(loc='best')
plt.title('RMSE: %.4f'% np.sqrt(sum((predict_ts-myts)**2)/myts.size))
plt.show()

 

9 向後對forecast值作圖

steps = 20
start_time = myts.index[-1]
forecast_ts = results.forecast(steps)
 
fore = pd.DataFrame()
fore['date'] = pd.date_range(start=start_time ,periods=steps, freq='7D')
fore['result'] = pd.DataFrame(forecast_ts.values)
fore.index = pd.to_datetime(fore['date'])
 
predict_ts['2019/1/18':].plot(color='blue', label='Predict',figsize=(12,8))
myts['2019/1/18':].plot(color='red', label='Original',figsize=(12,8))
fore.result.plot(color='black', label='forecast',figsize=(12,8))

plt.legend(loc='best')
plt.show()

這裏有個函數pd.date_range是專門用於產生時間序列索引的,start = 開始時間,end = 結束時間,periods=時間索引的個數,freq=‘7D’表示7天爲一個時間索引間隔,也可以是'7W'七週,'M'一個月等等。

由於預測的數據沒有時間索引,只有序號所以我要在這給他生成時間索引,併合併到dataframe,這樣就可以和其他值一起在圖像上展示了。

 

最後forecast的效果還是可以的嘛,保存forecast文件

fore.to_csv('forecast_20steps.csv')

 

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