はじめに:勘ではなくデータで売上を見通したい
「来週どれくらい売れそうか」を、経験と勘でなんとなく見積もっている。中小ECや個人事業主の現場では、よくある光景だと思います。仕入れや広告予算、在庫の発注は、本当はもう少し根拠のある数字で決めたいところです。
とはいえ、時系列予測のために Python を書いて、ライブラリを学んで、モデルを管理して……となると、片手間ではなかなか手が回りません。
そこで便利なのが BigQuery ML の ARIMA_PLUS です。すでに BigQuery に売上データが入っているなら、SQLを数本書くだけで週次の売上予測ができてしまいます。本記事では、学習データの準備から予測の出力、そして定期再学習の自動化までを、中小ECの現場目線で順番に見ていきます。
ARIMA_PLUS とは
ARIMA_PLUS は、BigQuery ML が提供する時系列予測用のモデルタイプです。名前のとおり古典的な ARIMA をベースにしつつ、実務で困りがちな部分を自動でケアしてくれるのが特徴です。
- トレンド(右肩上がり・右肩下がり)の自動推定
- 季節性(週次・年次など)の自動検出
- 祝日効果(国を指定すると、その国の祝日の影響を考慮)
- 外れ値・欠損値のある程度の自動補正
うれしいのは、Python やノートブックを用意しなくても、いつもの SQL の延長で完結する点です。データの置き場所(BigQuery)と分析の道具(SQL)が同じなので、データを外に持ち出す手間もありません。
ARIMA_PLUS は単一系列向けのモデルです。「商品カテゴリごとにまとめて予測したい」という場合は、後述の
time_series_id_colを使うと、1つのCREATE MODELで複数系列を同時に学習できます。
学習データの準備
まずは予測のもとになる売上データを整えます。時系列モデルに必要なのは、基本的に 「日付」と「その日の値(売上)」の2列だけです。
ここでは注文明細テーブル raw.orders から、日次の売上を集計する例を考えます。
-- 日次売上を集計する
SELECT
DATE(ordered_at) AS sales_date,
SUM(amount) AS sales_amount
FROM `myproject.raw.orders`
WHERE status = 'paid' -- キャンセル・未決済は除外
GROUP BY sales_date
ORDER BY sales_date;
ECの売上は曜日の波が大きいので、ノイズを抑えたいときは週次に丸めるのも有効です。DATE_TRUNC で週の頭(月曜)にそろえて集計します。
-- 週次売上を集計する(週の起点を月曜にそろえる)
SELECT
DATE_TRUNC(DATE(ordered_at), WEEK(MONDAY)) AS sales_week,
SUM(amount) AS sales_amount
FROM `myproject.raw.orders`
WHERE status = 'paid'
GROUP BY sales_week
ORDER BY sales_week;
データの粒度(日次か週次か)は、最後まで統一しておくのが鉄則です。学習を日次でやったら予測も日次、というように合わせます。粒度が混ざると予測がうまく機能しません。
なお、ARIMA_PLUS は日付の歯抜け(欠損日)をある程度補ってくれますが、学習に使う期間はできるだけ連続しているほうが安定します。季節性をきちんと捉えるには、最低でも1年分以上のデータがあると安心です。
モデルを作る(CREATE MODEL)
データが用意できたら、CREATE MODEL でモデルを学習します。OPTIONS に model_type='ARIMA_PLUS' を指定し、どの列が時刻でどの列が値なのかを伝えるだけです。
CREATE OR REPLACE MODEL `myproject.mart.sales_forecast_model`
OPTIONS(
model_type = 'ARIMA_PLUS',
time_series_timestamp_col = 'sales_date', -- 時刻の列
time_series_data_col = 'sales_amount', -- 予測したい値の列
holiday_region = 'JP', -- 日本の祝日を考慮
auto_arima = TRUE, -- パラメータを自動探索
data_frequency = 'AUTO_FREQUENCY' -- 頻度(日次/週次など)も自動判定
) AS
SELECT
DATE(ordered_at) AS sales_date,
SUM(amount) AS sales_amount
FROM `myproject.raw.orders`
WHERE status = 'paid'
GROUP BY sales_date;
ポイントを補足します。
holiday_region = 'JP'を入れると、日本の祝日(年末年始や連休)による売上の凹凸を考慮してくれます。auto_arima = TRUEにしておくと、ARIMAの細かいパラメータ(p, d, q)を BigQuery 側が自動で探索します。最初はこれに任せて問題ありません。- 複数商品をまとめて予測したいときは、集計に商品IDなどを残し、
time_series_id_col = 'product_id'をOPTIONSに追加します。
複数系列をまとめて学習する場合
CREATE OR REPLACE MODEL `myproject.mart.sales_forecast_by_category`
OPTIONS(
model_type = 'ARIMA_PLUS',
time_series_timestamp_col = 'sales_date',
time_series_data_col = 'sales_amount',
time_series_id_col = 'category', -- カテゴリごとに別々のモデルを学習
holiday_region = 'JP'
) AS
SELECT
DATE(ordered_at) AS sales_date,
category,
SUM(amount) AS sales_amount
FROM `myproject.raw.orders`
WHERE status = 'paid'
GROUP BY sales_date, category;
これで、カテゴリごとに個別のモデルが1つの CREATE MODEL で同時に作られます。
ML.FORECAST で予測を出す
学習したモデルから将来の予測を取り出すには ML.FORECAST を使います。horizon で何ステップ先まで予測するかを指定します。日次データなら horizon => 28 でおよそ4週間先までです。
SELECT
forecast_timestamp, -- 予測の対象日
forecast_value, -- 予測した売上(中央値)
prediction_interval_lower_bound, -- 予測区間の下限
prediction_interval_upper_bound -- 予測区間の上限
FROM
ML.FORECAST(
MODEL `myproject.mart.sales_forecast_model`,
STRUCT(
28 AS horizon, -- 28ステップ先まで予測
0.8 AS confidence_level -- 予測区間の信頼水準(80%)
)
)
ORDER BY forecast_timestamp;
forecast_value が予測の中心値、*_bound の2列が「だいたいこの範囲に収まりそう」という幅(予測区間)です。1つの数字だけを鵜呑みにせず、上下の幅もセットで見るのが、予測とうまく付き合うコツです。
週次で予測したい場合は、週次に集計したデータでモデルを作り、horizon を「何週先まで予測するか」の数(例:12)にします。
結果をそのまま使うのではなく、テーブルに保存しておくとダッシュボードからも参照しやすくなります。
CREATE OR REPLACE TABLE `myproject.mart.weekly_sales_forecast` AS
SELECT
DATE(forecast_timestamp) AS forecast_date,
ROUND(forecast_value) AS forecast_amount,
ROUND(prediction_interval_lower_bound) AS lower_bound,
ROUND(prediction_interval_upper_bound) AS upper_bound
FROM
ML.FORECAST(
MODEL `myproject.mart.sales_forecast_model`,
STRUCT(28 AS horizon, 0.8 AS confidence_level)
);
精度の見方・季節性・限界
予測モデルは「当てるための機械」ではなく、「不確実さの幅を見える化する道具」と捉えると扱いやすくなります。
モデルがデータからどんな構造を読み取ったかは ML.ARIMA_EVALUATE で確認できます。
SELECT *
FROM ML.ARIMA_EVALUATE(MODEL `myproject.mart.sales_forecast_model`);
この結果には、検出された季節性の周期や、AIC(モデルの当てはまりの指標。小さいほど良いとされます)などが並びます。has_holiday_effect や seasonal_periods を見れば、「ちゃんと週次の波や祝日効果を拾えているか」をざっくり判断できます。
注意したいのは限界です。ARIMA_PLUS は過去のパターンの延長線を引くモデルなので、過去に例のない出来事は予測できません。大型セールの初開催、テレビ・SNSでの突発的なバズ、新商品の発売などは、予測区間を平気で飛び越えます。こうしたイベントが分かっている週は、予測値を機械的に信じず、人の判断で上振れ・下振れを補正してください。
外れ値(一時的な異常値)も要注意です。たとえば「システム障害で1日だけ売上ゼロ」のような日が学習データに混ざると、トレンドの推定が歪むことがあります。明らかに異常な日は、学習用クエリの
WHEREであらかじめ除外しておくのが安全です。
なお、本記事に登場する数値はすべて構文を示すための例です。実際の精度はデータの量や質、扱う商材の特性によって大きく変わります。まずは自分のデータで動かし、何週か予測と実績を並べて確かめてみてください。
自動化:スケジュールクエリで定期再学習
予測は一度作って終わりではありません。新しい売上が積み上がるたびに学習し直さないと、すぐに古くなってしまいます。とはいえ毎週手でクエリを実行するのは現実的ではないので、BigQuery のスケジュールクエリで自動化します。
やることはシンプルで、「モデルを再学習する CREATE MODEL」と「予測をテーブルに書き出す ML.FORECAST」を、定期実行に登録するだけです。
-- スケジュールクエリに登録する内容(例:毎週月曜の朝に実行)
-- 1. 最新データでモデルを再学習
CREATE OR REPLACE MODEL `myproject.mart.sales_forecast_model`
OPTIONS(
model_type = 'ARIMA_PLUS',
time_series_timestamp_col = 'sales_date',
time_series_data_col = 'sales_amount',
holiday_region = 'JP',
auto_arima = TRUE
) AS
SELECT
DATE(ordered_at) AS sales_date,
SUM(amount) AS sales_amount
FROM `myproject.raw.orders`
WHERE status = 'paid'
GROUP BY sales_date;
再学習と書き出しを分けたい場合は、スケジュールクエリを2本に分け、書き出し側を学習の少しあとの時刻に設定すると安全です。スケジュールクエリの作り方や運用上の注意点は、BigQueryのスケジュールクエリでデータマートを毎日自動更新する で詳しく解説していますので、あわせてご覧ください。
頻度の目安としては、週次予測なら週1回の再学習で十分なことが多いです。日次で頻繁に学習し直すよりも、無駄なコストを抑えられます。BigQuery の費用を抑える工夫は BigQueryの料金を月1万円以下に抑える実践テクニック にまとめてあります。
まとめ
ARIMA_PLUS を使えば、Python もインフラも用意せず、SQLだけでEC売上の週次予測ができます。本記事の流れを整理すると、次の4ステップです。
- 学習データの準備 — 日付と売上の2列に集計する
- モデルの作成 —
CREATE MODEL ... OPTIONS(model_type='ARIMA_PLUS', ...) - 予測の取得 —
ML.FORECASTで予測値と予測区間を出す - 自動化 — スケジュールクエリで定期的に再学習する
大事なのは、予測値を「絶対の正解」ではなく「判断の補助線」として使うことです。予測区間の幅を意識し、セールやイベントなど機械が知らない事情は人が補う。この組み合わせで、勘だけに頼っていた仕入れや予算配分に、ぐっと根拠が加わります。
まずは手元の売上データで、4週間先の予測を1本出してみるところから始めてみてください。