時系列解析ライブラリProphet　公式ドキュメント翻訳6（周期性・イベント効果・説明変数の追加編）

Facebookが公開している時系列解析用のライブラリProphetの公式ドキュメントを翻訳していきます。

目次
[1.Prophet公式ドキュメント翻訳（概要＆特徴編)](https://qiita.com/japanesebonobo/items/96868e58d4da42d36807)
[2.Prophet公式ドキュメント翻訳（インストール編)](https://qiita.com/japanesebonobo/items/a7309bc07c59c11bee7b)
[3.Prophet公式ドキュメント翻訳（クイックスタート編）](https://qiita.com/japanesebonobo/items/6f3234adb592f73bafbf)
[4.Prophet公式ドキュメント翻訳（飽和状態の予測編）](https://qiita.com/japanesebonobo/items/9a5a5a13440e90f06f30)
[5.Prophet公式ドキュメント翻訳（トレンドの変化点編）](https://qiita.com/japanesebonobo/items/26cccef8ee9d45006093)
**6.Prophet公式ドキュメント翻訳（周期性・イベント効果・説明変数の追加編）**
[7.Prophet公式ドキュメント翻訳（増加し続ける周期性編）](https://qiita.com/japanesebonobo/items/ed02f49ea66eab432af8)
[8.Prophet公式ドキュメント翻訳（誤差の間隔編）](https://qiita.com/japanesebonobo/items/f1acdd4ec1a1d0948553)
[9.Prophet公式ドキュメント翻訳（外れ値編）](https://qiita.com/japanesebonobo/items/4eb36710c689bca80c71)
[10.Prophet公式ドキュメント翻訳（1日単位ではないデータ編）](https://qiita.com/japanesebonobo/items/7062dd300a14bda4f84b)
[11.Prophet公式ドキュメント翻訳（モデルの診断編）](https://qiita.com/japanesebonobo/items/2266729f849868581011)
[12.Prophet公式ドキュメント翻訳（ヘルプ編）](https://qiita.com/japanesebonobo/items/08f85d74371240568fff)

2018/10/24公開。原文リンクは以下。
・Prophet概要＆特徴：https://facebook.github.io/prophet/
・公式ドキュメント：https://facebook.github.io/prophet/docs/quick_start.html

今回の記事は周期性・イベント効果・説明変数の追加編です。
![スクリーンショット(94).png](https://qiita-image-store.s3.amazonaws.com/0/296645/656a15ce-6490-bb79-c1e0-11fbf4d62699.png)
#祭日や特別なイベントをモデリングする
あなたのデータにモデル化したい祭日やイベントが繰り返しある場合、それらを格納したデータフレームを作成しなければいけません。そのデータフレームは`holiday`と`ds`という2つのカラムをもちます。データフレームの行には、各イベント日の値が入っています。そのイベント日は過去(時系列データの過去の部分)と未来(時系列データの予測によって作成された部分)の両方をデータフレームに入れます。もし、イベント日の効果が未来の時点で繰り返さない場合、Prophetはそのことをモデル化し、予測にそのイベント効果を含めません。

さらに、`lower_window`や`upper_window`というカラムを追加して、イベント当日の周辺の日までイベント効果が適応される範囲を延ばすことができます。例えば、クリスマスの日に加えてクリスマスイブにもイベント効果を適応させたい場合は、`lower_window=-1,upper_window=0`とします。また、サンクスギビングデーの効果をブラックフライデーにも適応させたい場合は、`lower_window=0,upper_window=1`とします。さらに`prior_scale`カラムを追加することで、事前に各イベント日に対して別々に優先度をつけることができます。具体的には、以下のコードを参照してください。

今回は、Peyton Manningのプレーオフへの出場の日を含んだデータフレームを作成しました。

```R
# R
library(dplyr)
playoffs <- data_frame(
  holiday = 'playoff',
  ds = as.Date(c('2008-01-13', '2009-01-03', '2010-01-16',
                 '2010-01-24', '2010-02-07', '2011-01-08',
                 '2013-01-12', '2014-01-12', '2014-01-19',
                 '2014-02-02', '2015-01-11', '2016-01-17',
                 '2016-01-24', '2016-02-07')),
  lower_window = 0,
  upper_window = 1
)
superbowls <- data_frame(
  holiday = 'superbowl',
  ds = as.Date(c('2010-02-07', '2014-02-02', '2016-02-07')),
  lower_window = 0,
  upper_window = 1
)
holidays <- bind_rows(playoffs, superbowls)
```

```py
# Python
playoffs = pd.DataFrame({
  'holiday': 'playoff',
  'ds': pd.to_datetime(['2008-01-13', '2009-01-03', '2010-01-16',
                        '2010-01-24', '2010-02-07', '2011-01-08',
                        '2013-01-12', '2014-01-12', '2014-01-19',
                        '2014-02-02', '2015-01-11', '2016-01-17',
                        '2016-01-24', '2016-02-07']),
  'lower_window': 0,
  'upper_window': 1,
})
superbowls = pd.DataFrame({
  'holiday': 'superbowl',
  'ds': pd.to_datetime(['2010-02-07', '2014-02-02', '2016-02-07']),
  'lower_window': 0,
  'upper_window': 1,
})
holidays = pd.concat((playoffs, superbowls))
```
上のコードでは、プレイオフとスーパーボウルの試合の両方をスーパーボウル期間としてデータフレームに格納しています。これは、プレーオフのイベントの中で、スーパーボウルが最大のイベント効果をもたらすということを意味しています。

テーブルが一度作成されれば、`holidays`という引数にそのテーブルを渡すことで、イベント効果を含めた予測ができます。ここでは[クイックスタートの章](https://qiita.com/japanesebonobo/items/6f3234adb592f73bafbf)で使った、Peyton Manningのデータでイベント効果を調べます。

```R
# R
m <- prophet(df, holidays = holidays)
forecast <- predict(m, future)
```

```python
# Python
m = Prophet(holidays=holidays)
forecast = m.fit(df).predict(future)
```
イベント効果は`forecast`データフレームで見ることができます。

```R
# R
forecast %>% 
  select(ds, playoff, superbowl) %>% 
  filter(abs(playoff + superbowl) > 0) %>%
  tail(10)
```

```py
# Python
forecast[(forecast['playoff'] + forecast['superbowl']).abs() > 0][
        ['ds', 'playoff', 'superbowl']][-10:]
```
|| **DS**|**PLAYOFF**|**SUPERBOWL**|
|-----------:|-----------:|------------:|------------:|
|**2190**|2014-02-02|1.229999|1.176410|
|**2191**|2014-02-03|1.900543|1.486962|
|**2532**|2015-01-11|1.229999|0.000000|
|**2533**|2015-01-12|1.900543|0.000000|
|**2901**|2016-01-17|1.229999|0.000000|
|**2902**|2016-01-18|1.900543|0.000000|
|**2908**|2016-01-24|1.229999|0.000000|
|**2909**|2016-01-25|1.900543|0.000000|
|**2922**|2016-02-07|1.229999|1.176410|
|**2923**|2016-02-08|1.900543|1.486962|
イベント効果はcomponents plotで可視化できます。以下のプロットでは、プレーオフ期間は針のように波形が反応しています。スーパーボウル期間は反応の大きさが特に大きくなっています。

```R
# R
prophet_plot_components(m, forecast)
```

```py
# Python
fig = m.plot_components(forecast)
```
![image.png](https://qiita-image-store.s3.amazonaws.com/0/296645/a3618348-e04d-ebb5-c8d3-0726e2afb25b.png)
Pythonで`fbprophet.plot`ライブラリをインポートすることで使える`plot_forecast_component`関数を用いて、`plot_forecast_component(forecast, 'superbowl')`のようにスーパーボウルのイベント効果をプロットすることができます。
#周期性の推定にフーリエ級数を利用する
部分的にフーリエ級数を使うことで周期性を推定することができます。このトピックについての詳細は[この論文](https://peerj.com/preprints/3190/)を参照してください。また、[Wikipediaのこの画像](https://en.wikipedia.org/wiki/Fourier_series#/media/File:Fourier_Series.svg)は恣意的な周期性のある波形を、どのように部分的なフーリエ級数に近似するのかを説明しています。フーリエ級数の項数は、周期性が変わるまでの期間の長さを決めるパラメータです。これらを説明するために、[クイックスタートの章](https://qiita.com/japanesebonobo/items/6f3234adb592f73bafbf)で使用したPeyton Manningのデータを使って考えていきましょう。年単位の周期性におけるフーリエ級数の項数は10個です。以下のコードで実装できます。

```R
# R
m <- prophet(df)
prophet:::plot_yearly(m)
```

```py
# Python
from fbprophet.plot import plot_yearly
m = Prophet().fit(df)
a = plot_yearly(m)
```
![image.png](https://qiita-image-store.s3.amazonaws.com/0/296645/4fdf2abf-654f-39ea-99cd-5e6afc48d049.png)
デフォルトのパラメータの値は、だいたいの場面で適切な値です。しかし、さらに頻繁な変化にたいして周期性をフィッティングさせる必要がある場合、パラメータを増加させることができます。増加させた場合は、一般的に波形は滑らかさを失います。モデルをインスタンス化した際の周期性を作成するプロセスで、フーリエ級数のパラメータは毎回指定することができます。ここではパラメータを20まで増加させます。

```R
# R
m <- prophet(df, yearly.seasonality = 20)
prophet:::plot_yearly(m)
```

```py
# Python
from fbprophet.plot import plot_yearly
m = Prophet(yearly_seasonality=20).fit(df)
a = plot_yearly(m)
```
![image.png](https://qiita-image-store.s3.amazonaws.com/0/296645/e6d8b360-472e-1a47-e9b2-26e117bfde84.png)
フーリエ級数のパラメータを増加させることは、より速いサイクルで変化する場面にモデルの周期性を対応させることを可能にします。しかし、それは同時に過学習のリスクをはらんでいます。周期性をモデリングする変数2N個に対して、N個のフーリエ級数の項数を指定することをおすすめします。
#カスタムした周期性を指定する
デフォルト設定では、時系列データが2つ以上の周期性を持つ場合、Prophetは週・年単位の周期性をフィッティングします。1時間単位の時系列データなど、単位が1日より短いデータについては、日ごとの周期性をフィットすることもできます。`add_seasonality`(Pythonではメソッド、Rでは関数)を用いることで、その他の周期性(月・四半期・時間単位)をフィッティングすることができます。

周期性を指定する関数の引数は周期性の単位、周期性をフィッティングする期間、フーリエ級数の項数です。参考までに、デフォルト設定ではProphetはフーリエ級数の項数は3に対し週単位の周期性、項数10に対し年単位の周期性を使用しています。この引数を調整したい場合は、`add_seasonality`を用いてください。コードは以下に示しています。

例として、ここでは[クイックスタートの章](https://qiita.com/japanesebonobo/items/6f3234adb592f73bafbf)で使用したPeyton Manningのデータをフィッティングします。ただ、週単位の周期性を月単位の周期性に変更します。そして月単位の周期性をプロットします。

```R
# R
m <- prophet(weekly.seasonality=FALSE)
m <- add_seasonality(m, name='monthly', period=30.5, fourier.order=5)
m <- fit.prophet(m, df)
forecast <- predict(m, future)
prophet_plot_components(m, forecast)
```

```py
# Python
m = Prophet(weekly_seasonality=False)
m.add_seasonality(name='monthly', period=30.5, fourier_order=5)
forecast = m.fit(df).predict(future)
fig = m.plot_components(forecast)
```
![image.png](https://qiita-image-store.s3.amazonaws.com/0/296645/66e38434-df63-ac6c-98dd-cb67238ac778.png)

#イベント効果や周期性を前もって調整する
イベント効果によって過学習が起こっていることに気が付いた場合、` holidays_prior_scale`というパラメータを用いて過学習を防ぐように、事前にモデルを調整できます。デフォルト設定ではこのパラメータは10で、これはわずかな正則化を行っています。このパラメータを減少させると、イベント効果を弱めることができます。

```R
# R
m <- prophet(df, holidays = holidays, holidays.prior.scale = 0.05)
forecast <- predict(m, future)
forecast %>% 
  select(ds, playoff, superbowl) %>% 
  filter(abs(playoff + superbowl) > 0) %>%
  tail(10)
```

```py
# Python
m = Prophet(holidays=holidays, holidays_prior_scale=0.05).fit(df)
forecast = m.predict(future)
forecast[(forecast['playoff'] + forecast['superbowl']).abs() > 0][
    ['ds', 'playoff', 'superbowl']][-10:]
```
|| **DS**|**PLAYOFF**|**SUPERBOWL**|
|-----------:|-----------:|------------:|------------:|
|**2190**|2014-02-02|1.205344|0.963327|
|**2191**|2014-02-03|1.851992|0.991010|
|**2532**|2015-01-11|1.205344|0.000000|
|**2533**|2015-01-12|1.851992|0.000000|
|**2901**|2016-01-17|1.205344|0.000000|
|**2902**|2016-01-18|1.851992|0.000000|
|**2908**|2016-01-24|1.205344|0.000000|
|**2909**|2016-01-25|1.851992|0.000000|
|**2922**|2016-02-07|1.205344|0.963327|
|**2923**|2016-02-08|1.851992|0.991010|
イベント効果の強さが先ほどよりも減少しています。特にスーパーボウルでは顕著です。`seasonality_prior_scale`というパラメータは`holidays_prior_scale`と似ていて、モデルの周期性をデータにフィットさせる度合いを調整します。

イベント日が格納されたデータフレームに`prior_scale`カラムを追加することで、個別のイベント日に対して別々に調整することができます。また、別々の周期性に対して調整を行いたい場合は、`add_seasonality`という引数に値を渡してください。例えば、週単位の周期性にのみ調整を行いたい場合は、以下のようにします。

```R
# R
m <- prophet()
m <- add_seasonality(
  m, name='weekly', period=7, fourier.order=3, prior.scale=0.1)
```

```py
# Python
m = Prophet()
m.add_seasonality(
    name='weekly', period=7, fourier_order=3, prior_scale=0.1)
```
#説明変数を追加する
`add_regressor`メソッド(関数)を用いて、線形モデルに新たな説明変数を追加することができます。説明変数の値を格納したカラムは、フィッティングと予測値の生成を行う両方のデータフレームに追加する必要があります。例えば、NFLのシーズン中に追加の効果をもたらす説明変数を加えることができます。予測結果については、説明変数の追加による効果を`extra_regressors`プロットで見ることができます。

```R
# R
nfl_sunday <- function(ds) {
  dates <- as.Date(ds)
  month <- as.numeric(format(dates, '%m'))
  as.numeric((weekdays(dates) == "Sunday") & (month > 8 | month < 2))
}
df$nfl_sunday <- nfl_sunday(df$ds)

m <- prophet()
m <- add_regressor(m, 'nfl_sunday')
m <- fit.prophet(m, df)

future$nfl_sunday <- nfl_sunday(future$ds)

forecast <- predict(m, future)
prophet_plot_components(m, forecast)
```

```py
# Python
def nfl_sunday(ds):
    date = pd.to_datetime(ds)
    if date.weekday() == 6 and (date.month > 8 or date.month < 2):
        return 1
    else:
        return 0
df['nfl_sunday'] = df['ds'].apply(nfl_sunday)

m = Prophet()
m.add_regressor('nfl_sunday')
m.fit(df)

future['nfl_sunday'] = future['ds'].apply(nfl_sunday)

forecast = m.predict(future)
fig = m.plot_components(forecast)
```
![image.png](https://qiita-image-store.s3.amazonaws.com/0/296645/b19ceb14-1382-9b3a-dca4-d7d9f490847c.png)
NFLの日曜日を考慮する説明変数を追加するには、まず過去と未来のNFLの日曜日のリストを作成します。そして上で述べたイベント効果を追加する方法と同様の方法を使うこともできますが、もっと便利な方法があります。`add_regressor`関数は説明変数の追加において、さらに便利な関数です。特筆すべきは、追加する説明変数として時系列データを使うことができます。ただ、その時系列データは未来の日付が入った状態のデータである必要があります。

`add_regressor`関数には、追加する説明変数の影響の強さを指定できる引数があります。また、説明変数を標準化するかを指定できる引数もあります。詳細はPythonであれば`help(Prophet.add_regressor)`を、Rであれば`?add_regressor`というコードを入力して、docstringを参照して下さい。注意点として、説明変数の追加はモデルのフィッティングの前に行わなければいけません。

追加の説明変数は、先ほど述べたように過去と未来の両方の日付が入ったデータでなければなりません。したがって、`nfl_sunday`のような、未来の日付があらかじめわかっているようなものか、もしくは別で予測モデルを作成して未来の日付が入ったものである必要があります。説明変数のデータがすべて過去のデータである場合、Prophetはエラーを返します。そこからフィッティングするものが何もないからです。

追加の説明変数は線形モデルの1変数として扱われます。したがって説明変数を追加することは、時系列データの周期性を追加する要因になるのか、増加し続ける時系列データを説明する要因になるのかといった、モデルの潜在的な性質を決定づけます。(増加し続ける時系列データに対するモデリングについては次の章で説明します。)