Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

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

目次
1.Prophet公式ドキュメント翻訳(概要&特徴編)
2.Prophet公式ドキュメント翻訳(インストール編)
3.Prophet公式ドキュメント翻訳(クイックスタート編)
4.Prophet公式ドキュメント翻訳(飽和状態の予測編)
5.Prophet公式ドキュメント翻訳(トレンドの変化点編)
6.Prophet公式ドキュメント翻訳(周期性・イベント効果・説明変数の追加編)
7.Prophet公式ドキュメント翻訳(増加し続ける周期性編)
8.Prophet公式ドキュメント翻訳(誤差の間隔編)
9.Prophet公式ドキュメント翻訳(外れ値編)
10.Prophet公式ドキュメント翻訳(1日単位ではないデータ編)
11.Prophet公式ドキュメント翻訳(モデルの診断編)
12.Prophet公式ドキュメント翻訳(ヘルプ編)

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

今回の記事は周期性・イベント効果・説明変数の追加編です。
スクリーンショット(94).png

祭日や特別なイベントをモデリングする

あなたのデータにモデル化したい祭日やイベントが繰り返しある場合、それらを格納したデータフレームを作成しなければいけません。そのデータフレームはholidaydsという2つのカラムをもちます。データフレームの行には、各イベント日の値が入っています。そのイベント日は過去(時系列データの過去の部分)と未来(時系列データの予測によって作成された部分)の両方をデータフレームに入れます。もし、イベント日の効果が未来の時点で繰り返さない場合、Prophetはそのことをモデル化し、予測にそのイベント効果を含めません。

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

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

# 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)
# 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という引数にそのテーブルを渡すことで、イベント効果を含めた予測ができます。ここではクイックスタートの章で使った、Peyton Manningのデータでイベント効果を調べます。

# R
m <- prophet(df, holidays = holidays)
forecast <- predict(m, future)
# Python
m = Prophet(holidays=holidays)
forecast = m.fit(df).predict(future)

イベント効果はforecastデータフレームで見ることができます。

# R
forecast %>% 
  select(ds, playoff, superbowl) %>% 
  filter(abs(playoff + superbowl) > 0) %>%
  tail(10)
# 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
prophet_plot_components(m, forecast)
# Python
fig = m.plot_components(forecast)

image.png
Pythonでfbprophet.plotライブラリをインポートすることで使えるplot_forecast_component関数を用いて、plot_forecast_component(forecast, 'superbowl')のようにスーパーボウルのイベント効果をプロットすることができます。

周期性の推定にフーリエ級数を利用する

部分的にフーリエ級数を使うことで周期性を推定することができます。このトピックについての詳細はこの論文を参照してください。また、Wikipediaのこの画像は恣意的な周期性のある波形を、どのように部分的なフーリエ級数に近似するのかを説明しています。フーリエ級数の項数は、周期性が変わるまでの期間の長さを決めるパラメータです。これらを説明するために、クイックスタートの章で使用したPeyton Manningのデータを使って考えていきましょう。年単位の周期性におけるフーリエ級数の項数は10個です。以下のコードで実装できます。

# R
m <- prophet(df)
prophet:::plot_yearly(m)
# Python
from fbprophet.plot import plot_yearly
m = Prophet().fit(df)
a = plot_yearly(m)

image.png
デフォルトのパラメータの値は、だいたいの場面で適切な値です。しかし、さらに頻繁な変化にたいして周期性をフィッティングさせる必要がある場合、パラメータを増加させることができます。増加させた場合は、一般的に波形は滑らかさを失います。モデルをインスタンス化した際の周期性を作成するプロセスで、フーリエ級数のパラメータは毎回指定することができます。ここではパラメータを20まで増加させます。

# R
m <- prophet(df, yearly.seasonality = 20)
prophet:::plot_yearly(m)
# Python
from fbprophet.plot import plot_yearly
m = Prophet(yearly_seasonality=20).fit(df)
a = plot_yearly(m)

image.png
フーリエ級数のパラメータを増加させることは、より速いサイクルで変化する場面にモデルの周期性を対応させることを可能にします。しかし、それは同時に過学習のリスクをはらんでいます。周期性をモデリングする変数2N個に対して、N個のフーリエ級数の項数を指定することをおすすめします。

カスタムした周期性を指定する

デフォルト設定では、時系列データが2つ以上の周期性を持つ場合、Prophetは週・年単位の周期性をフィッティングします。1時間単位の時系列データなど、単位が1日より短いデータについては、日ごとの周期性をフィットすることもできます。add_seasonality(Pythonではメソッド、Rでは関数)を用いることで、その他の周期性(月・四半期・時間単位)をフィッティングすることができます。

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

例として、ここではクイックスタートの章で使用したPeyton Manningのデータをフィッティングします。ただ、週単位の周期性を月単位の周期性に変更します。そして月単位の周期性をプロットします。

# 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)
# 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

イベント効果や周期性を前もって調整する

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

# 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)
# 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
m <- prophet()
m <- add_seasonality(
  m, name='weekly', period=7, fourier.order=3, prior.scale=0.1)
# Python
m = Prophet()
m.add_seasonality(
    name='weekly', period=7, fourier_order=3, prior_scale=0.1)

説明変数を追加する

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

# 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)
# 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
NFLの日曜日を考慮する説明変数を追加するには、まず過去と未来のNFLの日曜日のリストを作成します。そして上で述べたイベント効果を追加する方法と同様の方法を使うこともできますが、もっと便利な方法があります。add_regressor関数は説明変数の追加において、さらに便利な関数です。特筆すべきは、追加する説明変数として時系列データを使うことができます。ただ、その時系列データは未来の日付が入った状態のデータである必要があります。

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

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

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした