時系列分析の基礎
YoY・STL・季節性補正
SEO流入・広告KPI・売上・PVのような事業データは、強い季節性とノイズを含む時系列データです。生の数字をそのまま前日比・前年比で読むと、季節要因と施策効果が混ざり、判断を誤りやすくなります。
たとえば BtoB SaaS の日次セッション数を 1 年分眺めると、1 本の折れ線の中に「月〜金で高く土日で半減する曜日効果」「3 月期末・9 月期末の駆け込み流入」「GW・お盆・年末年始の落ち込み」「コアアップデートでの突発下落」が全部混ざっています。これを トレンド(中長期の方向)・季節性(曜日・年内パターン)・残差(突発要因)の 3 層に分けて読むのが、時系列分析の基本姿勢です。
時系列分析の出発点は、観測値を トレンド・季節性・残差(ノイズ) の3成分に分解することです。移動平均で日次ノイズを取り、STL分解(時系列を 3 成分に統計的に分けるアルゴリズム、04 章で詳述)で曜日効果や年内パターンを分離し、季節調整後の残差で異常検知や施策効果を語る。前年比(YoY)も曜日・祝祭日・うるう年で補正してから比較するのが原則です。
01.まず結論:時系列は『トレンド × 季節性 × ノイズ』に分解する
事業の時系列データは、見かけ上1本の折れ線でも、中身は3つの成分の足し合わせです。これを意識しないと、「先週比+10%」が施策の成果なのか曜日効果なのか、「前年同月比-5%」が劣化なのか祝祭日の位置ずれなのかを切り分けられません。
| 成分 | 意味 | 例 |
|---|---|---|
| トレンド | 中長期で続く方向感(上向き/横ばい/下向き) | サイト成長による地道な流入増、ブランド浸透による検索数増 |
| 季節性 | 周期的に繰り返される動き | 曜日効果(BtoBの平日/週末)、月末売上、年末年始の落ち込み、新年度の伸び |
| 残差(ノイズ) | 上記2つで説明できない揺らぎや突発イベント | コアアップデート、TVCM、競合の参入、計測トラブル |
図:時系列データの3層分解(観測 = トレンド + 季節性 + 残差)
観測値(一番上)は、3つの成分の足し合わせ。STL分解はこの3層を統計的に分離して、施策効果と季節要因を切り分けるための前処理として使う。
02.移動平均と指数平滑|日次ノイズを丸める
最初に身につけたい時系列の前処理が 移動平均 です。日次データのジリジリした上下動を均して、トレンドが見やすい形に変換します。
| 手法 | 中身 | 向く場面 |
|---|---|---|
| 単純移動平均(SMA) | 直近 N 日の単純平均で平滑化 | 曜日効果を消す7日/月内変動を消す28日が定番 |
| 指数加重移動平均(EWMA) | 最近の値ほど重み付けして平均 | 直近の変化に追従させたい。アラート用途 |
| 中心移動平均 | 前後 N/2 日の平均(始端・終端は欠損) | トレンドの可視化(リアルタイム監視には向かない) |
| ローパスフィルタ | 周波数領域で高周波を落とす | 高度な季節性除去。実装コストは高い |
窓(ウィンドウ)は、移動平均で一度に平均する範囲のことです。たとえば窓 7 日なら、毎日「その日を含む直近 7 日間」の値を平均して 1 点を出し、窓を 1 日ずつずらしながら系列全体に適用します。これがコード上の data.rolling(window=7).mean() の中身です。
窓サイズは、消したい季節性の周期の整数倍を選ぶのがコツです。週次の曜日効果(土日落ち)を消したいなら 7 日、月次の繰り返しを消したいなら 28 日(または 30 日)が定番。中途半端な日数(例:10 日)を選ぶと、特定の曜日が窓内に 2 回入ったり 1 回しか入らなかったりして、平滑化したつもりで波打ちが残ります。
# 移動平均と指数平滑(pandas)
import pandas as pd
# data: DatetimeIndex を持つ Series(日次トラフィック)
sma_7 = data.rolling(window=7).mean() # 7日単純移動平均
sma_28 = data.rolling(window=28).mean() # 28日単純移動平均
ewma = data.ewm(span=14, adjust=False).mean() # 指数加重(半減期14日相当)
# 中心移動平均(前後3.5日 = 7日窓)
sma_center = data.rolling(window=7, center=True).mean()コードを書かずに試したい場合は、下のツールが使えます。本章のような「緩やかな増加トレンド+曜日の波」を持つ日次データを読み込んであり、線形トレンドの延長による簡易予測と95%予測区間を表示します。トレンド線が曜日の波を均してしまう(=季節性はトレンドでは捉えられない)感覚も、そのまま体感できます。
時系列データを古い順に貼り付けると、線形トレンドで先の期間を予測し、95%予測区間つきのグラフと予測値一覧をその場で表示します(ブラウザ内で計算、サーバー送信なし)。
入力フォーマットの例を見る
Excel・Googleスプレッドシートで範囲をコピーしてそのまま貼り付け(タブ区切り)できるほか、CSV(カンマ区切り)・スペース区切りにも対応しています。1行目が見出しの場合は自動で判定します。日付や文字列など数値でないセルは自動的に除外されるので、きれいに整形してから貼り付ける必要はありません。
月,売上(万円) 2026/01,502 2026/02,531 2026/03,520
公開データで試す
ボタンを押すと、お使いのブラウザから各公開APIへ直接アクセスして実データを取得し、入力欄へ読み込みます(当社サーバーは経由しません)。為替やビットコインなどの金融価格は「過去のトレンドが続く」前提が特に成り立ちにくいため、ツールの動きを体験する用途でお使いください。
03.YoY・MoM・WoWの正しい使い分けと罠
マーケティングのレポートでよく登場する YoY(前年同期比)・MoM(前月比)・WoW(前週比)は、便利な反面、季節性や曜日効果を含んだまま比較してしまう落とし穴があります。
| 比較 | 計算 | 向く場面 | 罠 |
|---|---|---|---|
| YoY(前年同期比) | 今年の値 / 前年同期の値 − 1 | 中長期トレンドの把握。季節性をまたいだ比較 | 祝祭日の位置ずれ、うるう年、コアアップデートの混入 |
| MoM(前月比) | 今月 / 前月 − 1 | 月次施策の効果 | 月の日数差、月末月初の曜日ずれ |
| WoW(前週比) | 今週 / 前週 − 1 | 短期トレンド・施策直後の反応 | 祝日週は大きくぶれる。1週間では季節性除去不可 |
| 同一曜日比較 | 今週月曜 / 先週月曜 − 1 等 | 曜日効果を除去した短期比較 | 祝日が絡むと意味が薄い |
| YoY同曜日比較 | 今年の第N週月曜 / 前年第N週月曜 | 曜日と季節性を同時に揃えた比較 | 祝日の位置と前年祝日が同じか確認 |
5月のゴールデンウィークが「5月3日が水曜」の年と「5月3日が土曜」の年では、平日数も訪問パターンも変わり、流入の YoY 比較に直接効きます。さらにうるう年(2月29日の有無)で2月のYoYが2〜3%ずれます。これらを補正せずに「YoYで-3%だから劣化」と判定するのは早計です。祝祭日カレンダーをモデルに持ち込み、当該日と隣接日を別フラグで扱うのが安全な実務です。
04.STL分解|トレンド・季節性・残差の分離
STL(Seasonal and Trend decomposition using Loess)は、時系列を トレンド・季節性・残差 の3成分に分解する代表的な手法です。Loess(局所重み付き回帰)を使うため、季節パターンが少しずつ変わっていく時系列にも対応できます。
| 観点 | 加法分解(Additive) | 乗法分解(Multiplicative) |
|---|---|---|
| 式 | 観測 = トレンド + 季節 + 残差 | 観測 = トレンド × 季節 × 残差 |
| 向く場面 | 季節変動の幅がトレンドに依存しない | 値が大きいほど季節変動も大きい(成長期) |
| 例 | PV、定常的なCVR | 売上、急成長中のサイト |
| 対処 | そのまま STL | log を取って加法に戻すか、`seasonal_decompose(model='multiplicative')` |
# STL分解(statsmodels)
from statsmodels.tsa.seasonal import STL
import pandas as pd
# data: DatetimeIndex を持つ Series。少なくとも 2〜3 周期分のデータがあると良い
stl = STL(data, period=7, robust=True) # 週次季節性なら period=7
res = stl.fit()
trend = res.trend # トレンド成分
seasonal = res.seasonal # 季節性成分
resid = res.resid # 残差(ノイズ)
# 季節調整値(観測 − 季節性)。施策効果や異常検知はこれで議論する
season_adjusted = data - seasonal05.季節性の種類と扱い方
実務の時系列データは、複数の季節性が重なっているのが普通です。最低でも以下の4つは分けて扱えると、レポートの精度が大きく上がります。
| 季節性 | 周期 | 実務での出方 |
|---|---|---|
| 曜日効果(週次) | 7日 | BtoB:平日に高く、土日に下がる。BtoC:週末に伸びるカテゴリも |
| 月内パターン | 約28〜31日 | 給料日効果、月末締めの駆け込み、月初の予算消化 |
| 年内パターン | 365日 | 夏休み・年末年始の落ち込み、新年度・受験期の伸び、業界別の繁忙期 |
| 祝祭日効果 | 不規則 | GW・お盆・年末年始は曜日と組み合わさってさらに複雑 |
通常の STL 分解は1 つの周期しか扱えません。実務では「週次(7日)」と「年次(365日)」のように複数の季節性が重なっているため、片方しか扱わないと残差にもう片方が漏れ出します。
たとえば BtoB SaaS の日次セッション数を 1 年分分析するとき、「月〜金で高く土日で半減する曜日効果」と「3 月期末・9 月期末で伸びる年次効果」、さらに「GW・お盆・年末年始の落ち込み」を同時にモデル化したい。このとき選択肢になるのが次の 2 つです。
- MSTL(Multiple STL、statsmodels 0.14+):複数の季節周期を順に Loess で分解する。
MSTL(data, periods=(7, 365)).fit()のように周期のタプルを渡すだけで、週次と年次を別の成分として取り出せる。 - Prophet(Meta 製):週次・年次の季節性を内蔵し、GW・お盆など祝祭日テーブルもデフォルトで扱える。
add_regressor()で「TVCM 放映日のフラグ」「セール期間のフラグ」のような外部変数も一緒に推定でき、施策効果と季節効果を切り分けやすい。
使い分けの目安:純粋に分解して残差を見たいだけなら MSTL、祝祭日や外部キャンペーンの効果まで切り分けたいなら Prophet。SEO 流入や売上のように祝祭日と外部要因が強く効く事業データは Prophet を選ぶケースが多いです。
# Prophet で複数季節性と祝祭日・外部変数を同時に扱う
from prophet import Prophet
import pandas as pd
# ds(日付)と y(値)の2列が必要
df = data.reset_index().rename(columns={"date": "ds", "sessions": "y"})
holidays = pd.read_csv("jp_holidays.csv") # 祝祭日カレンダー(ds, holiday列)
m = Prophet(
weekly_seasonality=True,
yearly_seasonality=True,
holidays=holidays,
)
m.add_regressor("tvcm_flag") # TVCM 放映日のフラグ
m.add_regressor("campaign_flag") # キャンペーン期間のフラグ
m.fit(df)
forecast = m.predict(df)
# forecast["weekly"] / forecast["yearly"] / forecast["holidays"] で
# 成分ごとに分解結果が取れる。残差は y - yhat。06.異常検知の入口|移動平均±σ・Z-score・残差
日々のKPIを監視していると、「これは普段の揺らぎなのか、本物の異常なのか」を判定したい場面が出てきます。一番シンプルで運用しやすいのが 移動平均±σ による管理図(SPC: Statistical Process Control)の発想です。
図:管理図(移動平均±2σ)による異常検知
中心線(移動平均)と上下の管理限界(±2σ)の帯から外れたら異常として検知する。SPC(統計的工程管理)の考え方をKPI監視に流用する形。
オレンジ点が±2σの帯から外れた異常。実務では「2σ越えが連続2点」「中心線の片側に7点連続」など Western Electric ルールも組み合わせる。
| 手法 | 中身 | 向く場面 |
|---|---|---|
| 移動平均±2σ | 中心線±2標準偏差の帯から外れたら異常 | 週次・月次の安定指標。シンプルで説明しやすい |
| Z-score | (観測 − 平均) / 標準偏差。|Z| > 2 や 3 で異常 | 1点単位の判定。簡易ダッシュボード向き |
| Western Electric ルール | 「±2σ越え2点」「中心線片側に7点連続」など複合条件 | 誤検知を減らしたい本番監視 |
| Isolation Forest | 多次元データの異常スコアを学習で出す | 複数KPIを同時に監視。流入×CVR×滞在の同時異常 |
| 残差での異常検知 | STL分解の残差に±σを適用 | 季節性を除いてから判定したい |
BtoBサイトの日次トラフィックを生のまま±2σで監視すると、毎週土日が「異常」として検知され、アラート疲れになります。必ず季節性を除いた残差で異常検知するか、曜日別に基準値を切り分けるのが運用上の必須条件です。「曜日別中央値±2σ」「STL残差±2σ」「Prophet の予測区間外れ」のいずれかが現実的な選択肢です。
07.季節性補正したKPIレポートの作り方
最後に、ここまでの考え方を組み合わせて、季節性に振り回されないKPIレポートを作るときのチェックリストをまとめます。Looker Studio・Tableau・社内ダッシュボードのどれでも応用できる骨格です。
| チェック項目 | 中身 | 具体例 |
|---|---|---|
| 01. 比較軸を3層用意 | WoW・MoM・YoY を並べて、短中長期のトレンドを同時に見る | 短期:WoW、中期:MoM、長期:YoY |
| 02. 曜日効果を除く | 7日移動平均または同一曜日比較を主軸にする | 「日次の生データ + 7日移動平均」の2本立て |
| 03. 祝祭日フラグ | 祝祭日とその前後を別フラグで管理し、レポート上で示す | GW・お盆・年末年始の期間はバナーで明示 |
| 04. STL分解 or Prophet | 重要KPI には季節調整値を併記 | 売上・主要LPのCVRはトレンド成分も並べる |
| 05. アラート閾値 | 残差ベースの±2σで自動アラート | Slack 通知 + 異常時のセグメント別ドリルダウン |
| 06. 介入マーカー | コアアップデート・大型施策の日付をグラフに重ねる | Looker Studio の参照線、Tableau の注釈 |
| 07. ガードレール指標 | 目的指標と一緒に逆方向の指標も並べる | CVR ↑ と一緒に離脱率・読み込み時間も監視 |
08.よくある質問(FAQ)
STL分解は何日分のデータがあれば使えますか?
STL の理屈上は周期の 2 倍以上あれば計算できますが、実用上は 3〜4 周期分 を推奨します。週次季節性なら最低 28 日、できれば 8〜12 週。年次季節性なら最低 2 年分、できれば 3 年分です。データが短いとトレンドと季節成分の分離が不安定になり、ノイズに引っ張られて変な季節パターンを学習することがあります。短い場合は素直に移動平均と曜日別平均で代用するのが安全です。
移動平均と指数平滑、どちらを使うべきですか?
用途で分けます。レポートの可視化・中長期トレンドの把握は 単純移動平均(SMA) が読みやすく、直近の動きへの追従やアラートには 指数加重移動平均(EWMA) が向きます。同じデータに対して SMA と EWMA を並べてプロットすると、トレンドが加速しているか減速しているかが直感的に分かります。両者をレポートに同時表示するチームも多いです。
異常検知の閾値は何σが良いですか?
ビジネス指標の本番監視では ±2σ を起点にするのが運用しやすいラインです。±2σ は理論上 約 4.55% の確率で偶然超えるので、毎日監視していると 約 22 日に 1 回はアラートが出る計算になります。アラート疲れが起きるなら ±3σ(年に数回レベル)に緩め、逆に静かすぎるなら Western Electric ルール(±2σ越え2点連続、中心線片側に7点連続など)を組み合わせて感度を上げます。生データではなく 季節調整後の残差で σ を取る のが大前提です。
週次と年次の季節性が同時にあるとき、どう扱えば良いですか?
通常のSTL分解は単一周期を前提にしているため、週次(7日)と年次(365日)が同時に効くデータには MSTL(Multiple STL、statsmodels 0.14+)または Prophet(Meta製)が向きます。Prophet はトレンド・複数の季節性・祝祭日効果・外部変数を一括で扱える設計で、コードもシンプルなので実務での定番になっています。SEO 流入や売上のような複合季節性データには Prophet を第一候補にする運用が多いです。
Prophetは使うべきですか?scikit-learnやARIMAで十分ですか?
マーケや事業データの予測・KPI 監視なら、Prophet が扱いやすく短コードで済むため第一選択になりやすいです。理論的な厳密さ・小サンプルなら ARIMA/SARIMA、説明可能性を重視するなら線形回帰や状態空間モデル(statsmodels の UnobservedComponents)も選択肢です。Prophet はトレンドの自動チェンジポイント検出、祝祭日効果、外部変数の追加が標準装備なので、最初の一手として広く使われています。
YoY比較で『前年が異常値だった』場合、どう処理すれば良いですか?
前年の異常値をそのまま比較対象に使うと、施策効果と前年の異常イベントが混ざります。対処は2系統。1つは 補正YoY として、前年の異常値を年間トレンドや前々年で補完した推定値に置き換えて比較する。もう1つは 注釈付きYoY として、生のYoYに「前年は◯月に障害/コアアップデートで-15%だった」のような注釈を必ず併記する。BIツール側で異常区間に網掛けやマーカーを出す機能が使えるならそれが便利です。
09.まとめ
時系列分析の基礎は、観測値を トレンド・季節性・残差 の3層に分解することから始まります。日次ノイズは移動平均で丸め、曜日や年次の季節性は STL 分解や Prophet で分離し、施策効果や異常は季節調整後の残差で議論する。これだけで、生データで議論していた頃の判断ミスの大半は防げます。
YoY・MoM・WoW は便利ですが、祝祭日・曜日・うるう年で簡単にぶれます。同一曜日比較と祝祭日補正を当たり前にしておくと、レポート品質が一段上がります。複雑なモデルに踏み込む前に、まずはここまでを徹底するのがおすすめです。
SEO分析の統計入門|順位別CTR・カニバリ検知・記事の鮮度低下
季節性補正・コアアップデートの介入分析・記事の鮮度低下の中央値曲線比較までを整理します。
A/Bテスト設計の基礎
MDE・サンプルサイズ・検定の使い分け・多重比較・早期停止までを初学者向けに整理します。
Search Console × BigQuery × Looker Studioで分析基盤を作る
長期トレンドとロングテールクエリを扱うための分析基盤の構成と SQL サンプルを整理します。
データ分析でよく使うアルゴリズム・指標まとめ
類似度・一致度・予測モデル評価の3カテゴリで指標を整理した総合解説です。
KPI 設計と時系列ダッシュボード構築をご相談ください
季節性補正済みのKPIレポート設計、Looker Studio・BigQueryでの異常検知パイプライン構築の伴走支援を行っています。お気軽にお問い合わせください。

