轉載自:https://www.jianshu.com/p/cced6617b423
侵刪
1.異常值和缺失值的處理
這絕對是數據分析時讓所有人都頭疼的問題。異常和缺失值會破壞數據的分佈,並且干擾分析的結果,怎麼處理它們是一門大學問,而我根本還沒入門。
(1)異常值
3 ways to remove outliers from your data提供了關於如何對時間序列數據進行異常值檢測的方法,作者認爲移動中位數的方法最好,代碼如下:
from pandas import rolling_median
threshold = 3 #指的是判定一個點爲異常的閾值
df['pandas'] = rolling_median(df['u'], window=3, center=True).fillna(method='bfill').fillna(method='ffill')
#df['u']是原始數據,df['pandas'] 是求移動中位數後的結果,window指的是移動平均的窗口寬度
difference = np.abs(df['u'] - df['pandas'])
outlier_idx = difference > threshold
rolling_median
函數詳細說明參見pandas.rolling_median
(2)缺失值
缺失值在DataFrame中顯示爲nan
,它會導致ARMA無法擬合,因此一定要進行處理。
a.用序列的均值代替,這樣的好處是在計算方差時候不會受影響。但是連續幾個nan
即使這樣替代也會在差分時候重新變成nan
,從而影響擬合迴歸模型。
b.直接刪除。我在很多案例上看到這樣的做法,但是當一個序列中間的nan
太多時,我無法確定這樣的做法是否還合理。
2.平穩性檢驗
序列平穩性是進行時間序列分析的前提條件,主要是運用ADF檢驗。
from statsmodels.tsa.stattools import adfuller
def test_stationarity(timeseries):
dftest = adfuller(timeseries, autolag='AIC')
return dftest[1]
#此函數返回的是p值
adfuller
函數詳細說明參見statsmodels.tsa.stattools.adfuller
3.不平穩的處理
(1)對數處理。對數處理可以減小數據的波動,因此無論第1步檢驗出序列是否平穩,都最好取一次對數。關於爲什麼統計、計量學家都喜歡對數的原因,知乎上也有討論:在統計學中爲什麼要對變量取對數?
(2)差分。一般來說,非純隨機的時間序列經一階差分或者二階差分之後就會變得平穩。那差分幾階合理呢?我的觀點是:在保證ADF檢驗的p<0.01的情況下,階數越小越好**,否則會帶來樣本減少、還原序列麻煩、預測困難的問題。——這是我的直覺,還沒有查閱資料求證。基於這樣的想法,構造了選擇差分階數的函數:
def best_diff(df, maxdiff = 8):
p_set = {}
for i in range(0, maxdiff):
temp = df.copy() #每次循環前,重置
if i == 0:
temp['diff'] = temp[temp.columns[1]]
else:
temp['diff'] = temp[temp.columns[1]].diff(i)
temp = temp.drop(temp.iloc[:i].index) #差分後,前幾行的數據會變成nan,所以刪掉
pvalue = test_stationarity(temp['diff'])
p_set[i] = pvalue
p_df = pd.DataFrame.from_dict(p_set, orient="index")
p_df.columns = ['p_value']
i = 0
while i < len(p_df):
if p_df['p_value'][i]<0.01:
bestdiff = i
break
i += 1
return bestdiff
(3)平滑法。利用移動平均的方法來處理數據,可能可以用來處理週期性因素,我還沒實踐過。
(4)分解法。將時間序列分解成長期趨勢、季節趨勢和隨機成分,同樣沒實踐過。
對於(3)(4),參見《python時間序列分析》或者Complete guide to create a Time Series Forecast (with Codes in Python)【翻譯版《時間序列預測全攻略(附帶Python代碼)》】
4.隨機性檢驗
只有時間序列不是一個白噪聲(純隨機序列)的時候,該序列纔可做分析。(參考自:《時間序列ARIMA模型詳解:python實現店鋪一週銷售量預測》)
from statsmodels.stats.diagnostic import acorr_ljungbox
def test_stochastic(ts):
p_value = acorr_ljungbox(ts, lags=1)[1] #lags可自定義
return p_value
acorr_ljungbox
函數詳細說明參見statsmodels.stats.diagnostic.acorr_ljungbox
5.確定ARMA的階數
ARMA(p,q)是AR(p)和MA(q)模型的組合,關於p和q的選擇,一種方法是觀察自相關圖ACF和偏相關圖PACF, 另一種方法是通過藉助AIC、BIC統計量自動確定。由於我有幾千個時間序列需要分別預測,所以選取自動的方式,而BIC可以有效應對模型的過擬合,因而選定BIC作爲判斷標準。
from statsmodels.tsa.arima_model import ARMA
def proper_model(data_ts, maxLag):
init_bic = float("inf")
init_p = 0
init_q = 0
init_properModel = None
for p in np.arange(maxLag):
for q in np.arange(maxLag):
model = ARMA(data_ts, order=(p, q))
try:
results_ARMA = model.fit(disp=-1, method='css')
except:
continue
bic = results_ARMA.bic
if bic < init_bic:
init_p = p
init_q = q
init_properModel = results_ARMA
init_bic = bic
return init_bic, init_p, init_q, init_properModel
這個函數的原理是,根據設定的maxLag
,通過循環輸入p和q值,選出擬合後BIC最小的p、q值。
然而在statsmodels
包裏還有更直接的函數:
import statsmodels.tsa.stattools as st
order = st.arma_order_select_ic(timeseries,max_ar=5,max_ma=5,ic=['aic', 'bic', 'hqic'])
order.bic_min_order
timeseries
是待輸入的時間序列,是pandas.Series
類型,max_ar
、max_ma
是p、q值的最大備選值。order.bic_min_order
返回以BIC準則確定的階數,是一個tuple
類型
6.擬合ARAM
from statsmodels.tsa.arima_model import ARMA
model = ARMA(timeseries, order=order.bic_min_order)
result_arma = model.fit(disp=-1, method='css')
ARMA
函數詳細說明參見statsmodels.tsa.arima_model.ARMA,.fit
參見statsmodels.tsa.arima_model.ARMA.fit
對於差分後的時間序列,運用於ARMA時該模型就被稱爲ARMIA,在代碼層面改寫爲model = ARIMA(timeseries, order=(p,d,q))
,但是實際上,用差分過的序列直接進行ARMA建模更方便,之後添加一步還原的操作即可。
7.預測的y值還原
從前可知,放入模型進行擬合的數據是經過對數或(和)差分處理的數據,因而擬合得到的預測y值要經過差分和對數還原纔可與原觀測值比較。
暫時寫了對數處理過的還原:
def predict_recover(ts):
ts = np.exp(ts)
return ts
8.判定擬合優度
在我學習計量經濟學的時候,判斷一個模型擬合效果是用一個調整R方的指標,但是似乎在機器學習領域,迴歸時常用RMSE(Root Mean Squared Error,均方根誤差),可能是因爲調整R方衡量的預測值與均值之間的差距,而RMSE衡量的是每個預測值與實際值的差距。《均方根值(RMS)+ 均方根誤差(RMSE)+標準差(Standard Deviation)》、《(轉)SSE,MSE,RMSE,R-square指標講解》提供了詳細公式。
train_predict = result_arma.predict()
train_predict = predict_recover(train_predict) #還原
RMSE = np.sqrt(((train_predict-timeseries)**2).sum()/timeseries.size)
9.預測未來的值
用statsmodel
這個包來進行預測,很奇怪的是我從來沒成功過,只能進行下一步(之後一天)的預測,多天的就無法做到了。可以啊,根據How to Create an ARIMA Model for Time Series Forecasting with Python,輸入前100天的數據,則可以預測任意時間長度的值,只需要
predict_ts = result_arma.predict(101,110,...)
predict_ts = result_arma.predict()
predict
方法詳細說明參見statsmodels.tsa.arima_model.ARMAResults.predict,反正我不太懂這個方法怎麼使用……
還有根據How to Create an ARIMA Model for Time Series Forecasting with Python,用來預測的代碼是:
for t in range(len(test)):
model = ARIMA(history, order=(5,1,0))
model_fit = model.fit(disp=0)
output = model_fit.forecast()
yhat = output[0]
predictions.append(yhat)
obs = test[t]
history.append(obs)
print('predicted=%f, expected=%f' % (yhat, obs))
然而我真的不懂,按他寫forecast
方法的方式,每次循環預測的都是history樣本的下一個值,因而如何用這個循環來預測history樣本的之後,比如十個值?如果不用循環,直接令forecast
中的參數steps=
爲要預測的時長,我也沒成功……forecast
方法詳細說明參見statsmodels.tsa.arima_model.ARIMAResults.forecast,
此外,Stackoverflow上的一個解答:ARMA out-of-sample prediction with statsmodels,又給了一個預測的寫法。
10. 更方便的時間序列包:pyflux
好在《AR、MA及ARMA模型》提到了python的另一個包pyflux
,它的文檔在PyFlux 0.4.0 documentation。這個包在macOS上安裝之前需要安裝XCode命令行工具:
xcode-select --install
同時它的畫圖需要安裝一個seaborn
的包(如果沒有Anaconda則用pip
的方式。《數據可視化(三)- Seaborn簡易入門》簡要介紹了seaborn
,它是“在matplotlib的基礎上進行了更高級的API封裝”。
conda install seaborn
我用這個包寫了一個簡略而完整的ARIMA建模:
# -*- coding: utf-8 -*-
"""
Created on Tue Jan 31 14:13:58 2017
@author: 竹間爲簡
@published in: 簡書
"""
import pandas as pd
from statsmodels.tsa.stattools import adfuller
import statsmodels.tsa.stattools as st
import numpy as np
import pyflux as pf
daily_payment = pd.read_csv('xxx.csv',parse_dates=[0], index_col=0)
def test_stationarity(timeseries):
dftest = adfuller(timeseries, autolag='AIC')
return dftest[1]
def best_diff(df, maxdiff = 8):
p_set = {}
for i in range(0, maxdiff):
temp = df.copy() #每次循環前,重置
if i == 0:
temp['diff'] = temp[temp.columns[1]]
else:
temp['diff'] = temp[temp.columns[1]].diff(i)
temp = temp.drop(temp.iloc[:i].index) #差分後,前幾行的數據會變成nan,所以刪掉
pvalue = test_stationarity(temp['diff'])
p_set[i] = pvalue
p_df = pd.DataFrame.from_dict(p_set, orient="index")
p_df.columns = ['p_value']
i = 0
while i < len(p_df):
if p_df['p_value'][i]<0.01:
bestdiff = i
break
i += 1
return bestdiff
def produce_diffed_timeseries(df, diffn):
if diffn != 0:
df['diff'] = df[df.columns[1]].apply(lambda x:float(x)).diff(diffn)
else:
df['diff'] = df[df.columns[1]].apply(lambda x:float(x))
df.dropna(inplace=True) #差分之後的nan去掉
return df
def choose_order(ts, maxar, maxma):
order = st.arma_order_select_ic(ts, maxar, maxma, ic=['aic', 'bic', 'hqic'])
return order.bic_min_order
def predict_recover(ts, df, diffn):
if diffn != 0:
ts.iloc[0] = ts.iloc[0]+df['log'][-diffn]
ts = ts.cumsum()
ts = np.exp(ts)
# ts.dropna(inplace=True)
print('還原完成')
return ts
def run_aram(df, maxar, maxma, test_size = 14):
data = df.dropna()
data['log'] = np.log(data[data.columns[0]])
# test_size = int(len(data) * 0.33)
train_size = len(data)-int(test_size)
train, test = data[:train_size], data[train_size:]
if test_stationarity(train[train.columns[1]]) < 0.01:
print('平穩,不需要差分')
else:
diffn = best_diff(train, maxdiff = 8)
train = produce_diffed_timeseries(train, diffn)
print('差分階數爲'+str(diffn)+',已完成差分')
print('開始進行ARMA擬合')
order = choose_order(train[train.columns[2]], maxar, maxma)
print('模型的階數爲:'+str(order))
_ar = order[0]
_ma = order[1]
model = pf.ARIMA(data=train, ar=_ar, ma=_ma, target='diff', family=pf.Normal())
model.fit("MLE")
test = test['payment_times']
test_predict = model.predict(int(test_size))
test_predict = predict_recover(test_predict, train, diffn)
RMSE = np.sqrt(((np.array(test_predict)-np.array(test))**2).sum()/test.size)
print("測試集的RMSE爲:"+str(RMSE))
pyflux
的predict
函數就十分易用,model.predict(h = )
就可。詳細參見ARIMA的文檔,畫圖起來也是十分方便。
Time Series Forecasting using ARIMA in Python也提供了利用pyflux
進行建模的例子。
11. 調參
- 對於ARIMA來說,可調參的地方也挺多:
- 差分階數。
- p、q的階數。
- 模型擬合的方法:MLE、OLS等,參見Bayesian Inference和Classical Inference。
- 預測的週期、滾動預測的週期。
12. 結合卡爾曼濾波
在時間序列中存在的噪聲,會干擾序列中每個點的波動,給預測造成難度,所以人們就想出一個辦法來過濾這個噪聲,一個有名的辦法叫”卡爾曼濾波“
這個東西我還沒研究……給出參考資料:
其他參考讀物:
在針對ARIMA以及時間序列分析話題搜尋資料時,還接觸了以下資料:
- 《時間序列模型的三個重要概念》:自相關、白噪聲、平穩性。
- 《時間序列分析之一:數據預處理》,講了對數據進行平穩、正態、獨立、週期、趨勢項檢驗,以及提了一句對異常值的處理。
- 有什麼好的模型可以做高精度的時間序列預測呢?提供了一些關於預測模型選擇的見解。
- 時間序列建模問題,如何準確的建立時間序列模型?討論了建立時間序列模型的流程。
- Machine Learning Strategies for Time Series Prediction
- Data transformations and forecasting models: what to use and when,顧名思義,列舉了預測模型的適用範圍。
- Autoregressive moving average model Wikipedia
- 《需求預測我喜歡用ARIMA模型》,通俗、形象。
- 《金融時間序列入門(二)----MA & ARMA & ARIMA》
- 《python時間序列分析之ARIMA》的貢獻在於講到了對模型的檢驗,然而沒說清楚做檢驗的用處,真要了解還得去看計量經濟學書籍。
- Seasonal ARIMA with Python,貢獻在於講到了seasonal difference的方法,但也沒講如何確定一個序列應該應用這種差分方法。
- How to Use and Remove Trend Information from Time Series Data in Python,如果一個時間序列帶有趨勢的特徵,如何應對的方法。
- How to Make Predictions for Time Series Forecasting with Python,貢獻在於講了如何保存擬合好的模型,以及有新的數據加入的時候,更新預測模型。
- Jason Brownlee這個人最近寫了N篇關於TimeSeries的文章……
題外話:
<Evaluating Machine Learning Models>講解了對各種模型使用的評價指標、調參的方法以及AB測試的陷阱,它的未完成翻譯版見《機器學習模型評價(Evaluating Machine Learning Models)-主要概念與陷阱》
作者:竹間爲簡
鏈接:https://www.jianshu.com/p/cced6617b423
來源:簡書
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。