Databricksにおける需要予測のウォークスルー

結構、需要予測のデモをするのですが、きちんと需要予測が何であるのかを顧みずにデモしていることに(今更)気づきました。

# 需要予測とは

例によってGPT-3.5に聞いてみます。

**質問**

> 需要予測を簡単に教えてください

**回答**

> 需要予測は、将来の商品やサービスの需要を予測することです。ビジネスや組織が効率的な在庫管理や生産計画を立てるために重要です。以下に簡単な需要予測の手順を示します：
> 
> 1. **データ収集**: 過去の売上データや需要データを収集します。このデータは、商品やサービスの販売数量や需要量、時期ごとのパターンなどを含みます。
> 
> 2. **データの分析**: 収集したデータを分析し、トレンドや季節性、特定の影響因子（例えばマーケティングキャンペーン、経済指標の変化など）を特定します。これにより、将来の需要に影響を与える要因を理解します。
> 
> 3. **予測モデルの選択**: 分析結果を基に、適切な予測モデルを選択します。代表的なモデルには、移動平均法、時系列分析（ARIMAモデル）、機械学習モデル（ランダムフォレスト、ニューラルネットワークなど）があります。
> 
> 4. **モデルの構築**: 選択したモデルを使って、将来の需要を予測します。モデルの構築には、データのトレーニング（過去のデータを使ってモデルを学習させる）とテスト（予測精度を評価する）が含まれます。
> 
> 5. **予測の精度評価**: 予測結果の精度を評価し、必要に応じてモデルを調整します。これにより、より正確な予測結果を得ることができます。
> 
> 6. **予測結果の活用**: 最終的な予測結果を基に、在庫管理や生産計画、マーケティング戦略などの意思決定を行います。

GPT-4oにも聞いてみます。

**質問**

> 需要予測を図を用いて説明してください

**回答**

![output.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/8d7c1cc8-d076-5128-5d6d-04bc77335606.png)

> このグラフは、過去の売上データと移動平均を用いた需要予測の例を示しています。
> 
> - **青い線**は、実際の売上データを示しています。各点は特定の日の売上数量です。
> - **オレンジ色の点線**は、7日間の移動平均を示しています。この線はデータのノイズを平滑化し、トレンドを見やすくしています。
> - **緑の線**は、将来の売上を予測した結果を示しています。予測期間は30日間で、実際のデータと異なる色で示されています。
> 
> グラフの中央にある**灰色の縦線**は、過去のデータと予測データの境界を示しています。この縦線の右側が予測期間です。
> 
> このように、過去の売上データを分析し、移動平均を用いて将来の需要を予測することで、在庫管理や生産計画を効率的に行うことができます。

# 需要予測のウォークスルー

こちらでは、以下のアプローチを取ります。正直ノートブックであれば(ロジックさえ書けば)何でもできるのですが、適材適所で赤字にしている他のソリューションも活用します。
![Screenshot 2024-06-25 at 12.40.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/9caaa6a4-b491-863f-f447-79fbda7a9705.png)

