僕がデータ分析者として覚醒するまで

しがない会社員がデータ分析者として覚醒するまでのブログ

時系列データ分析SARIMAモデル 「学習データのinputについて」【statsmodels】

stasmodelsのSARIMAX

statsmodelsのSARIMAXに学習データを読み込む際の注意点についてのメモです。

SARIMAXのモデル作成

学習データ

statsmodelsではSARIMAモデルを扱うのに下記のクラスを使用します。 学習データは、endogに入力します。(arrayやDataframeに対応)←今回の問題箇所

 class statsmodels.tsa.statespace.sarimax.SARIMAX(endog, exog=None,
 order=(1, 0, 0), seasonal_order=(0, 0, 0, 0), trend=None, 
measurement_error=False, time_varying_regression=False, 
mle_regression=True, simple_differencing=False, 
enforce_stationarity=True, enforce_invertibility=True, 
hamilton_representation=False, **kwargs)

私の場合は下記のようにpandasのDataframeでフォルダ内のcsvを全部読み込んで結合してendogに入力していました。 もっとスマートに作りたかったのは内緒です。

import pandas as pd
import glob,os
def CombineFiletoDF(folderpath):
    csvs = glob.glob(folderpath + '\*.csv')
    for csv in csvs:
        if csv == csvs[0]:
            df = pd.read_csv(csvs[0],dtype=np.float64,encoding='SHIFT-JIS',index_col=[0],parse_dates=[0])
        else:
            df = df.append(pd.read_csv(csv,encoding='SHIFT-JIS',index_col=[0],parse_dates=[0],skiprows=0)) 
    
    df = df.resample('H').asfreq()
    return df

で、下記のようにtrainDFつくって入力してフィッティングして、実データと比較してました。 ソフトウェアエンジニアではないので、私の場合はプロトタイプ作成を行った形です。 出力結果としては、タイムスタンプ付きのDataframe(Series?)として出力されます。

mod = sm.tsa.statespace.SARIMAX(endog = trainDF,#Dataframeで入力
                                       enforce_stationarity=False,
                                       enforce_invertibility=False,
                                       order=SARIMAorder,
                                       seasonal_order=SARIMAORDER,
                                       freq='H'
                                      )

prediction_result = mod.fit(disp=False)

prediction_result.predict(start=49,end=72)

# Output
2017-01-24 01:00:00       3.074818
2017-01-24 02:00:00       3.000000
2017-01-24 03:00:00       3.925182
2017-01-24 04:00:00       3.074818
2017-01-24 05:00:00       3.925182
2017-01-24 06:00:00       3.074818
2017-01-24 07:00:00       3.925182
2017-01-24 08:00:00       3.074818
2017-01-24 09:00:00    1426.854329
2017-01-24 10:00:00    1479.087658
2017-01-24 11:00:00    1384.257394
2017-01-24 12:00:00    1287.257394
2017-01-24 13:00:00    1279.074818
2017-01-24 14:00:00    1165.202675
2017-01-24 15:00:00    1131.945281
2017-01-24 16:00:00    1111.496370
2017-01-24 17:00:00    1027.658846
2017-01-24 18:00:00     802.657166
2017-01-24 19:00:00     312.232253
2017-01-24 20:00:00     313.782805
2017-01-24 21:00:00     300.346733
2017-01-24 22:00:00      26.996639
2017-01-24 23:00:00       2.224455
2017-01-25 00:00:00       2.925182
Freq: H, dtype: float64

他方、ソフトウェア開発を行っている協力会社の方は、numpyのarrayでendogに入力していました。numpyのarrayにはindexがついていません。要するにどのデータを実際に使ったのかをindexで確認できない状況でした。結果としては、同じ値が出力されています。

mod = sm.tsa.statespace.SARIMAX(endog = np.array(trainDF),#numpyのarrayで入力
                                       enforce_stationarity=False,
                                       enforce_invertibility=False,
                                       order=SARIMAorder,
                                       seasonal_order=SARIMAORDER,
                                       freq='H'
                                      )

prediction_result = mod.fit(disp=False)

prediction_result.predict(start=49,end=72)

# Output
array([    3.0748185 ,     3.        ,     3.9251815 ,     3.0748185 ,
           3.9251815 ,     3.0748185 ,     3.9251815 ,     3.0748185 ,
        1426.85432885,  1479.08765798,  1384.25739448,  1287.25739448,
        1279.0748185 ,  1165.20267547,  1131.94528099,  1111.49637   ,
        1027.65884648,   802.65716595,   312.23225339,   313.78280451,
         300.346733  ,    26.99663893,     2.2244555 ,     2.9251815 ])

start,endの固定値は、協力会社さんの作ったプログラムから出力した結果を決め打ちで入れています。

何が問題って、本来予測したかったのは、2017/1/25であったこと。

Dataframe入力しておけば、すぐ気づけた間違いも、np.arrayにしたことで見えなくなってしまってました。 データの操作が色々あったので、配列のほうが都合がよかったのかと思いますが、僕はDataframeで入力することをお勧めします。

だって、時系列データ分析してるのに、タイムスタンプとったらわけわからんと思うんだ。