Python時間序列數據分析--以示例說明


向AI轉型的程序員都關注了這個號👇👇👇html

人工智能大數據與深度學習  公衆號: datayxpython


本文的內容主要來源於博客:
https://www.analyticsvidhya.com/blog/2016/02/time-series-forecasting-codes-python/ 英文不錯的讀者能夠前去閱讀原文。git



在閱讀本文以前 ,推薦先閱讀:


時間序列預測之--ARIMA模型

http://www.cnblogs.com/bradleon/p/6827109.html


本文主要分爲四個部分:程序員

  1. 用pandas處理時序數據github

  2. 怎樣檢查時序數據的穩定性算法

  3. 怎樣讓時序數據具備穩定性微信

  4. 時序數據的預測機器學習

1. 用pandas導入和處理時序數據

第一步:導入經常使用的庫函數

import pandas as pd
import numpy as np
import matplotlib.pylab as plt
from matplotlib.pylab
import rcParams
#rcParams設定好畫布的大小

rcParams['figure.figsize'] = 15, 6

第二步:導入時序數據
數據文件可在github:post

關注微信公衆號 datayx  而後 回覆 「時序」 便可獲取。


data = pd.read_csv(path+"AirPassengers.csv")
print data.head()
print '\n Data types:'
print data.dtypes


運行結果以下:數據包括每月對應的passenger的數目。
能夠看到data已是一個DataFrame,包含兩列Month和#Passengers,其中Month的類型是object,而index是0,1,2...


第三步:處理時序數據
咱們須要將Month的類型變爲datetime,同時做爲index。

dateparse = lambda dates: pd.datetime.strptime(dates, '%Y-%m')
#---其中parse_dates 代表選擇數據中的哪一個column做爲date-time信息,

#---index_col 告訴pandas以哪一個column做爲 index

#--- date_parser 使用一個function(本文用lambda表達式代替),使一個string轉換爲一個datetime變量

data = pd.read_csv('AirPassengers.csv', parse_dates=['Month'], index_col='Month',date_parser=dateparse)
print data.head()
print data.index


結果以下:能夠看到data的index已經變成datetime類型的Month了。

2.怎樣檢查時序數據的穩定性(Stationarity)

由於ARIMA模型要求數據是穩定的,因此這一步相當重要。

1. 判斷數據是穩定的常基於對於時間是常量的幾個統計量:

  1. 常量的均值

  2. 常量的方差

  3. 與時間獨立的自協方差

用圖像說明以下:

  1. 均值

    X是時序數據的值,t是時間。能夠看到左圖,數據的均值對於時間軸來講是常量,即數據的均值不是時間的函數,全部它是穩定的;右圖隨着時間的推移,數據的值總體趨勢是增長的,全部均值是時間的函數,數據具備趨勢,因此是非穩定的。

  2. 方差

    能夠看到左圖,數據的方差對於時間是常量,即數據的值域圍繞着均值上下波動的振幅是固定的,因此左圖數據是穩定的。而右圖,數據的振幅在不一樣時間點不一樣,因此方差對於時間不是獨立的,數據是非穩定的。可是左、右圖的均值是一致的。


  3. 自協方差

    一個時序數據的自協方差,就是它在不一樣兩個時刻i,j的值的協方差。能夠看到左圖的自協方差於時間無關;而右圖,隨着時間的不一樣,數據的波動頻率明顯不一樣,致使它i,j取值不一樣,就會獲得不一樣的協方差,所以是非穩定的。雖然右圖在均值和方差上都是與時間無關的,但還是非穩定數據。


2. python判斷時序數據穩定性

有兩種方法:
1.Rolling statistic-- 即每一個時間段內的平均的數據均值和標準差狀況。

  1. Dickey-Fuller Test -- 這個比較複雜,大體意思就是在必定置信水平下,對於時序數據假設 Null hypothesis: 非穩定。
    if 經過檢驗值(statistic)< 臨界值(critical value),則拒絕null hypothesis,即數據是穩定的;反之則是非穩定的。

from statsmodels.tsa.stattools import adfuller
def test_stationarity(timeseries):        #這裏以一年爲一個窗口,每個時間t的值由它前面12個月(包括本身)的均值代替,標準差同理。    rolmean = pd.rolling_mean(timeseries,window=12)    rolstd = pd.rolling_std(timeseries, window=12)        #plot rolling statistics:    fig = plt.figure()    fig.add_subplot()    orig = plt.plot(timeseries, color = 'blue',label='Original')    mean = plt.plot(rolmean , color = 'red',label = 'rolling mean')    std = plt.plot(rolstd, color = 'black', label= 'Rolling standard deviation')        plt.legend(loc = 'best')    plt.title('Rolling Mean & Standard Deviation')    plt.show(block=False)            #Dickey-Fuller test:        print 'Results of Dickey-Fuller Test:'    dftest = adfuller(timeseries,autolag = 'AIC')    #dftest的輸出前一項依次爲檢測值,p值,滯後數,使用的觀測數,各個置信度下的臨界值    dfoutput = pd.Series(dftest[0:4],index = ['Test Statistic','p-value','#Lags Used','Number of Observations Used'])    
for key,value in dftest[4].items():        dfoutput['Critical value (%s)' %key] = value        print dfoutput     ts = data['#Passengers'] test_stationarity(ts)


結果以下:


能夠看到,數據的rolling均值/標準差具備愈來愈大的趨勢,是不穩定的。
且DF-test能夠明確的指出,在任何置信度下,數據都不是穩定的。


3. 讓時序數據變成穩定的方法

讓數據變得不穩定的緣由主要有倆:

  1. 趨勢(trend)-數據隨着時間變化。好比說升高或者下降。

  2. 季節性(seasonality)-數據在特定的時間段內變更。好比說節假日,或者活動致使數據的異常。

因爲原數據值域範圍比較大,爲了縮小值域,同時保留其餘信息,經常使用的方法是對數化,取log。

ts_log = np.log(ts)

檢測和去除趨勢
一般有三種方法:

    • 聚合 : 將時間軸縮短,以一段時間內星期/月/年的均值做爲數據值。使不一樣時間段內的值差距縮小。

    • 平滑: 以一個滑動窗口內的均值代替原來的值,爲了使值之間的差距縮小

    • 多項式過濾:用一個迴歸模型來擬合現有數據,使得數據更平滑。

本文主要使用平滑方法

Moving Average--移動平均

moving_avg = pd.rolling_mean(ts_log,12)
plt.plot(ts_log ,color = 'blue')
plt.plot(moving_avg, color='red')



能夠看出moving_average要比原值平滑許多。

而後做差:

ts_log_moving_avg_diff = ts_log-moving_avg
ts_log_moving_avg_diff.dropna(inplace = True)
test_stationarity(ts_log_moving_avg_diff)



能夠看到,作了處理以後的數據基本上沒有了隨時間變化的趨勢,DFtest的結果告訴咱們在95%的置信度下,數據是穩定的。

上面的方法是將全部的時間平等看待,而在許多狀況下,能夠認爲越近的時刻越重要。因此引入指數加權移動平均-- Exponentially-weighted moving average.(pandas中經過ewma()函數提供了此功能。)

# halflife的值決定了衰減因子alpha:  alpha = 1 - exp(log(0.5) / halflife)expweighted_avg = pd.ewma(ts_log,halflife=12)
ts_log_ewma_diff = ts_log - expweighted_avg
test_stationarity(ts_log_ewma_diff)



能夠看到相比普通的Moving Average,新的數據平均標準差更小了。並且DFtest能夠獲得結論:數據在99%的置信度上是穩定的。

  1. 檢測和去除季節性
    有兩種方法:

    • 1 差分化: 以特定滯後數目的時刻的值的做差

    • 2 分解: 對趨勢和季節性分別建模在移除它們

Differencing--差分

ts_log_diff = ts_log - ts_log.shift()
ts_log_diff.dropna(inplace=True)
test_stationarity(ts_log_diff)


如圖,能夠看出相比MA方法,Differencing方法處理後的數據的均值和方差的在時間軸上的振幅明顯縮小了。DFtest的結論是在90%的置信度下,數據是穩定的。


3.Decomposing-分解

#分解(decomposing) 能夠用來把時序數據中的趨勢和週期性數據都分離出來:

from statsmodels.tsa.seasonal import seasonal_decompose
def decompose(timeseries):        # 返回包含三個部分 trend(趨勢部分) , seasonal(季節性部分) 和residual (殘留部分)    decomposition = seasonal_decompose(timeseries)        trend = decomposition.trend    seasonal = decomposition.seasonal    residual = decomposition.resid        plt.subplot(411)    plt.plot(ts_log, label='Original')    plt.legend(loc='best')    plt.subplot(412)    plt.plot(trend, label='Trend')    plt.legend(loc='best')    plt.subplot(413)    plt.plot(seasonal,label='Seasonality')    plt.legend(loc='best')    plt.subplot(414)    plt.plot(residual, label='Residuals')    plt.legend(loc='best')    plt.tight_layout()        return trend , seasonal, residual


如圖能夠明顯的看到,將original數據 拆分紅了三份。Trend數據具備明顯的趨勢性,Seasonality數據具備明顯的週期性,Residuals是剩餘的部分,能夠認爲是去除了趨勢和季節性數據以後,穩定的數據,是咱們所須要的。

#消除了trend 和seasonal以後,只對residual部分做爲想要的時序數據進行處理trend , seasonal, residual = decompose(ts_log)
residual.dropna(inplace=True)
test_stationarity(residual)



如圖所示,數據的均值和方差趨於常數,幾乎無波動(看上去比以前的陡峭,可是要注意他的值域只有[-0.05,0.05]之間),因此直觀上能夠認爲是穩定的數據。另外DFtest的結果顯示,Statistic值原小於1%時的Critical value,因此在99%的置信度下,數據是穩定的。


4. 對時序數據進行預測

假設通過處理,已經獲得了穩定時序數據。接下來,咱們使用ARIMA模型
對數據已經預測。ARIMA的介紹能夠見本目錄下的另外一篇文章。

step1: 經過ACF,PACF進行ARIMA(p,d,q)的p,q參數估計

由前文Differencing部分已知,一階差分後數據已經穩定,因此d=1。
因此用一階差分化的ts_log_diff = ts_log - ts_log.shift() 做爲輸入。
等價於