Prophet で外れ値を検出して可視化する

Prophet は Facebook が作った時系列予測パッケージです。詳しくは次のスライドを参照してください。

- [Prophet入門【R編】Facebookの時系列予測システム](https://www.slideshare.net/hoxo_m/prophetrfacebook)
- [Prophet入門【理論編】Facebookの時系列予測システム](https://www.slideshare.net/hoxo_m/prophetfacebook)

理論編のスライドにも書きましたが、Prophet が採用している時系列モデルは誤差が正規分布に従うと仮定しています。

![](https://qiita-image-store.s3.amazonaws.com/0/2570/346c8db2-8311-5c10-ed33-94d39eee7f57.jpeg)

したがって、一度モデルのフィッティングを行えば、そこから大きく外れた値を正規分布の外れ値検出を使って検出することができます。

これを行う関数を作成し、**prophetExt** パッケージに追加しましたので使い方を説明します。

まずはデータを準備し、モデルのフィッティングを行います。

```r:R
library(prophet)

# データの読み込み
df <- read.csv("https://raw.githubusercontent.com/facebookincubator/prophet/master/examples/example_wp_peyton_manning.csv")
df$y <- log(df$y)

# モデルのフィッティング
m <- prophet(df)
```

外れ値を検出する関数は `prophet_detect_outliers()` です。
次のように使います。

```r:R
outliers <- prophet_detect_outliers(m, recursive = FALSE)
head(outliers)
```

```:結果
          ds        y    resid      p_value
1 2008-02-04 12.09746 2.881975 6.723970e-07
2 2008-02-05 10.63528 1.705241 3.261588e-02
3 2009-01-04 10.15382 1.846918 1.540605e-02
4 2009-11-16 10.55870 1.693423 3.486000e-02
5 2010-01-17 11.00793 1.748206 2.641636e-02
6 2010-01-25 11.48406 1.806283 1.831846e-02
```

このようにして、モデルのフィッティングから大きく外れた値を検出することができました。

さらに、この外れ値を可視化するために `autolayer()` 関数で Prophet plot に重ね描きできるようにしました。

```r:R
future <- make_future_dataframe(m, 1)
fore <- predict(m, future)
plot(m, fore) + autolayer(outliers)
```

![](https://qiita-image-store.s3.amazonaws.com/0/2570/93ec25dc-f49a-02fd-b0b2-e29bf6e98eff.png)

赤い点が外れ値を表しています。

このように大きく外れた値に対しては、イベントの追加や外れ値除去で対処する必要があります。
しかし、このプロットだとドメイン知識を入れるのに少し見づらく感じます。
そこで、外れ値のカレンダープロットを作成する機能もつけました。

```r:R
prophet_calendar_plot(outliers)
```

![](https://qiita-image-store.s3.amazonaws.com/0/2570/650ddb06-677c-5205-cf7f-27a76446212d.png)

これを見ると、毎年どのあたりに外れ値が多いか、また予測より実際の値が大きいか小さいかの傾向が見やすくなります。

このデータは有名なアメフト選手の Wikipedia 記事のページビュー数ですが、カレンダープロットから毎年1月中旬と2月の2週目が高くなる傾向が見えます。
その頃はアメフトリーグの決勝戦がテレビで放映される時期であり、この選手が決勝戦に出場すると Wikipedia で調べる人が多くなるのではないでしょうか。
したがって、これをイベント効果として取り込んでみます。

```r:R
# 選手が出場したプレーオフとスーパーボールの日をイベントに追加
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,
  stringsAsFactors = FALSE
)
superbowls <- data.frame(
  holiday = 'superbowl',
  ds = as.Date(c('2010-02-07', '2014-02-02', '2016-02-07')),
  lower_window = 0,
  upper_window = 1,
  stringsAsFactors = FALSE
)
event_df <- rbind(playoffs, superbowls)

# モデルのフィッティング
m2 <- prophet(df, holidays = event_df)

# 外れ値の検出
outliers2 <- prophet_detect_outliers(m2, recursive = FALSE)

# カレンダープロット
prophet_calendar_plot(outliers2)
```

![](https://qiita-image-store.s3.amazonaws.com/0/2570/fdc39e3d-9f80-4787-68ec-b62ed86e46ce.png)

アメフトの決勝戦をイベント効果に取り入れることで、1月と2月の外れ値を消すことができました。

ちなみに、外れ値が集中している 2012年の3月は、この選手が長年在籍したチームを解雇されたときのようです。ニュースの出た 3/7 以降に外れ値が集中していることが見て取れます。

このように、カレンダープロットを使うことで、ドメイン知識を入れやすくなったと思います。

以上です。

## **prophetExt** のインストール

**prophetExt** パッケージは CRAN に上がっておらず、GitHub からインストールする必要があります。
GitHub のパッケージをインストールするには、**githubinstall** パッケージが便利です。

```r:R
# githubinstall のインストール
install.packages("githubinstall")
```

```r:R
library(githubinstall)
githubinstall("prophetExt")
```

```
Suggestion:
 - hoxo-m/prophetExt  Extension for Facebook Prophet
Do you want to install the package (Y/n)?  
```

これを実行するとインストール候補が表示されるので、そのままエンターキーを押してください。インストールが始まります。

## 関連

- [Prophet で有効な変化点だけを抽出する](http://qiita.com/hoxo_m/items/3f5fae96ed48aebaf434)