時系列・ベイズ・MMMの素養 — 介入効果と「予算配分」の入口(連載第7回)
個人単位の因果手法の先にある、集計レベルの手法(CausalImpact・MMM)へ進むための基礎を扱います。時系列の定常性、ベイズの考え方、CausalImpactで離散介入の増分を測る方法、そしてMMMの位置づけと「いつ解禁されるか」を、架空企業SOLNAの動くコードで整理します。深入りはせず、地図を持つことが目的です。
by Shin
LP刷新やメルマガ開始のような「いつ起きたか明確な一回きりの介入」は、どう測れば効果と言えるのでしょうか。答えは、無影響なコントロール系列を使って「もし介入がなかったら」を時系列で推定する——CausalImpactです。この回では、時系列とベイズの最小限の素養を入れ、架空のD2C企業SOLNAのLP刷新を実際に測り、最後にMMMが「いつ解禁されるか」までを地図にします。深入りは書籍に譲り、ここでは順序とつながりだけを渡します。
到達目標
- CausalImpactで離散介入の増分を、反実仮想つきで測れる。
- ベイズの「事前 → 事後」「不確実性を分布で持つ」という発想を、実務の言葉で説明できる。
- MMMの成立条件と限界を言え、取引件数が少なく履歴が短い場合は「いまやらない」と判断し、発動条件を書ける。
- 地域・店舗が増えたとき、階層ベイズで何が解けるようになるかを位置づけられる。
前提
第4〜6回で、潜在結果・ATE・DAG・観測データの因果手法(傾向スコア/DiD/RDD)と、CATE・upliftまでを扱いました。本回はその上に「時間軸の介入」と「集計レベルの予算配分」という、診断フレームの上段——個人単位ではなく集計レベルで効果を測る手法——へ進むための基礎を重ねます。SOLNAのスキーマ(orders / sessions / ad_spend など)は前回までと共通です。
時系列の基礎(定常性・季節性の最小限)
時系列で最初に押さえるのは二つだけです。定常性(平均・分散が時間で大きく動かないか)と季節性(曜日・月の周期があるか)。これを見ないまま「前年比で上がった」を効果と呼ぶと、トレンドや季節をまるごと効果に計上してしまいます。
SOLNAの日次購入には、週末の伸びと給料日付近の山があります。まず素朴に分解して、何が周期で何がトレンドかを目で確認します。
import pandas as pd
import statsmodels.api as sm
from statsmodels.tsa.stattools import adfuller
# daily: index=日付, columns=[purchases, brand_search_clicks, youtube_sessions]
daily = pd.read_parquet("solna_daily_series.parquet").asfreq("D")
# 1) 定常性の目安(ADF検定)。p値が小さいほど「定常」寄り
adf_stat, adf_p, *_ = adfuller(daily["purchases"].dropna())
print(f"ADF p = {adf_p:.3f}") # `p < 0.05` なら定常寄り。大きければトレンド/季節が残る
# 2) 季節分解(週周期を仮定)。trend / seasonal / resid を分けて目視
decomp = sm.tsa.seasonal_decompose(daily["purchases"], model="additive", period=7)
decomp.plot()ベイズの考え方(事前 → 事後・不確実性を分布で)
ベイズは難しい理論ではなく、態度です。「効果は件」と点で言い切らず、「効果はの確からしさでこの区間にある」と分布で持つ。手元の知識(事前分布)に、観測したデータ(尤度)を掛けて、更新後の信念(事後分布)を得る。それだけです。
実務上の効きどころは三つあります。第一に、不確実性が最初から出力に入るので「数字の一人歩き」を防げる。第二に、サンプルが少ない群を、似た群の情報で補える(後述の階層ベイズ)。第三に、CausalImpactもMMMも内部はベイズで、出力は点ではなく区間で返ってくる——その区間を読めることが、上段の手法を使う前提になります。
CausalImpactで離散介入の増分を測る(SOLNA:LP刷新)
ここが本回の中心です。SOLNAは2026-03-01にトップLPを刷新しました。「刷新後に購入が増えた」だけでは、季節やキャンペーンと区別できません。CausalImpactは、介入の影響を受けないコントロール系列から「介入がなかった場合の購入数」を予測し、実測との差を増分(反実仮想)として返します。
コントロールの選び方が生命線です。条件は二つ。介入前にターゲットとよく連動していること、そして介入の下流にないこと。LP刷新はサイトに着地した後のCVRを動かすので、コントロールには着地より上流の需要シグナルを置きます。ブランド検索のクリック数(需要の代理)と、刷新の影響を受けないYouTube経由の日次セッションが候補です。
まず日次系列をBigQueryで組みます。
-- SOLNA 日次系列: target=購入数, controls=ブランド検索クリック / YouTubeセッション
WITH purchases AS (
SELECT DATE(order_at) AS d, COUNT(*) AS purchases
FROM `solna.orders`
WHERE order_type IN ('first', 'repeat', 'subscription')
GROUP BY d
),
brand_search AS ( -- 需要の代理(LP着地より上流=介入の影響を受けにくい)
SELECT date_day AS d, SUM(clicks) AS brand_search_clicks
FROM `solna.ad_spend`
WHERE channel = 'Google' AND campaign LIKE '%brand%'
GROUP BY d
),
yt_sessions AS ( -- LP刷新の下流ではない別チャネルの流入
SELECT DATE(session_start) AS d, COUNT(*) AS youtube_sessions
FROM `solna.sessions`
WHERE channel = 'YouTube'
GROUP BY d
)
SELECT p.d, p.purchases, b.brand_search_clicks, y.youtube_sessions
FROM purchases p
LEFT JOIN brand_search b USING (d)
LEFT JOIN yt_sessions y USING (d)
ORDER BY p.d次に、介入日の前後で期間を切ってCausalImpactを走らせます。ターゲットを先頭列、以降をコントロールに置くのが約束です。
from causalimpact import CausalImpact
# 先頭列=target、2列目以降=controls の順に並べる
data = daily[["purchases", "brand_search_clicks", "youtube_sessions"]]
pre_period = ["2025-12-01", "2026-02-28"] # 学習期間(介入前)
post_period = ["2026-03-01", "2026-04-30"] # 評価期間(介入後)
ci = CausalImpact(data, pre_period, post_period)
print(ci.summary()) # 平均/累積の増分と95%区間
print(ci.summary("report")) # 文章での解釈
ci.plot() # 実測 vs 反実仮想予測、差分、累積差分読み方はこうです。summary の累積増分(cumulative)が「刷新によって追加で得られた購入数」、その区間がゼロをまたいでいれば「効いたとは言えない」。plotの上段で実測(黒)が予測帯(青)を継続的に上回っていれば、視覚的にも増分が立ちます。
この手法が、実験面を持たないB2Bでも最初に効く理由は、比較地域も明確な閾値も要らず、「明確な介入時点」だけを必要とするからです。介入時点は自分で作れます。取引件数が少なく単一市場でも成立する数少ない因果手法であることは、対の因果編で判断軸として整理しています。
MMMとは何か、そしていつ解禁されるか
MMM(マーケティングミックスモデル)は、週次の売上を各チャネルの出稿で説明し、予算配分を最適化するための集計レベルの回帰です。鍵は二つの非線形を入れること。adstock(広告効果は当日で消えず数週尾を引く)と飽和(出せば出すほど効くわけではなく、逓減する)。この二つを入れて初めて「いまMetaにあと万入れたら売上はいくら増えるか」という限界収益の問いに答えられます。
最小構成はこう書けます。ただしSOLNAではまだ走らせません——理由は次節です。発動条件を満たした未来のために、形だけ置いておきます。
# 発動条件を満たしてから実行する想定の最小構成(PyMC-Marketing)
from pymc_marketing.mmm import MMM
from pymc_marketing.mmm import GeometricAdstock, LogisticSaturation
# weekly: 週次の売上(y) と チャネル別出稿(spend) + コントロール(季節/価格/在庫)
mmm = MMM(
date_column="week",
channel_columns=["meta_spend", "google_spend", "line_spend", "yt_spend"],
control_columns=["price_index", "is_promo_week"],
adstock=GeometricAdstock(l_max=8), # 効果が尾を引く週数
saturation=LogisticSaturation(), # 逓減(飽和)
)
mmm.fit(X=weekly.drop(columns="sales"), y=weekly["sales"])
mmm.plot_channel_contributions() # チャネル別の寄与(区間つき)
# mmm.optimize_budget(...) で配分提案。ただし区間が広ければ採用しない寄与もROIも点ではなく区間で出る点が肝心です。区間が広ければ、配分提案は「示唆」止まりで、意思決定には使いません。これはベイズの態度がそのまま実務判断に効く例です。
なぜ取引件数が少なく履歴も短いうちはMMMが時期尚早か
MMMは集計統計に依拠するので、サンプル数(週数)と各週の成果件数に支配されます。adstockと飽和という非線形パラメータを安定して推定するには、経験的に週次で1.5〜2年の履歴と、各週に十分な成果が必要です。SOLNAは取引件数の多いD2Cなので件数は足りますが、週次系列が貯まるまでは推定が不安定です。だから本連載では、MMMを「いま実装する手法」ではなく「発動条件つきの予約」として扱います。
| 手法 | いまやらない理由 | 発動条件(再評価のトリガー) |
|---|---|---|
| MMM | 週次履歴が短く、adstock/飽和の推定が不安定 | 週次の支出×売上が1.5〜2年規模で揃ったとき |
| 地域実験(geo) | 集計レベルの対照群がまだ薄い | 複数地域・複数店舗へ展開したとき |
ここに、本連載が一貫して見せている非対称があります。SOLNA(取引件数が多く複数地域を持つ)はやがてMMMもgeo実験も解禁されますが、対の因果編で扱う取引件数が少なく単一市場のB2Bは、件数と市場の構造そのものが足りず、MMMもgeo実験も原理的に成立しません。同じ手法でも、データの構造が成立条件を握っている——「手法から考えず、データが許す手法を選ぶ」という連載の主旋律が、上段でもそのまま効きます。
階層ベイズの出番(地域・店舗が増えたら)
地域や店舗が増えると、各地域のデータは薄くなりがちです。地域ごとに別々に推定すると、件数の少ない地域の数字が暴れます。逆に全地域をひとまとめにすると、地域差を潰してしまう。この中間を取るのが**階層ベイズ(部分プーリング)**です。地域効果を「共通の親分布から生まれた子」として推定し、データの薄い地域は全体平均に、厚い地域は自分のデータに寄せて、自動でバランスします。
実務的には、CausalImpactで個別介入の増分を測りつつ、複数地域・複数店舗の効果を束ねて語りたくなった段階が、階層ベイズ(PyMCや緑本のGLMM、Stan)の出番です。これも「最初からやる」のではなく、集計レベルが育ったときに開く一段、と位置づけます。
対応書籍と読みどころ
各書とも、本連載の地図の中での役割で挙げます。
- 金本『因果推論』(時系列章):理論側の参照軸。観測因果から時系列の介入効果へ橋を架ける章があり、CausalImpact的な発想の理論側を補強します。
- 久保『データ解析のための統計モデリング入門』(緑本・階層ベイズ):GLMから階層モデルへ自然に登る一冊。第3回でGLMを前振りした流れが、ここで階層ベイズとして回収されます。前節の部分プーリングの直感はこの本で固まります。
- 松浦『StanとRでベイズ統計モデリング』(アヒル本):ベイズを「書いて動かす」ための実装書。事前→事後を手で回す感覚を付けたい人向け。
- 馬場(ベイズ・状態空間):CausalImpactの内側にある状態空間モデル(BSTS)を理解するための橋。トレンド・季節をどう状態として持つかが腑に落ちます。
フレームとの接続
本回は診断フレームの上段——集計レベルの手法——へ進むための基礎です。CausalImpactは「明確な介入時点」というゲートで開く手法、MMMは「週次履歴×成果件数」というゲートで開く手法です。成熟度のステップでは因果段階の上端から自動化の入口に位置し、そして本回の核心は**「やらない判断」を発動条件つきで書くこと**にあります。やらない手法を、トリガーと一緒に台帳化する——これ自体が意思決定の質を上げます。
章末チェックリスト
- 「上がった」を「効いた」と言い換えていないか(反実仮想を作ったか)
- CausalImpactのコントロールは、介入の下流にない上流シグナルか
- 介入日を跨ぐ計測方式の変更(タグ・CV定義)がないか確認したか
- 効果を点ではなく区間で読み、ゼロをまたぐかを見たか
- MMMを「いま」やろうとしていないか。週次履歴の発動条件を書いたか
- 地域・店舗が増えたとき、階層ベイズに切り替える条件を持っているか
- やらない手法を、発動条件とセットで台帳に残したか
ここまでは「上段の手法をどう実装し、いつ解禁するか」というHOWでした。自社のデータ構造でCausalImpactが成立するか、MMMが時期尚早かという判断は、対の因果編(実験が打てない現場の因果測定)で、取引件数が少なく単一市場のB2Bを例に整理しています。手法から入らず、データが許すものから選ぶ——その判断軸を確認してから戻ってきてください。