In [1]:
import pandas as pd
import matplotlib.pyplot as plt
import io
import requests
In [2]:
# 飛行機乗客数データ
url = "https://www.analyticsvidhya.com/wp-content/uploads/2016/02/AirPassengers.csv"
stream = requests.get(url).content
content = pd.read_csv(io.StringIO(stream.decode('utf-8')), index_col='Month', parse_dates=True, dtype='float')
In [3]:
content.head()
Out[3]:
#Passengers
Month
1949-01-01 112.0
1949-02-01 118.0
1949-03-01 132.0
1949-04-01 129.0
1949-05-01 121.0
In [4]:
passengers = content['#Passengers'][:120]
passengers_plot = content['#Passengers']
plt.plot(passengers_plot)
plt.show()

ARモデル

STEP1:単位根検定(ADF)による定常性確認、自己相関・偏自己相関の確認

In [11]:
from statsmodels import api as s_api
In [8]:
result = s_api.tsa.stattools.adfuller(passengers)
result
Out[8]:
(-0.7734607708969391,
 0.8267937485032444,
 13,
 106,
 {'1%': -3.4936021509366793,
  '5%': -2.8892174239808703,
  '10%': -2.58153320754717},
 788.3190093041055)
In [10]:
print('ADF Statistic: %f' % result[0])
print('p-value: %f' % result[1])
print('critical Values:')
for key, value in result[4].items():
    print('臨界値\t%s: %.3f' % (key, value))
s_api.graphics.tsa.plot_acf(passengers, lags=35) # 自己相関表示
s_api.graphics.tsa.plot_pacf(passengers, lags=35) # 偏自己相関表示
plt.show()
ADF Statistic: -0.773461
p-value: 0.826794
critical Values:
臨界値	1%: -3.494
臨界値	5%: -2.889
臨界値	10%: -2.582
/home/yoshi-1/.local/lib/python3.8/site-packages/statsmodels/graphics/tsaplots.py:348: FutureWarning: The default method 'yw' can produce PACF values outside of the [-1,1] interval. After 0.13, the default will change tounadjusted Yule-Walker ('ywm'). You can use this method now by setting method='ywm'.
  warnings.warn(
  • Autocorrelation図より、ラグ14まで自己相関係数が有意
  • Partial Autocorrelation図より、ラグ1に大きな正の相関が、ラグ13に負の相関がある
  • 検定結果も帰無仮説が棄却されないので、非定常性critical Values

STEP2:残差に対する自己相関分析、偏自己相関分析

In [12]:
from statsmodels import tsa
import warnings
warnings.filterwarnings('ignore')
In [29]:
# ARのorder選択
for i in range(10):
    model = tsa.arima.model.ARIMA(passengers, order=(i+1, 0, 0)).fit()
    print('order ',i,"'s AIC: ", model.aic)
order  0 's AIC:  1152.396297381442
order  1 's AIC:  1141.9925064432778
order  2 's AIC:  1139.9350653758238
order  3 's AIC:  1140.2265748610776
order  4 's AIC:  1134.203504699284
order  5 's AIC:  1135.8411984200757
order  6 's AIC:  1136.4605246736808
order  7 's AIC:  1135.8717872323025
order  8 's AIC:  1107.879602446463
order  9 's AIC:  1107.1791284112683
  • 上記より、AR(9)で実行
In [31]:
model = tsa.arima.model.ARIMA(passengers, order=(9, 0, 0)).fit()
In [33]:
# 残差の確認
resid = model.resid
resid[:5]
Out[33]:
Month
1949-01-01   -133.915443
1949-02-01      2.406346
1949-03-01      7.702516
1949-04-01     -9.751786
1949-05-01     -6.124980
dtype: float64

残差の自己相関と偏自己相関を可視化

In [34]:
fig = plt.figure(figsize=(5,8))
ax1 = fig.add_subplot(211)
fig = s_api.graphics.tsa.plot_acf(resid, lags=40, ax=ax1)
ax2 = fig.add_subplot(212)
fig = s_api.graphics.tsa.plot_pacf(resid, lags=40, ax=ax2)
  • 上記のように、非定常性のデータをそのままARモデルに適用すると、相関が残差に現れる。
    • -> 逆に言えば、残差に相関が残っていることは、解析手法が元データに残っている非定常性を処理できていないことを示している。

STEP3:ARモデルの予測値を実測値をプロット

In [36]:
pred = model.predict('1955-01-01', '1958-12-01')
plt.figure(figsize=(6,5))
plt.plot(passengers[70:120],'--')
plt.plot(pred, "k")
plt.xticks(rotation=45)

pred = model.predict('1958-01-01', '1965-12-01')
plt.figure(figsize=(4,5))
plt.plot(passengers[40:],'--')
plt.plot(pred, "k")
plt.xticks(rotation=45)

plt.show()
  • 点線:予測値、棒線:実測値の上グラフを見ると、予測値が右にずれていおり、見せかけの回帰が発生していることがわかる
    • -> 予測できているように見えるが、単なる見せかけの回帰
  • 下グラフより全く予測できていないことがわかる

MAモデル

STEP1:サンプルデータに対するADF検定と、自己相関分析・偏自己相関分析

In [ ]:
# ARモデルでの結果を参照

STEP2:残差に対する自己相関分析、偏自己相関分析

In [13]:
# ARのorder選択
for i in range(10):
    model = tsa.arima.model.ARIMA(passengers, order=(0, 0, i+1)).fit()
    print('order ',i,"'s AIC: ", model.aic, "'s BIC: ", model.bic)
order  0 's AIC:  1295.4218464795822 's BIC:  1303.7843217079283
order  1 's AIC:  1229.2184917300697 's BIC:  1240.3684587011978
order  2 's AIC:  1169.7840864259215 's BIC:  1183.7215451398317
order  3 's AIC:  1191.7531165754308 's BIC:  1208.4780670321231
order  4 's AIC:  1146.1866048870752 's BIC:  1165.6990470865494
order  5 's AIC:  1194.8330506439975 's BIC:  1217.1329845862538
order  6 's AIC:  1117.6213797642388 's BIC:  1142.7088054492772
order  7 's AIC:  1142.0796507734246 's BIC:  1169.9545682012451
order  8 's AIC:  1120.9473864873762 's BIC:  1151.6097956579788
order  9 's AIC:  1130.2465217063082 's BIC:  1163.6964226196926
  • 上記より、MA(6)で実行
In [14]:
model = tsa.arima.model.ARIMA(passengers, order=(0, 0, 6)).fit()
In [15]:
resid = model.resid
resid
Out[15]:
Month
1949-01-01   -133.762558
1949-02-01    -18.719800
1949-03-01    -31.972892
1949-04-01    -31.805934
1949-05-01    -17.076811
                 ...    
1958-08-01     28.221216
1958-09-01    -29.780514
1958-10-01    104.049944
1958-11-01    -45.369312
1958-12-01     16.546870
Length: 120, dtype: float64
In [16]:
fig = plt.figure(figsize=(5,8))
ax1 = fig.add_subplot(211)
fig = s_api.graphics.tsa.plot_acf(resid, lags=40, ax=ax1)
ax2 = fig.add_subplot(212)
fig = s_api.graphics.tsa.plot_pacf(resid, lags=40, ax=ax2)
  • グラフより、残差のデータ同士はまだ相関が残っているのがわかる
  • MA回帰条件は定常性を満たす必要があるため、非定常性のデータにそのままMAモデルを適用すると、相関を持った残差が表れる

STEP3:MAモデルによる予測値と実測値をプロット

In [19]:
pred = model.predict('1955-01-01', '1958-12-01')
plt.figure(figsize=(6,5))
plt.plot(passengers[70:120],'--', label='real value')
plt.plot(pred, "k", label='predicted value')
plt.xticks(rotation=45)

pred = model.predict('1958-01-01', '1965-12-01')
plt.figure(figsize=(6,5))
plt.plot(passengers[40:],'--', label='real value')
plt.plot(pred, "k", label='predicted value')
plt.xticks(rotation=45)
plt.legend()
plt.show()
  • 予測にトレンドも周期性も全く反映されていない

ARMAモデル

STEP1:サンプルデータに対するADF検定と、自己相関分析・偏自己相関分析

In [ ]:
# ARモデルでの結果参照

STEP2:残差に対する自己相関分析、偏自己相関分析

In [23]:
# モデルの選定
sellected_order = tsa.stattools.arma_order_select_ic(passengers, max_ar=10, max_ma=10, ic='aic')
In [24]:
# 選定結果
sellected_order.aic_min_order
Out[24]:
(8, 9)
In [25]:
model = tsa.arima.model.ARIMA(passengers, order=(8, 0, 9)).fit()
In [26]:
resid = model.resid
resid
Out[26]:
Month
1949-01-01   -133.909019
1949-02-01      3.168622
1949-03-01      7.120942
1949-04-01    -10.555256
1949-05-01      2.466391
                 ...    
1958-08-01     37.732684
1958-09-01    -27.268864
1958-10-01      0.500663
1958-11-01    -22.223930
1958-12-01     17.949356
Length: 120, dtype: float64
In [27]:
fig = plt.figure(figsize=(5,8))
ax1 = fig.add_subplot(211)
fig = s_api.graphics.tsa.plot_acf(resid, lags=40, ax=ax1)
ax2 = fig.add_subplot(212)
fig = s_api.graphics.tsa.plot_pacf(resid, lags=40, ax=ax2)
  • 残差に12周期ごとの自己相関がのこっている

STEP3:ARMAモデルによる予測値と実測値をプロット

In [28]:
pred = model.predict('1955-01-01', '1958-12-01')
plt.figure(figsize=(6,5))
plt.plot(passengers[70:120],'--', label='real value')
plt.plot(pred, "k", label='predicted value')
plt.xticks(rotation=45)

pred = model.predict('1958-01-01', '1965-12-01')
plt.figure(figsize=(6,5))
plt.plot(passengers[40:],'--', label='real value')
plt.plot(pred, "k", label='predicted value')
plt.xticks(rotation=45)
plt.legend()
plt.show()
  • 周期性は捉えられるようになってきている
  • トレンドが捉えられていない

ARIMAモデル

STEP1省略

STEP2:残差に対する自己相関分析、偏自己相関分析

  • ARIMAモデルのパラメータ探索は、まずデータを差分(微分)してから、arma_order_select_ic()で探索する
In [36]:
import numpy as np
In [37]:
# ARIMAのorder選択

# まずデータを差分して定常性にする
diffed_passengers = np.diff(passengers)
In [39]:
diffed_passengers
Out[39]:
array([   6.,   14.,   -3.,   -8.,   14.,   13.,    0.,  -12.,  -17.,
        -15.,   14.,   -3.,   11.,   15.,   -6.,  -10.,   24.,   21.,
          0.,  -12.,  -25.,  -19.,   26.,    5.,    5.,   28.,  -15.,
          9.,    6.,   21.,    0.,  -15.,  -22.,  -16.,   20.,    5.,
          9.,   13.,  -12.,    2.,   35.,   12.,   12.,  -33.,  -18.,
        -19.,   22.,    2.,    0.,   40.,   -1.,   -6.,   14.,   21.,
          8.,  -35.,  -26.,  -31.,   21.,    3.,  -16.,   47.,   -8.,
          7.,   30.,   38.,   -9.,  -34.,  -30.,  -26.,   26.,   13.,
         -9.,   34.,    2.,    1.,   45.,   49.,  -17.,  -35.,  -38.,
        -37.,   41.,    6.,   -7.,   40.,   -4.,    5.,   56.,   39.,
         -8.,  -50.,  -49.,  -35.,   35.,    9.,  -14.,   55.,   -8.,
          7.,   67.,   43.,    2.,  -63.,  -57.,  -42.,   31.,    4.,
        -22.,   44.,  -14.,   15.,   72.,   56.,   14., -101.,  -45.,
        -49.,   27.])
In [40]:
# モデルの選定
sellected_order = tsa.stattools.arma_order_select_ic(diffed_passengers, max_ar=10, max_ma=10, ic='aic')
In [41]:
# 選定結果
sellected_order.aic_min_order
Out[41]:
(10, 4)
In [42]:
# モデル
model = tsa.arima.model.ARIMA(passengers, order=(10, 1, 4)).fit()
In [43]:
resid = model.resid
resid
Out[43]:
Month
1949-01-01    112.000000
1949-02-01      5.979191
1949-03-01     13.533695
1949-04-01     -5.529943
1949-05-01    -11.400880
                 ...    
1958-08-01     16.545189
1958-09-01    -43.245368
1958-10-01      8.969222
1958-11-01      6.587121
1958-12-01      2.746302
Length: 120, dtype: float64
In [44]:
fig = plt.figure(figsize=(5,8))
ax1 = fig.add_subplot(211)
fig = s_api.graphics.tsa.plot_acf(resid, lags=40, ax=ax1)
ax2 = fig.add_subplot(212)
fig = s_api.graphics.tsa.plot_pacf(resid, lags=40, ax=ax2)

STEP3:ARIMAモデルによる予測値と実測値をプロット

In [45]:
pred = model.predict('1955-01-01', '1958-12-01')
plt.figure(figsize=(6,5))
plt.plot(passengers[70:120],'--', label='real value')
plt.plot(pred, "k", label='predicted value')
plt.xticks(rotation=45)

pred = model.predict('1958-01-01', '1965-12-01')
plt.figure(figsize=(6,5))
plt.plot(passengers[40:],'--', label='real value')
plt.plot(pred, "k", label='predicted value')
plt.xticks(rotation=45)
plt.legend()
plt.show()
  • 過去データはかなり一致してきているが、予測に関してはARMAとあまり変わらない?
In [47]:
# 残差のプロット
plt.plot(resid)
Out[47]:
[<matplotlib.lines.Line2D at 0x7f0f4471c550>]

SARIMAモデル

STEP1省略

STEP2:残差に対する自己相関分析、偏自己相関分析

  • 今までの残差の自己相関から、12ごとの周期性があったので、seasonalに12を設定
  • seasonalの(p,d,q)はとりあえず1で設定
In [59]:
# モデル
model = tsa.statespace.sarimax.SARIMAX(passengers, order=(10,1,4), seasonal_order=(1,1,1,12)).fit()
In [60]:
resid = model.resid
resid
Out[60]:
Month
1949-01-01    112.000000
1949-02-01      6.002080
1949-03-01     13.999394
1949-04-01     -2.998547
1949-05-01     -7.999666
                 ...    
1958-08-01     14.339924
1958-09-01    -32.845755
1958-10-01     -0.248593
1958-11-01     -2.495599
1958-12-01     -8.481652
Length: 120, dtype: float64
In [61]:
fig = plt.figure(figsize=(5,8))
ax1 = fig.add_subplot(211)
fig = s_api.graphics.tsa.plot_acf(resid, lags=40, ax=ax1)
ax2 = fig.add_subplot(212)
fig = s_api.graphics.tsa.plot_pacf(resid, lags=40, ax=ax2)

STEP3:SARIMAモデルによる予測値と実測値をプロット

In [62]:
pred = model.predict('1955-01-01', '1958-12-01')
plt.figure(figsize=(6,5))
plt.plot(passengers[70:120],'--', label='real value')
plt.plot(pred, "k", label='predicted value')
plt.xticks(rotation=45)

pred = model.predict('1958-01-01', '1965-12-01')
plt.figure(figsize=(6,5))
plt.plot(passengers[40:],'--', label='real value')
plt.plot(pred, "k", label='predicted value')
plt.xticks(rotation=45)
plt.legend()
plt.show()
  • トレンドも考慮されるようになった
In [63]:
# 残差のプロット
plt.plot(resid)
Out[63]:
[<matplotlib.lines.Line2D at 0x7f0f4266f1c0>]
In [ ]: