時系列予測では、モデルの選び方だけでなく、どの粒度で予測するか、予測値にどんな制約を持たせるか、異常値や欠損値をどう扱うかによって、結果の質が大きく変わります。
とくに初心者がつまずきやすいのは、モデルそのものよりも前段の設計です。数式や手法名だけを追うのではなく、「どんな場面で、なぜその考え方を使うのか」が見えるように、実務で判断に迷いやすいポイントを順に整理していきます。
1. この記事でわかること
- 週次・月次・日次・日内データで、予測の考え方がどう変わるか。
- 予測値を整数、非負、ある区間内に収めたいときの代表的な方法。
- 異常値と欠損値を見つける方法と、現実的な処理の進め方。
2. 先に押さえたい結論
- 予測精度はモデル以前に、時間粒度の選び方で大きく変わる。
- 予測対象に整数制約や非負制約があるなら、学習前にそれを設計へ織り込むべきである。
- 異常値処理は、検出よりも「なぜ異常に見えるのか」の切り分けが重要である。
- 初心者は、いきなり複雑なモデルへ進むより、粒度、特徴量、前処理の整合性を先に確認した方が失敗しにくい。
3. 粒度ごとの予測の考え方
3.1 まず押さえたい考え方
時系列予測では、何を 1 ステップとして予測するかで、見える周期性も使うべきモデルも変わります。売上を例にすると、月次売上、日次売上、時間別売上は、同じ対象でも別の問題として扱う必要があります。
初心者が最初に確認したいポイントは次の 2 つです。
- 予測したい意思決定はどの粒度で行われるのか。
- その粒度で十分な履歴長があるのか。
3.2 週次・月次の時系列予測
周期性の特徴
週次・月次データでは、年単位の周期性が重要になることが多くあります。
- 週次系列: 1 年を 52 週として捉えることが多い
- 月次系列: 1 年を 12 か月として捉える
候補となる手法
- ETS
- ARIMA
- Prophet
- 機械学習
- 深層学習
手法選択の目安
| データの状況 | 推奨手法 | 考え方 |
|---|---|---|
| 各系列の履歴が 1 年未満 | ETS、ARIMA | 年周期を十分に学習しにくいため、まずは比較的単純な統計モデルが扱いやすい |
| 各系列の履歴が 1 年以上あり、系列数が少ない | Prophet | 年周期を活かしつつ、系列ごとに個別モデルを作りやすい |
| 1 年以上の系列と未満の系列が混在し、系列数も多い | 機械学習、深層学習 | 複数系列をまとめて学習しやすく、系列間の共通パターンも利用しやすい |
機械学習で最低限入れたい特徴量
- 直近 1 期前の観測値
- 1 年前に相当する観測値
- 外生変数
特徴量の例
週次予測で使う例:
- y_{t-1}: 1 週前の観測値
- y_{t-52}: 52 週前の観測値
- external_features_t: 現在または将来時点の外生変数
月次予測で使う例:
- y_{t-1}: 1 か月前の観測値
- y_{t-12}: 12 か月前の観測値
- external_features_t: 現在または将来時点の外生変数
初心者向けメモ
履歴が短いのに季節性の強いモデルを使うと、きれいに当てはまって見えても、実際には過学習になっていることがあります。まずは「去年の同じ時期をどこまで参照できるか」を確認すると判断しやすくなります。
3.3 日次の時系列予測
周期性の特徴
日次データでは、週周期と年周期の両方が重要になることが多くあります。
- 週単位の変動: 7 日周期
- 年単位の変動: おおむね 365 日周期
候補となる手法
- Prophet
- 機械学習
- 深層学習
手法選択の目安
| データの状況 | 推奨設定 | 補足 |
|---|---|---|
| 各系列の履歴が 1 年未満 | Prophet、yearly_seasonality = False、weekly_seasonality = True
|
年周期は十分に学習しにくいため、週周期を中心に扱う |
| 各系列の履歴が 1 年以上あり、系列数が少ない | Prophet、yearly_seasonality = True、weekly_seasonality = True
|
年周期と週周期の両方を明示的に扱いやすい |
| 1 年以上の系列と未満の系列が混在し、系列数も多い | 機械学習、深層学習 | 複数系列をまとめて扱いやすい |
機械学習で最低限入れたい特徴量
- 1 日前の観測値
- 7 日前の観測値
- 365 日前の観測値
- 外生変数
特徴量の例
日次予測で使う例:
- y_{t-1}: 1 日前の観測値
- y_{t-7}: 7 日前の観測値
- y_{t-365}: 365 日前の観測値
- external_features_t: 外生変数
初心者向けメモ
日次データで精度が出ないときは、モデルより先に、曜日ダミー、祝日情報、販促期間の有無といった外生情報を見直す方が効くことが少なくありません。
3.4 日内データの時系列予測
基本的な考え方
時間単位や分単位のデータは、日内データとして扱うのが自然です。英語では sub-daily data と呼ばれることもあります。
周期性の特徴
日内データでは、少なくとも次の三つの周期が候補になります。
- 日周期
- 週周期
- 年周期
候補となる手法
- Prophet
- 機械学習
- 深層学習
戦略 1: まず日次総量を予測し、その後に配分する
次のような場面では、この方法が実務的です。
- 日内の細かい誤差より、日次や週次の総量精度を重視したい
- 日内データが疎で、直接学習するとノイズが大きい
まず日次総量を予測する
│
▼
時間帯別の比率で日内へ配分する
戦略 2: 日内データを直接予測する
日内データを直接予測する場合は、日周期、週周期、年周期のどれが本当に必要かを見極めることが大切です。粒度を細かくしすぎると、系列は長く見えても、1 パターンあたりの有効サンプル数が足りなくなることがあります。
Prophet を使う場合は、周期の単位に注意が必要です。Prophet の period は時間数ではなく「日数」で指定するため、時間単位データで日周期を追加するなら period=1 を使います。
時間単位データの例:
from prophet import Prophet
model = Prophet(
yearly_seasonality=True,
weekly_seasonality=True,
daily_seasonality=False,
)
model.add_seasonality(
name="daily",
period=1,
fourier_order=10,
)
初心者向けメモ
「24 時間だから period=24」と考えたくなりますが、Prophet では時間ではなく日を基準に季節性を指定します。ここは実装でつまずきやすい点なので、覚えておく価値があります。
4. 予測値を所定の範囲に収める方法
4.1 なぜ制約を意識するのか
予測モデルが返す値は、統計的には妥当でも、業務上は不自然なことがあります。たとえば販売数量なのに負の値が出る、在庫率なのに 1 を超える、件数なのに小数になる、といったケースです。
そのため、実務では次の三つの制約がよく問題になります。
- 予測値を整数にしたい
- 予測値を 0 以上にしたい
- 予測値をある区間
(a, b)に収めたい
4.2 予測値を整数にする方法
方法 1: 四捨五入する
y_hat_integer = round(y_hat)
販売数量や件数など、整数で扱うのが自然な対象では最も手軽です。
方法 2: 離散分布で直接モデリングする
予測対象がカウントデータなら、はじめから離散分布を前提にした方が自然なことがあります。代表例は次の二つです。
- ポアソン分布
- 負の二項分布
たとえば DeepAR では、予測対象を確率分布として扱います。
-
z: 予測対象 -
P(z):zの確率分布
予測対象 z がカウントデータ
│
▼
適切な離散分布を選ぶ
│
├─▶ Poisson 分布
└─▶ Negative Binomial 分布
│
▼
モデルは単一値ではなく分布パラメータを出力する
初心者向けメモ
カウントデータは整数で観測されますが、モデルの点予測として平均値を取り出すと小数になることはあります。業務で最終出力を整数にしたいなら、評価指標と運用方法を見ながら、最後に丸めるか、分布からの扱い方を決める必要があります。
4.3 予測値を 0 以上に保つ方法
方法 1: 負の値を 0 に切り上げる
y_hat = max(y_hat, 0)
最も簡単ですが、0 付近の誤差構造が歪む可能性はあります。
方法 2: 目的変数に対数変換をかける
z = log(y)
予測後に元のスケールへ戻します。
y = exp(z)
0 を含むデータでは log(y) はそのまま使えないため、実務では log1p(y) と expm1(z) を使うことも多くあります。
方法 3: 出力層で非負制約を持たせる
深層学習モデルなら、出力層に ReLU を置いて非負制約を持たせる方法があります。
output = ReLU(raw_output)
ReLU(x) = max(0, x)
連続値の予測では、ReLU だけでなく Softplus を使う設計もありますが、まずは「出力の意味に合う制約を持たせる」という発想を押さえるのが先です。
4.4 予測値を区間 (a, b) に収める方法
方法 1: 上下限で打ち切る
if y_hat < a:
y_hat = a
elif y_hat > b:
y_hat = b
等価な書き方:
y_hat = min(max(y_hat, a), b)
単純で使いやすい一方、境界付近の予測分布は不自然になりやすい点に注意が必要です。
方法 2: logit 型の変換を使う
区間 (a, b) 上の値を実数全体へ写像し、予測後に逆変換して戻す考え方です。
z = log((y - a) / (b - y))
予測後の逆変換:
y = (b - a) * exp(z) / (1 + exp(z)) + a
同じ内容を sigmoid で書くと次のようになります。
y = a + (b - a) * sigmoid(z)
方法 3: Sigmoid と線形変換を組み合わせる
深層学習では、出力を一度 (0, 1) に押し込み、その後で (a, b) にスケーリングする方法が使えます。
raw_output ─▶ sigmoid ─▶ s ∈ (0, 1)
予測値 y = a + (b - a) * s
その結果、出力は常に次を満たします。
y ∈ (a, b)
初心者向けメモ
Sigmoid を使う方法は厳密には開区間 (a, b) へ押し込む形です。端点をちょうど取りたい場合は、事後的なクリップを組み合わせることがあります。
5. 異常値と欠損値の扱い方
5.1 まず「異常」の意味を分けて考える
異常値には、単なる入力ミスもあれば、販促や障害のような業務上重要なイベントもあります。見た目が外れているからといって、すべて機械的に削除してよいわけではありません。
最初に確認したいのは次の三点です。
- 本当にデータ不良なのか。
- 特別な業務イベントなのか。
- 将来も起こりうる現象なのか。
5.2 異常値の見つけ方
代表的な見つけ方は、次の三つです。
方法 1: 3σ ルール
向いている場面:
- 外生変数の影響が小さい
- 季節変動やトレンドがそれほど強くない
基本的な考え方:
平均から大きく外れた観測値を異常値として扱う
代表的な判定式:
異常判定: |y_t - mean(y)| > 3 * std(y)
初心者向けメモ
強いトレンドや季節性がある系列に、そのまま 3σ ルールを当てると、正常な季節変動まで異常扱いしてしまうことがあります。その場合は、先に差分を取る、あるいは季節性を外してから見る方が安全です。
方法 2: STL 分解
向いている場面:
- 外生変数の影響が小さい系列
- 季節性を分離して見たい系列
系列をトレンド、季節成分、残差に分け、残差の極端な値を異常候補として見る方法です。
時系列 = トレンド + 季節成分 + 残差
y_t = trend_t + seasonal_t + residual_t
residual_t が極端に大きい、または小さい
↓
その時点は異常値の候補
方法 3: モデル残差を使う
手順は次の流れです。
- 全データでモデルを学習する。
- 学習データに対する当てはまりを計算する。
- 実測値とフィット値の差、つまり残差を求める。
- 残差が大きい点を異常値候補として扱う。
履歴データ → モデル学習 → 学習データへフィット
│
▼
残差を計算
│
▼
残差が大きい点を異常とみなす
初心者向けメモ
この方法は「モデルが説明できなかった点」を見つける発想なので、モデル自体が弱いと異常値の検出も不安定になります。まずは単純なベースラインで残差を見るところから始めると扱いやすくなります。
5.3 異常値と欠損値の処理方法
処理方法としては、次の三つが基本になります。
方法 1: 該当サンプルを削除する
向いている場面:
- 欠損や異常がごく少ない
- Prophet
- 機械学習
異常値や欠損値を含むサンプルを除外し、残りで学習する
注意点
削除は簡単ですが、欠損が連続している場合や、重要な繁忙期データを落としてしまう場合は、系列の構造そのものを壊すことがあります。
方法 2: 平均値で置き換える
向いている場面:
- 外生変数の影響が小さい
- 季節変動があまり強くない
欠損値 / 異常値 → 全体平均または近傍平均で補完
注意点
平均値補完は簡単ですが、ピークや谷をならしてしまいやすく、需要予測では実務感から離れることがあります。強い季節性がある系列では、同曜日平均や同時間帯平均の方が自然な場合もあります。
方法 3: Prophet でフィットして補完する
履歴データ → Prophet でフィット → 欠損箇所や異常箇所を予測値で補完
この方法は、欠損補完にも異常値の置き換えにも使えます。
注意点
補完モデルが強すぎると、現実のばらつきまで平滑化してしまうことがあります。補完した値は必ずフラグを持たせ、後から元データと区別できるようにしておくのが安全です。
6. 初心者向けの実務フロー
時系列予測を初めて組むときは、次の順番で整理すると進めやすくなります。
- 予測したい意思決定の粒度を決める。
- その粒度で必要な履歴長が足りているかを確認する。
- 非負、整数、区間制約など、出力条件を明文化する。
- 異常値と欠損値の発生理由を切り分ける。
- 単純なベースラインを作ってから複雑なモデルへ進む。
この順番を飛ばしていきなり深いモデルを試しても、原因切り分けが難しくなりがちです。特に実務では、「なぜ外れたのか」を説明できることが重要です。
7. まとめ
7.1 粒度ごとの考え方の整理
| 時間粒度 | 主な周期性 | 主な選択肢 |
|---|---|---|
| 週次 | 年周期、52 週 | ETS、ARIMA、Prophet、機械学習、深層学習 |
| 月次 | 年周期、12 か月 | ETS、ARIMA、Prophet、機械学習、深層学習 |
| 日次 | 7 日周期 + 365 日周期 | Prophet、機械学習、深層学習 |
| 日内 | 日周期 + 週周期 + 年周期 | Prophet、機械学習、深層学習、または日次予測後の配分 |
7.2 予測値制約の整理
| 目的 | 主な方法 |
|---|---|
| 整数にしたい | 四捨五入、ポアソン分布、負の二項分布 |
| 非負にしたい | 0 で打ち切る、対数変換、ReLU などの非負制約 |
区間 (a, b) に収めたい |
上下限で打ち切る、logit 型変換、Sigmoid + 線形変換 |
7.3 異常値と欠損値の整理
| 論点 | 方法 | 向いている場面 |
|---|---|---|
| 異常値検出 | 3σ ルール | 外生変数が少なく、トレンドや季節性が弱い |
| 異常値検出 | STL 分解 | 季節構造を分けて見たい |
| 異常値検出 | モデル残差法 | ベースラインモデルを作れる |
| 異常値 / 欠損値処理 | 削除 | 発生件数が少なく、影響範囲が小さい |
| 異常値 / 欠損値処理 | 平均値補完 | 構造が単純で、ざっくり補完したい |
| 異常値 / 欠損値処理 | Prophet 補完 | 時系列パターンを踏まえて補完したい |
8. チェックリスト
- 週次・月次・日次・日内のそれぞれで、どの周期を重視すべきか説明できるか。
- 系列数と履歴長の違いによって、Prophet と機械学習・深層学習をどう使い分けるか説明できるか。
- 予測値を整数、非負、区間内に制約する三つの考え方を整理できるか。
- カウントデータに対して、なぜポアソン分布や負の二項分布が候補になるのか説明できるか。
- 3σ ルール、STL 分解、モデル残差法の使いどころを言い分けられるか。
- 異常値や欠損値に対して、削除、平均補完、モデル補完をどう使い分けるか説明できるか。