まずは蝋の翼から。

学んだことを書きながら確認・整理するためのメモブログ。こういうことなのかな?といったことをふわっと書いたりしていますが、理解が浅いゆえに的はずれなことも多々あると思うのでツッコミ歓迎

ARIMAXで複数のモデルを一気に試す

ARIMAXモデルにおいて、X(外部要因)に何を入れるか考える際にいちいち1つ1つ試していくのは手間がかかる。そのため、loop処理で一気に計算をおこない結果を比較したい。

データ

テキトーに以下のデータで考える。

> df
# A tibble: 7 x 5
  date       value    x1  x2_1  x2_2
  <date>     <dbl> <dbl> <dbl> <dbl>
1 2017-04-01  126.  2.82  850.   412
2 2017-05-01  132.  2.04  511.   249
3 2017-06-01  130.  2.28  698.   340
4 2017-07-01  122.  1.24  323.   157
5 2017-08-01  143.  4.56 1175.   574
6 2017-09-01  172.  1.69  425.   207
7 2017-10-01  140.  1.41  285.   134

事前準備

# xtsに変換
xts_df <- as.xts(read.zoo(df))

# testとtrainに分割
xts_df.train = xts_df[1:5]
xts_df.test = xts_df[6:7]

外部要因部分

ARIMAXモデルをauto.arimaで作成する際には、以下のようにxregオプションに外部要因となるデータを渡す必要がある。

# xregに渡したいデータ
x_data = xts_df.train[,c('x1', "x2_1")]

  #=>
  # x1      x2_1
  # 2017-04-01 2.821320  849.7471
  # 2017-05-01 2.038116  510.6461
  # 2017-06-01 2.283226  698.1490
  # 2017-07-01 1.235125  323.1432
  # 2017-08-01 4.555123 1174.8850
  # 2017-09-01 1.694707  424.8735

# ARIMAXモデルの自動作成
  model_arimax = auto.arima(
    y = xts_df.train[,'value'],
    xreg = x_data,
    ic = "aic",
    max.order = 20,
    stepwise = F,
    approximation = F,
    parallel = T,
    num.cores = 2
  )

今回は、このxregに渡したいデータ(x_data)をloop処理で変えていく。
変えていくx_dataは、欲しいカラム部分のベクトルが書かれたlistを作成し、そのlistをloopで変えていく。

# xregに渡すデータに入れたいカラム組み合わせを複数記載
models_list = list(model1 = c('x1'),
                   model2 = c('x1', "x2_1"),
                   model3 = c('x1', "x2_2")
)

for (v in models_list) {
  x_data = xts_df.train[,v]
  
  # ARIMAXモデルの自動作成
  model_arimax = auto.arima(
    y = xts_df.train[,'value'],
    xreg = x_data,
    ic = "aic",
    max.order = 20,
    stepwise = F,
    approximation = F,
    parallel = T,
    num.cores = 2
  )
}

これでloop毎に、model1, model2, model3で指定したカラムのデータが外部要因となったARIMAXモデルがそれぞれで作成される。

ただし、このままではそれぞれのloopでの結果は保存されないので結果を保存する必要がある。

結果の保存

構築したモデルは上記コードのようにmodel_arimaxオブジェクトに格納されているとする。その場合、auto.arimaの結果は以下のようにして取り出すことができる。

  # 結果
  arma =  model_arimax$arma #次数の結果(前から順に AR次数,MA次数,季節要素のAR次数,季節要素のMA次数,季節要素の期間,差分,季節要素の差分)
  aic = model_arimax$aic # AIC
  coef = model_arimax$coef #係数推定値 

[次数の結果の参考]

auto.arimaで学習した結果はあるんだけど,best modelの次数を忘れちゃった時 - Qiita

また、構築されたモデルをtestデータで予測した値に対するRMSEは以下のようにして取り出すことができる。

  # testで予測
  model_arimax_test = forecast(
    model_arimax,
    xreg = xts_df.test[,v],
    h = 1,
    level = c(95,70)
  )

# testで予測した値に対するRMSE
rmse = accuracy(model_arimax_test, xts_df.test[,'value'])['Test set','RMSE']

ちなみにRMSEの意味は以下参考。

いろいろな誤差の意味(RMSE、MAEなど) - 具体例で学ぶ数学

結果の保存(いい感じの保存)

それぞれの結果は上記コードから取り出せるが、見づらいのでtibble形式で見やすいようにデータを加工して保存する。

  # モデルの推定値をtidyに整形
  temp_model_coef = tidy(model_arimax) 
  
  # 結果を整形して保存
  model = list(model = temp_model_coef) %>%
    enframe(name = 'model_no', value = 'coef') %>% 
    mutate(model = paste(v, collapse = ' + '), #モデル外部要因
           # testで予測した結果に対するRMSE
           rmse = accuracy(model_arimax_test, xts_merged.test[,'recall_townwork'])['Test set','RMSE']
    )

全容

今までの内容をまとめると以下のようになる。

xts_df <- as.xts(read.zoo(df))

# testとtrainに分割
xts_df.train = xts_df[1:5]
xts_df.test = xts_df[6:7]

# 入れたい外部要因
models_list = list(model1 = c('x1'),
                   model2 = c('x1', "x2_1"),
                   model3 = c('x1', "x2_2")
)

n = 0
models = tibble()

for (v in models_list) {
  print(v)
  n = n + 1
  
  x_data = xts_df.train[,v]
  
  
  # ARIMAX
  model_arimax = auto.arima(
    y = xts_df.train[,'value'],
    xreg = x_data,
    ic = "aic",
    max.order = 20,
    stepwise = F,
    approximation = F,
    parallel = T,
    num.cores = 2
  )
  
  # 結果
  arma =  model_arimax$arma #次数の結果(前から順に AR次数,MA次数,季節要素のAR次数,季節要素のMA次数,季節要素の期間,差分,季節要素の差分)
  aic = model_arimax$aic # AIC
  coef = model_arimax$coef #係数推定値 
  
  # testで予測
  model_arimax_test = forecast(
    model_arimax,
    xreg = xts_df.test[,v],
    h = 1,
    level = c(95,70)
  )
  
  # tidyに整形
  temp_model_coef = tidy(model_arimax) 
  
  # 欲しいデータを整形して保存
  model = list(model = temp_model_coef) %>%
    enframe(name = 'model_no', value = 'coef') %>% 
    mutate(model_no = n,
           model = paste(v, collapse = ' + '), #モデル外部要因
           # testで予測した結果に対するRMSE
           rmse = accuracy(model_arimax_test, xts_df.test[,'value'])['Test set','RMSE'],
         aic = aic,
         arma = paste(arma,collapse = ',')
    )
  
  # 各loop結果を保存
  models = rbind(models, model)
}

結果の見方

modelsオブジェクトに各結果が保存された。なお、coef列は各モデルの係数が別途入っている

> models
# A tibble: 3 x 6
# model_no coef             model      rmse   aic arma         
# <dbl> <list>           <chr>     <dbl> <dbl> <chr>        
#   1        1 <tibble [3 × 3]> x1         34.7  32.3 1,0,0,0,1,0,0
# 2        2 <tibble [3 × 3]> x1 + x2_1  32.2  31.5 0,0,0,0,1,0,0
# 3        3 <tibble [3 × 3]> x1 + x2_2  32.1  31.7 0,0,0,0,1,0,0

coefの中身を見たい場合はunnestすると、nestされたcoefが展開された状態で表示される。

df_result = models %>% 
  dplyr::select(model_no, coef) %>% 
  unnest %>% 
  mutate_if(is.double, round, digits=2)  #下二桁表示に変換


> df_result
# A tibble: 9 x 4
# model_no term      estimate std.error
# <dbl> <chr>        <dbl>     <dbl>
#   1        2 ar1          -0.77      0.3 
# 2        2 intercept   117.        3.33
# 3        2 x1            5.19      1.38
# 4        3 intercept   118.        3.01
# 5        3 x1           15.8       5.07
# 6        3 x2_1         -0.04      0.02
# 7        4 intercept   117.        3.05
# 8        4 x1           15.9       5.31
# 9        4 x2_2         -0.08      0.04

ただし、上記では見づらいので以下のようにspreadで各係数に対して、モデル毎に横持ちにすると見やすくなる。

df_result = models %>% 
  dplyr::select(model_no, coef) %>% 
  unnest %>% 
  mutate_if(is.double, round, digits=2) 

df_result %>% 
  mutate(term = fct_inorder(term)) %>%  #デフォルトではアルファベット順なので出てきた順にする
  dplyr::select(model_no, term, estimate) %>% 
  spread(model_no, estimate)

# A tibble: 5 x 4
# term          `2`     `3`     `4`
# <fct>       <dbl>   <dbl>   <dbl>
#   1 ar1         -0.77   NA      NA   
# 2 intercept  117.    118.    117.  
# 3 x1           5.19   15.8    15.9 
# 4 x2_1        NA      -0.04   NA   
# 5 x2_2        NA      NA      -0.08

その他

今回はモデルの診断(正規性や)に関してはループ処理内に入れてないが、ひとまずRMSEを基準にしてどのモデルが良いか候補を数点選び、その数点のみ以降の処理で厳密に診断すればよいかと。

R勉強会第六回は時系列分析だよ!! - yasuhisa's blog