これに基づいて以下のソリューションを活用した[需要予測](https://www.databricks.com/jp/glossary/demand-forecasting)を体験します。

- [Databricks AI/BI Genie](https://www.databricks.com/jp/product/ai-bi/genie)
- [Databricksノートブック](https://docs.databricks.com/ja/notebooks/index.html)
- [Databricks AutoML](https://www.databricks.com/jp/product/automl)



## 環境設定

```py
import re

# Username を取得
username_raw = dbutils.notebook.entry_point.getDbutils().notebook().getContext().tags().apply('user')
# Username の英数字以外を除去し、全て小文字化。Username をファイルパスやデータベース名の一部で使用可能にするため。
username = re.sub('[^A-Za-z0-9]+', '', username_raw).lower()

# UCのカタログ/データベース名、環境に合わせて変更ください
catalog_name = "takaakiyayoi_catalog"
database_name = "demand_forecast"

table_name = f"{username}_monthly_sales"

print(f"catalog_name: {catalog_name}")
print(f"database_name: {database_name}")
print(f"table_name: {table_name}")
```
```
catalog_name: takaakiyayoi_catalog
database_name: demand_forecast
table_name: takaakiyayoidatabrickscom_monthly_sales
```

## データの読み込み

キャンディ生産量のデータを使用します。

[Industrial Production: Manufacturing: Nondurable Goods: Sugar and Confectionery Product \(NAICS = 3113\) \(IPG3113N\) \| FRED \| St\. Louis Fed](https://fred.stlouisfed.org/series/IPG3113N)

```py
import pandas as pd

# 開始日と終了日を定義
start_date = "2020-01-01"
end_date = "2023-12-31"

# 指定された期間のデータを読み込む
df = pd.read_csv(
    f"https://fred.stlouisfed.org/graph/fredgraph.csv?bgcolor=%23e1e9f0&chart_type=line&drp=0&fo=open%20sans&graph_bgcolor=%23ffffff&height=450&mode=fred&recession_bars=on&txtcolor=%23444444&ts=12&tts=12&width=1138&nt=0&thu=0&trc=0&show_legend=yes&show_axis_titles=yes&show_tooltip=yes&id=IPG3113N&scale=left&cosd={start_date}&coed={end_date}&line_color=%234572a7&link_values=false&line_style=solid&mark_type=none&mw=3&lw=2&ost=-99999&oet=99999&mma=0&fml=a&fq=Monthly&fam=avg&fgst=lin&fgsnd=2020-02-01&line_index=1&transformation=lin&vintage_date=2024-06-24&revision_date=2024-06-24&nd=1972-01-01"
)
# DATE列をdatetime型に変換
df["DATE"] = pd.to_datetime(df["DATE"])
# 列名を変更
df.rename(columns={"DATE": "date", "IPG3113N": "monthly_sales"}, inplace=True)
# データフレームを表示
display(df)
```
![Screenshot 2024-06-25 at 12.20.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/f65d8957-4cd5-3e6b-744d-3cc0db9797ab.png)

可視化を追加して、全体的な傾向を把握します。
![Screenshot 2024-06-25 at 12.20.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/667c4feb-f65f-541d-54ad-0b6c1f07bc00.png)
![Screenshot 2024-06-25 at 12.20.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/99db1e1a-09bc-1ae4-d8a0-4ac09b614b70.png)

## Deltaテーブルへの保存

```py
# Pandas DataFrameをSpark DataFrameに変換
sdf = spark.createDataFrame(df)
# 変換したDataFrameを指定したテーブルに上書き保存
sdf.write.mode("overwrite").saveAsTable(f"{catalog_name}.{database_name}.{table_name}")
```

## Databricks AI/BI Genieによるデータ分析

このままノートブックで分析を進めてもいいのですが、新機能のGenieを使って日本語を駆使してデータを分析してみます。

以下のセルを実行して表示されるリンクをクリックします。

```py
displayHTML(f"<a href='/explore/data/{catalog_name}/{database_name}/{table_name}'>テーブルへのリンク</a>")
```

右上の**作成 > Genieスペース**を選択し、Genieスペースを作成します。

![](https://sajpstorage.blob.core.windows.net/yayoi/20240625_hackathon/create_genie.png)

テーブルが既に設定されているので、適切な名前を指定してGenieスペースを作成します。
![Screenshot 2024-06-25 at 12.22.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/43d481fc-3d66-7a99-64d8-aa9a9b1eba92.png)

このままでも問い合わせをスタートできるのですが、期待通りに動くように指示(**General Instructions**)を追加します。これは、いわゆるシステムプロンプトです。言語モデルの挙動をガイドするために活用します。

以下の指示を追加します。

> あなたは有能な需要予測に関するエキスパートです。質問に対して英語ではなく丁寧な日本語で回答してください。

![Screenshot 2024-06-25 at 12.24.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/fde90095-b1c2-52f2-7287-80ef1c49623b.png)

以下のような質問をしていきます。

> どのようなデータがありますか

![Screenshot 2024-06-25 at 12.26.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/a06cc7d1-b6a7-3b9e-e9b5-b4ddb011e6f1.png)

> 全体的な傾向を教えてください

![Screenshot 2024-06-25 at 12.26.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/eec8095a-4bf3-7e65-5191-51d2a0ae2cd5.png)

> Visualize (Auto Visualizeをクリック)

![Screenshot 2024-06-25 at 12.27.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/7ef2e200-df97-aa11-322b-06d75979b56f.png)

> どの期間のデータが含まれていますか

![Screenshot 2024-06-25 at 12.27.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/7933e3b9-5ad8-764c-d7c2-f89c937c8e0d.png)

> 需要予測してください

![Screenshot 2024-06-25 at 12.27.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/80e176aa-8f65-be7f-c008-adb7614876c7.png)

なんて素直。ということで、ノートブックに戻ります。

## データの確認

```py
import seaborn as sns
import matplotlib.pyplot as plt

dims = (2, 2)  # サブプロットの行数と列数を定義

# サブプロットを作成。figsizeで全体のサイズを指定。
f, axes = plt.subplots(dims[0], dims[1], figsize=(25, 15))
axis_i, axis_j = 0, 0  # サブプロットの位置を管理する変数

# 指定された年ごとにループ
for target_year in (2020, 2021, 2022, 2023):
    # seabornのlineplotで折れ線グラフを描画。x軸は日付、y軸は月間売上。
    sns.lineplot(x="date", y="monthly_sales", data=df[df["date"].dt.year == target_year], ax=axes[axis_i, axis_j], label=target_year)
    axis_j += 1  # 次の列へ移動
    if axis_j == dims[1]:  # 列の末尾に達したら次の行へ
        axis_i += 1
        axis_j = 0
```

毎年の傾向が似ているのがわかります。
![download.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/49e9caaf-92cb-dad0-f80c-40b86de43aa2.png)

## 移動平均による需要予測

予測手法にはいろいろありますが、ここでは[こちら](https://qiita.com/hasegawa16777216/items/31ebbeb2854e5c2a1867)で説明されている移動平均を使います。過去の平均を未来の予測に使ったろアプローチなので、毎年の傾向が類似している場合にはうまくいきますが、コロナのような突発的な事象には弱いですね。[そのようなケースに対応するための手法](https://qiita.com/taka_yayoi/items/810921ee3b30610ee521)も開発されているのがすごいところ。

2022年のデータから移動平均を計算します。

```py
# 2022年のデータをフィルタリング
small_df_2022 = df[df["date"].dt.year == 2022]

# 前3ヶ月の移動平均を計算し、新しい列として追加
small_df_2022["rolling_mean_3"] = (
    small_df_2022["monthly_sales"].rolling(window=3).mean()
)

# 生データと移動平均のデータを可視化
plt.plot(
    small_df_2022["date"], small_df_2022["monthly_sales"], color="red", label="raw_data"
)
plt.plot(
    small_df_2022["date"],
    small_df_2022["rolling_mean_3"],
    color="blue",
    label="rolling_mean",
)
plt.ylabel("monthly_sales")  # y軸のラベル
plt.xlabel("date")  # x軸のラベル
plt.legend(loc="upper right")  # 凡例の位置
plt.show()  # グラフを表示
```

![download (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/4889f082-e9fa-4aed-4516-aeb323c39d05.png)

2022年の移動平均と2023年の実績を比較します。

```py
# 2022年と2023年に絞る
small_df_2022 = df[df["date"].dt.year == 2022]
small_df_2023 = df[df["date"].dt.year == 2023]

# 月の列の追加
months = [i for i in range(1, 13)]
small_df_2022["months"] = months
small_df_2023["months"] = months
# 移動平均を計算・列に追加
small_df_2022["rolling_mean_3"] = (
    small_df_2022["monthly_sales"].rolling(window=3).mean()
)
small_df_2023["rolling_mean_3"] = (
    small_df_2023["monthly_sales"].rolling(window=3).mean()
)
# 可視化
plt.plot(
    small_df_2023["months"],
    small_df_2023["monthly_sales"],
    color="red",
    label="2023_raw",
)
plt.plot(
    small_df_2022["months"],
    small_df_2022["rolling_mean_3"],
    color="blue",
    label="2022_rolling",
)
plt.ylabel("monthly_sales")
plt.xlabel("month")
plt.legend(loc="upper right")
plt.show()

# MAEで精度の確認
small_df = small_df_2022.dropna()  # window=3の移動平均なので最初２つの行はNaNになっている
from sklearn.metrics import mean_absolute_error

print(
    mean_absolute_error(small_df_2023["monthly_sales"][2:], small_df["rolling_mean_3"])
)
```
![download (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/0779d716-e099-e5a9-6f2f-a886d6be16ac.png)

## Databricks AutoMLによる需要予測

上のようにノートブックでロジックを組み立てることで需要予測を行うことができますが工数がかかります。[Databricks AutoML](https://docs.databricks.com/ja/machine-learning/automl/index.html)を活用することで、クイックに機械学習モデルを構築し、予測結果を得ることができます。

サイドメニューの**エクスペリメント**にアクセス。
![Screenshot 2024-06-25 at 11.37.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/332956f3-4821-e169-f2ce-503744f67bfb.png)

**AutoMLエクスペリメントを作成**をクリック。
![Screenshot 2024-06-25 at 11.37.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/e5dd45b8-272c-ea0c-9ce6-744875c3f9d8.png)

![Screenshot 2024-06-25 at 11.39.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/1ff9237a-aec7-6dcd-cc7e-79da47f511e0.png)

- **クラスター:** MLクラスターを選択
- **機械学習の問題のタイプ:** 回帰、分類、予測から選択
- **入力学習用データセット:** トレーニングに使用するDeltaテーブルを選択
- 予測の場合
    - **予測ターゲット:** 予測する値を格納する列を選択
    - **時間列:** 日時を格納する列を選択
    - **予測期間と頻度:** どれだけ先の期間を予測するのかを指定
    - **出力用データベース:** 予測結果テーブルを格納するデータベースを指定

すでにトレーニングデータはDeltaテーブルに保存しているので、以下のように設定します。10ヶ月先まで予測します。
![Screenshot 2024-06-25 at 11.43.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/cfb1a54e-83cc-4a8a-9719-6433cb001089.png)

**AutoMLを開始**をクリックするとトレーニングがスタートします。様々なアルゴリズム、パラメーターを試行錯誤しながら最適なモデルを作り上げます。
![Screenshot 2024-06-25 at 11.45.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/5aff76c5-9b58-bc46-caf2-a8ea7177c1cd.png)

トレーニングが完了したら、データ探索用ノートブックや最適なモデルのノートブックを確認します。
![Screenshot 2024-06-25 at 11.48.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/d8a61d82-eff1-d42c-2f40-5f5e9bd7ab22.png)

![Screenshot 2024-06-25 at 11.50.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/a390f23a-13a5-550f-7db9-885e94677283.png)

![Screenshot 2024-06-25 at 11.49.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/e5f5604f-223c-9f73-42e1-c60708ded24f.png)

# まとめ

昔はノートブックしかなかったのですが、AutoMLやGenieが登場したことで、幅広い選択肢を持って需要予測に取り組めるようになりました。

- **AI/BI Genie:** 日本語を通じたデータ理解、予測自体には未対応
- **AutoML:** クイックに需要予測を実施
- **ノートブック:** オールマイティだが、ロジックは自分で組む必要あり。[Databricksアシスタント](https://docs.databricks.com/ja/notebooks/databricks-assistant-faq.html)を活用しましょう。上のコードを記述する際のオートコンプリートやコメント挿入で助けてもらいました。

### はじめてのDatabricks

[はじめてのDatabricks](https://qiita.com/taka_yayoi/items/8dc72d083edb879a5e5d)

### Databricks無料トライアル

[Databricks無料トライアル](https://databricks.com/jp/try-databricks)