【完全版】Prophetー時系列分析の基本から外部変数追加、holidaysの追加、ハイパーパラメータの調整まで(python)

※アフィリエイト広告を利用しています。

はじめに

Prophetについては、書籍やブログでまとまっている解説が少ないです。

ベーシックなところから、外部変数の追加holidaysの追加ハイパーパラメータの調整までの完全版ブログ記事にまとめてみました

時系列分析をはじめて行う初心者の方参考になれば幸いです。

Prophetとは

Prophetは、Facebookによって公開されたソフトウェアです。

年次、週次、及び日次の季節性を予測するモデルです。

強い季節効果を持つ時系列と、複数の季節の履歴データにあてはめるには最適です。

また、データの欠落や傾向の変化に対して堅牢であり、異常値についても適切に処理してくれます。

R又はPythonで利用できますが、本ブログでは、Pythonを使用して説明しています。

基本(単変量解析)

Prophetを使用する、データフレームを見てみましょう。

用意したデータフレームは、だいこんの2007年4月2日から2022年4月30日の値段です。これをもとに、2022年5月のだいこんの値段Prophet予測してみます。

データフレームのも確認してみます。

なお、dateがdatetime型ではなかった場合には、以下のコードを使用してdatetime型に変換します。

import datetime as dt

datetimes = []

for index,datum in df_da.iterrows():
    date = df_da['date'][i]
    datetime = dt.strptime(str_datetime,'%Y%m%d')
    datetimes.apped(datetime)
df_da['date'] = datetimes 

次に、データの前処理を行います。

Prophetを使用するには、時間を表す列名dsにして、求めたい値(今回はprice)をyにする必要があります。

公式ドキュメントによると、理想的には、日付の場合は YYYY-MM-DD、タイムスタンプの場合は YYYY-MM-DD HH:MM:SS である必要があるとのことです。

df_da_m =  df_da.rename(columns={'date':'ds','price':'y'})

次は、モデルの作成と適合です。ProphetはsklearnモデルAPIに従います。

from prophet import Prophet

model = Prophet()
model.fit(df_da_m)

そして、予測する対象のデータフレームをあらたに作成します。

今回は、2022年5月のだいこんの値段を予測するので、2022年の5月が入ったデータフレームをあらたに用意します。

future = model.make_future_dataframe(periods=31)
future

上記のfutureは2007年4月からのデータフレームですが、futureは予測対象の2022年5月だけのデータフレームでも大丈夫です。

それでは、予測してみます。

forecast = model.predict(future)

予測したものをみてみます。

forecast.tail(31)

2022年5月1日からのデータが確認できます。

今回の予測対象である値段については、このデータの一番右側のyhatの部分です。

図でも表してみます。

import matplotlib.pyplot as plt
model.plot(forecast)
plt.show();

外部変数の追加

今までは日付と予測値だけでProphetを使用しましたが、外部変数を追加したい場合は以下のようにします。

まずは、先ほどのデータフレーム(df_da_m)に気象データがあったとします。

ここで、max_tempという変数を追加したいとします。

その場合に、add_regressorを用います。

また、あわせてcapというものも追加してみます。capは、予測の上限値です。

上限値ですが、.max()で単純に過去の最大値を調べてその値をいれてもよいです。

もっとも、時系列分析では、予測値の直近のデータが影響を与えることが多いので、今回は、2022年4月1日から2022年4月30日の中の最大値にします。

import datetime as dt
df_da_m[(df_da_m['ds']>=dt.datetime(2022,4,1))&(df_da_m['ds']<dt.datetime(2022,4,30))]

これでみると最大値1512なので、1512をcapに設定します。

df_da_m['cap'] = 1512

また、気象データをいれたdf_da_mには、欠損値があるようなので、これを前の値で埋めます

df_da_m = df_da_m.fillna(method='ffill')

そして、当然外部変数を追加する際には、予測する対象の日の気象データについても必要なので、こちらも用意します。

それでは、これをもとにモデルの作成、適合、予測と一気にやっていきます。

from prophet import Prophet

model = Prophet()

model.add_regressor('max_temp')
model.fit(df_da_m)

future = pd.DataFrame(_da['date']).reset_index(drop=True)
future = future.rename(columns={'date':'ds'})
future['cap'] = 1512
future['max_temp'] = _da['max_temp']
future

forecast = model.predict(future)

これで、外部変数を追加した予測ができました。

holidaysの追加

Prophetには、休日の影響を考慮するということもさせることができます。

もっとも、休日ではなく、特定のイベントの日というものに代替することも可能です。

今回は、だいこんの値段なので、野菜の成長に影響をあたえるsun_time(日照時間)に注目して、これが多い日をイベントとして追加してみたいと思います。

また、このイベントが前の日からの続きの場合には、lower_window=-1,upper_window=0とし、次の日にも続く場合には、lower_window=0,upper_window=1とします。今回は後者を使用します。

sun_time13以上のものをholidaysとして追加してみます。

df_da_m[df_da_m['sun_time']>13]
from pandas.core.tools.datetimes import to_datetime
sun_times = pd.DataFrame({
    'holiday':'sun_times',
    'ds':pd.to_datetime(['2008-05-27','2008-08-05','2010-06-01',
                         '2012-06-26','2012-06-29','2016-05-18',
                         '2017-07-08','2018-05-22','2019-05-24',
                         '2019-05-25']),
    'lower_window': 0,
    'upper_window': 1,   
})
holidays = sun_times

そして、モデルの作成、適合、予測を一気にします。holidaysはモデルの作成の際に入れます。

from prophet import Prophet

model = Prophet(holidays=holidays)

model.add_regressor('max_temp')
model.fit(df_da_m)

future = pd.DataFrame(_da['date']).reset_index(drop=True)
future = future.rename(columns={'date':'ds'})
future['cap'] = 1512
future['max_temp'] = _da['max_temp']
future

forecast = model.predict(future)

これで、sun_time13以上のものをholidaysとして追加した予測ができました。

パラメータの調整

Prophetには、複数のパラメータがありますので、公式ドキュメントをもとにまとめてみました。

〈チューニング可能なパラメータ

changepoint_prior_scale:
最も影響力のあるパラメーター
トレンドの柔軟性、特にトレンドの変化点でトレンドがどれだけ変化するかを決定します。このドキュメントで説明されているように、それが小さすぎると、トレンドがアンダーフィットになり、トレンドの変化でモデル化されるべき分散が代わりにノイズ項で処理されてしまいます。大きすぎると、トレンドがオーバーフィットし、最も極端なケースでは、年間の季節性を捉えたトレンドになってしまう可能性があります。デフォルトの 0.05 は多くの時系列で機能しますが、これは調整できます。
[0.001, 0.5] の範囲がほぼ正しいでしょう。

seasonality_prior_scale:
このパラメーターは、季節性の柔軟性を制御します。
同様に、値が大きいと季節性が大きな変動に適合し、値が小さいと季節性の大きさが小さくなります。デフォルトは 10 で、基本的に正則化は適用されません。
調整の妥当な範囲はおそらく [0.01, 10] です。

holidays_prior_scale:
休日の効果に合わせて柔軟性を制御します。
seasonity_prior_scale と同様に、デフォルトは 10.0 で、基本的に正則化は適用されません。これは、通常、複数の休日の観察があり、その影響を適切に推定できるためです。
これは、seasonality_prior_scale と同様に [0.01, 10] の範囲で調整することもできます。

seasonality_mode:
オプションは [ ‘additive’、’multiplicative’] です。
デフォルト‘additive’です。多くのビジネス時系列には乗法的季節性があります。これは、時系列を調べて、季節変動の大きさが時系列の大きさに応じて大きくなるかどうかを確認することで最もよく識別されます。

changepoint_range:
これは、トレンドの変更が許可されている履歴の割合です。
履歴の 80% である 0.8 にデフォルト設定されます。これは、モデルが時系列の最後の 20% の傾向の変化に適合しないことを意味します。したがって、このパラメーターは、おそらく多数の時系列を除いて、調整しないほうがよいでしょう。
設定では、[0.8, 0.95] が妥当な範囲かもしれません。


〈調整されない可能性が高いパラメータ〉

growth: オプションは‘linear’ and ‘logistic’です。これはおそらく調整されません。既知の飽和点とその点に向けた成長がある場合はそれが含まれ、‘logistic’が使用されます。それ以外の場合は‘linear’になります。

changepoints: これは、変更点の場所を手動で指定するためのものです。デフォルトではなしで、自動的に配置されます。

