将来の売上予測への挑戦:⑤Prophetによる時系列分析


はじめに

前回まで、時系列分析のARIMAモデル等を用いて、将来の売上予測を行ってきました。
色々工夫をしているつもりなのですが、調整できるパラメータが限られていて、中々精度が上がりません。

そこで、ARIMAモデルではなく、今流行りのDeep Learningでも学んでみようかと思います。
ただ、いきなりゼロからは難しいので、今回は「時系列分析といえば」とよく使われている、Facebookが出した時系列分析のライブラリProphetを使ってみたいと思います。

下記のサイト等を拝見しながら、プログラムしていたのですが、ところどころで思うように動かなかったです。ライブラリのバージョンが変わっているんですかね。

というか、Prophetって2017年にはリリースされていたんですね。そんなことも知らずに、生きてきていました。。。

分析環境

Google Colaboratory

対象とするデータ

[前回]まで同様に、データは日別の売上と説明変数として気温(平均・最高・最低)を用います。

日付 売上金額 平均気温 最高気温 最低気温
2018-01-01 7,400,000 4.9 7.3 2.2
2018-01-02 6,800,000 4.0 8.0 0.0
2018-01-03 5,000,000 3.6 4.5 2.7
2018-01-04 7,800,000 5.6 10.0 2.6

1.元データ作成

BigQueryからPandasにデータを引っ張ってくる処理は、これまでと同様です。
ただ、未来予測をするので、過去2年分(df)と未来1ヶ月分(df_future)を作っています。

また、その後日付項目をdatetime64型へ変換する必要があります。
更に、日付はds, 予測する値(ここでは売上金額)はyという変数名に変える必要があります。

import pandas as pd

query = """
SELECT * 
FROM `myproject.mydataset.mytable`
WHERE CAST(日付 AS TIMESTAMP) between CAST("{from_day}" AS TIMESTAMP) AND CAST("{to_day}" AS TIMESTAMP) ORDER BY p_date'
"""

df = pd.io.gbq.read_gbq(query.format(from_day="2017-01-01",to_day="2018-12-31"), project_id="myproject", dialect="standard")
df_future = pd.io.gbq.read_gbq(query.format(from_day="2019-01-01",to_day="2019-01-31"), project_id="myproject", dialect="standard")

from datetime import datetime

# 日付項目を、datetime64型へ変換
def strptime_with_offset(string, format='%Y-%m-%d'):
  base_dt = datetime.strptime(string, format)
  return base_dt

df['日付'] = df['日付'].apply(strptime_with_offset)

df.rename(columns={'売上金額': 'y','日付': 'ds'}, inplace=True)

2.モデルの学習

Prophetを呼び出して、モデルに色々とaddしていきます。

from fbprophet import Prophet

# モデルは非線形
model = Prophet(growth='logistic', daily_seasonality=False)

# 国を指定して祝日を追加できます
model.add_country_holidays(country_name="JP")

# 季節性を月次の要素で追加
model.add_seasonality(name='monthly', period=30.5, fourier_order=5)

# 予測に追加する変数
features_list =["平均気温","最高気温","最低気温"]

for f in features_list:
  model.add_regressor(f)

#非線形の場合はCAPが必須のため、上限となる数値をいれる
df['cap']=15000000 

model.fit(df)

これでモデルが学習されます。
項目の追加等も簡単にできるので、色々と要素を足し引きしながら学習をしていけば良さそうです。

そして、できたモデルを未来のデータに適用します。

# どの程度先まで予測するか。ここでは30日を指定
future = model.make_future_dataframe(periods=30, freq='D')
future["cap"]=15000000

# 気温等の予測に追加する変数が必要なため、df_futureとマージしてから予測
future=pd.merge(future, df_future, on="ds")
df_forecast = model.predict(future)

これで、df_forecastに予測結果が格納されます。
中身を見てみると、yhatという値で入っているようです。さらに、yhat_lower, yhat_upperと幅で予測しています。
その他、各種トレンドだったり、季節性や気温等の影響が分析されています。

3.予測結果を見える化

分析結果を分かりやすくグラフ化しましょう。先1ヶ月の売上予測と実績が比較できます。

from matplotlib import pyplot as plt
% matplotlib inline

df_output=pd.merge(df_forecast, df_future, on="ds")

# なぜか今のバージョンだと、下記がないとエラーになった
pd.plotting.register_matplotlib_converters()

df_output.plot(figsize=(18, 12), x="ds", y=["yhat","y"])

やや、予測(yhat)が上振れしていますが、未来予測としては上げ下げでかなりいい傾向を示しているのではないでしょうか。

トレンド性と周期性を抽出して見ることもできます。

model.plot_components(df_forecast)
plt.show()

  • Holidayは成人の日ですね。突き抜けています。
  • Weeklyでは、土日の週末がやはり高いですね。
  • Monthlyはグニャグニャしていますね。月末月初が高いってことですかね。

おわりに

項目名をdsとyの決め打ちだったり、ところどころで先駆者の方々のプログラムだとエラーになるなど、一筋縄でいかなかったのですが、完成してみれば非常にシンプルに動かせました。

プログラム上は計算式入れていませんが、yとyhatを比較すると、だいたい10%以内に月間の誤差が収まっているので十分使えそうな気がします。

今回は、店舗全体の売上金額で出しましたが、今後は来店者数や特定カテゴリだけの売上金額など、より精度が上がるものを見つけていければと思います。