Rubyでprophetを使う

自分で使うにあたって、頭の整理も兼ねて記事にしました。

## rubyで時系列予測がしたい！
- 全部rubyでやりたい
- railsで動かしてるシステムの中で予測値を出したい
- pythonやRなどでいいライブラリはいっぱいあるが、railsとのつなぎこみが面倒臭い
- スプレッドシートのTREND関数などもあるけど、やっぱりrailsとのつなぎこみが面倒臭い
- 全部rubyでやりたい

## prophetを使おう
prophet-rbというgemを使います。
https://github.com/ankane/prophet

元はfacebook/prophet(https://github.com/facebook/prophet )。これを移植してきたもの。

- 複数の季節性
- 線形/非線形成長
- 祝日など特別な日のハンドリング
- 欠損値処理
などをいい感じにやってくれる。

## インストール
Gemfileに以下の行を追記。

```
gem 'prophet-rb'
```

## まずは試しに
{ 時系列: 実績値 }のハッシュを用意します。

```
series = {
  Date.parse("2020-01-01") => 100,
  Date.parse("2020-01-02") => 150,
  Date.parse("2020-01-03") => 136,
  Date.parse("2020-01-04") => 130,
  Date.parse("2020-01-05") => 128,
  Date.parse("2020-01-06") => 120,
  Date.parse("2020-01-07") => 135,
  Date.parse("2020-01-08") => 136,
  Date.parse("2020-01-09") => 127,
  Date.parse("2020-01-10") => 138
}
```

```
Prophet.forecast(series, count: 3) # 二つ目の引数で予測値をいくつ返すか指定
```
これを実行すると、下のように予測値が得られる、というわけです。予測に使う元データは最低10個必要です。

```
> Prophet.forecast(series, count: 3)
=> {Sat, 11 Jan 2020=>136.19586588066147, Sun, 12 Jan 2020=>137.322312420595, Mon, 13 Jan 2020=>138.44875896052858}
```

## CSVを読み込んで時系列予測をする

```
df = Rover.read_csv("example_wp_log_peyton_manning.csv")
df.head
```

こういうcsvがあったとする。

|  ds  |  y  |
| ---- | ---- |
|  2007-12-10  |  9.59076113  |
|  2007-12-11  |  8.51959031  |
|  2007-12-12  |  8.18367658  |
|  2007-12-13  |  8.07246736  |
|  2007-12-14  |  7.89357207  |


```
m = Prophet.new
m.fit(df)
```
fitメソッドで予測。


将来の時系列を与えて予測してみる

```
future = m.make_future_dataframe(periods: 365)
future.tail
```

|  ds  |
| ---- |
|  2007-12-10  |
|  2007-12-11  | 
|  2007-12-12  |
|  2007-12-13  |
|  2007-12-14  |

```
forecast = m.predict(future)
forecast[["ds", "yhat", "yhat_lower", "yhat_upper"]].tail
```

|  ds  |  yhat  | yhat_lower | yhat_upper |
| ---- | ---- |---- | ---- |
|  2017-01-15  |  8.21192840  | 7.52526442 | 8.92389960 |
|  2017-01-16  |  8.53696359  | 7.79124970 | 9.22620028 |
|  2017-01-17  |  8.32439891  | 7.62482699 | 9.04719328 |

下限/上限まで出してくれる！すごい！
https://github.com/mrkn/matplotlib.rb
これも使うといい感じに可視化までしてくれます。

## Saturating Forecasts
詳しい説明はこちら
https://facebook.github.io/prophet/docs/saturating_forecasts.html

```
df = Rover.read_csv("example_wp_log_R.csv")
df["cap"] = 8.5
m = Prophet.new(growth: "logistic")
m.fit(df)
future = m.make_future_dataframe(periods: 365)
future["cap"] = 8.5
forecast = m.predict(future)
```

## トレンドがのchange points
https://facebook.github.io/prophet/docs/trend_changepoints.html

```
fig = m.plot(forecast)
m.add_changepoints_to_plot(fig.gca, forecast)
```

## 特別な日の設定
https://facebook.github.io/prophet/docs/seasonality,_holiday_effects,_and_regressors.html

holiday列とds列を持つデータフレームを作成します。過去のデータに含まれるすべての事象と、予測したい将来の事象を含めます。

```
playoffs = Rover::DataFrame.new(
  "holiday" => "playoff",
  "ds" => ["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 = Rover::DataFrame.new(
  "holiday" => "superbowl",
  "ds" => ["2010-02-07", "2014-02-02", "2016-02-07"],
  "lower_window" => 0,
  "upper_window" => 1
)
holidays = playoffs.concat(superbowls)

m = Prophet.new(holidays: holidays)
m.fit(df)
```

国別の祝日を入れることもできる

```
m = Prophet.new
m.add_country_holidays(country_name: "US")
m.fit(df)
```

季節性も設定できる。

```
m = Prophet.new(weekly_seasonality: false)
m.add_seasonality(name: "monthly", period: 30.5, fourier_order: 5)
forecast = m.fit(df).predict(future)
```

他にも色々な機能があります！本当に便利ですね。。