n_changepoints: これは、自動的に配置された変化点の数です。デフォルトの 25 は、典型的な時系列の傾向の変化を捉えるのに十分なはずです (少なくとも Prophet がうまく機能するタイプ)。変化点の数を増やしたり減らしたりするよりも、これらのトレンドの変化で柔軟性を高めたり減らしたりすることに重点を置いた方が効果的changepoint_prior_scaleです。

yearly_seasonality: デフォルト (‘auto’) では、1 年のデータがある場合は年間の季節性がオンになり、それ以外の場合はオフになります。オプションは [‘auto’, True, False] です。

weekly_seasonality: 週間の季節性です。

daily_seasonality: 日間の季節性です。

そのほかは、公式ドキュメントを参考にしてください。

今回は、optunaを用いて、ベストなパラメータを設定したいと思います。

まずはoptunaをインストールして、インポートします。

!pip install optuna
import optuna

次に、optunaで最適なパラメータを探索するために、学習データとバリデーションデータにわけます。

train = df_da_m.iloc[0:4000,:]
val = df_da_m.iloc[4000:,:]

これをもとにoptunaで最適なパラメータを探索する範囲を指定し、実行します。

def objective(trial):
    params = {'changepoint_prior_scale' : 
                 trial.suggest_uniform('changepoint_prior_scale',
                                       0.001,0.5
                                      ),
              'seasonality_prior_scale' : 
                 trial.suggest_uniform('seasonality_prior_scale',
                                       0.01,10
                                      ),
              'seasonality_mode' : 
                 trial.suggest_categorical('seasonality_mode',
                                           ['additive', 'multiplicative']
                                          ),
              'changepoint_range' : 
                  trial.suggest_discrete_uniform('changepoint_range', 
                                                 0.8, 0.95, 
                                                 0.001),
              'n_changepoints' : 
                  trial.suggest_int('n_changepoints', 
                                    20, 35),
             }
    
    model = Prophet(holidays=holidays,
    **params)
    model.fit(train)

    
    forecast =  model.predict(val)
    
    val_rmse = np.sqrt(mean_squared_error(val.y, forecast.yhat))
    return val_rmse
study = optuna.create_study(direction="minimize")
study.optimize(objective, n_trials=100)

optunaで探索したベストパラメータをもとに予測をします。

なお、上記のstudy = optuna.create_study(direction=”minimize”) ですが、今回は評価指標rmseなので最小化したく、direction=”minimize”とします。

例えば、accuracyのように評価指標が高くなるようにしたい場合は、direction=”maximize”に設定してください。

from prophet import Prophet

model = Prophet(holidays=holidays,yearly_seasonality=False,
    weekly_seasonality=True,
    daily_seasonality=True,**study.best_params)

model.add_regressor('max_temp')
model.fit(df_da_m)

future = pd.DataFrame(_da['date']).reset_index(drop=True)
future = future.rename(columns={'date':'ds'})
future['cap'] = 1512
future['max_temp'] = _da['max_temp']
future

forecast = model.predict(future)

パラメータの調整した予測ができました。

以上の知識があれば、時系列分析を行う際に、Prophet使用できるかと思います!
是非お試しを。

データ分析・データサイエンスに興味がある方へ

データ分析・データサイエンスを学びたい方向けのブログ記事をまとめていますので、ご興味ありましたらご覧ください。

データサイエンス興味がある人、データサイエンティストなりたい人、データ分析をはじめてみたい人向けに、学習レベル別にオススメの書籍を紹介しています。
>>【レベル別】データサイエンス・データ分析の本まとめ(Python)

自信をもってオススメできる厳選動画をまとめてみました!!
>>【オススメ】データサイエンス・データ分析を学べる動画をまとめました(Python)

独学データ分析を学ぶにはどうしたらいいかについてもまとめていますのでこちらもご覧ください。
>>【独学】プログラミングとデータ分析(勉強法・資格・本・動画・テキスト)

最近話題の生成AIについて学びたい方

生成AIに興味がある方は是非以下のブログ記事もご覧ください。
生成AI関係ブログまとめ

手っ取り早くスクールに通いたい方はこちら

スクールで学ぶことに興味がある方は、まずは以下の無料オンライン説明会に参加してみてはいかがでしょうか。