🎓 Phase 6: MMM & 予算最適化の実践
18週間の学習を完成させる最終章
🚀 Phase 6へようこそ - 18週間の集大成
おめでとうございます! Phase 0から5までの17週間、因果推論、ベイズ統計、時系列分析、機械学習、そしてLLMとの統合まで、膨大な知識を習得してきました。
Phase 6は、これらすべての知識を「マーケティング予算最適化」という実務の最終ゴールに統合する最終章です。ここでは、Marketing Mix Modeling(MMM)を中心に、DoWhy・CausalMLの体系的活用、そして予算最適化シミュレーションまで、プロのマーケティングサイエンティストとして必要なすべてを完成させます。
Phase 6で達成すること
- Marketing Mix Modeling(MMM):広告の飽和効果とキャリーオーバー効果をモデル化し、チャネル別ROIを算出
- DoWhy & CausalML完全マスター:因果推論ライブラリを体系的に使いこなす
- 予算最適化:MMMの結果から最適な予算配分を自動計算
- Phase 0-6完全統合:18週間の学習すべてを1つのプロジェクトで活用
Phase 6の特別な位置づけ
Phase 6は単なる「次の章」ではありません。これは18週間の学習を完成させる「卒業プロジェクト」です。Phase 0-5で学んだすべての技術が、ここで1つに統合されます。
- Phase 1の因果推論 → DoWhyで体系化
- Phase 2のベイズ統計 → MMMの事前分布設計に活用
- Phase 3の時系列分析 → キャリーオーバー効果のモデリング
- Phase 4の機械学習 → CausalMLでUplift Modeling
- Phase 5のLLM → 経営層向けレポート自動生成
使用する最新ツール(2025年10月版)
PyMC-Marketing v0.16.0
最新
2025年9月リリース。DelayedSaturatedMMM、GPU対応、複数NUTSサンプラー対応。
DoWhy (PyWhy)
成熟
4ステップAPIで因果推論を体系化。GCM拡張で因果探索にも対応。
CausalML v0.15.5
実務的
2025年7月リリース。Uplift ModelingとMeta-Learnersの完全実装。
📚 Phase 6の学習ロードマップ
Week 1: Marketing Mix Modeling(MMM)
広告効果のモデリングとチャネル別ROI推定を完全マスター
Week 2: DoWhy & CausalMLの体系的活用
Phase 1-5の因果推論手法をライブラリで統合
Week 3: 予算最適化と総合プロジェクト
18週間の集大成としての完全な意思決定支援システム構築
目標:広告の飽和効果とキャリーオーバー効果をモデル化し、チャネル別ROIを正確に推定できるようになる。
MMMの理論基礎(月曜日)
学習内容:
- Marketing Mix Modelingとは何か?Phase 1-5との違い
- MMMの3つの核心概念:ベースライン需要、飽和効果、キャリーオーバー効果
- なぜ単純な回帰分析では不十分なのか
- MMMの歴史と最新トレンド(Bayesian MMM)
MMMの基本方程式
MMMは売上(または成果指標)を以下のように分解します:
- Yt:時点tでの売上(成果指標)
- baselinet:広告がなくても発生する基礎需要(季節性、トレンド等)
- adstockc(Xc,t):チャネルcの広告費に対するキャリーオーバー効果(遅延効果)
- effect(...):飽和効果(限界収穫逓減)を表す関数
- εt:誤差項
Phase 1-5との違い:MMMはなぜ特別なのか
- Phase 1(因果推論):処置効果を推定(RCT、DID、PSM等)→ MMMは「複数の連続的な処置」を同時にモデル化
- Phase 2(ベイズ統計):事前分布と事後分布 → MMMでは広告効果の不確実性を定量化
- Phase 3(時系列):VARモデルで時系列の相互依存 → MMMではAdstockで遅延効果をモデル化
- Phase 4(機械学習):CATEを推定 → MMMではチャネル別ROIを推定
- Phase 5(LLM):因果探索で構造を発見 → MMMでは事前知識を前提に効果を推定
つまり、MMMはこれまで学んだすべての技術の統合形態であり、実務の最終ゴールです。
広告の飽和効果(Saturation Effect)(火曜日)
学習内容:
- 飽和効果の経済学的意味:限界収穫逓減の法則
- 飽和関数の種類:Logistic、Hill、Adbudg
- PyMC-Marketingでの実装方法
- パラメータの解釈(半飽和点、曲率)
主要な飽和関数
1. Hill関数(推奨)
- k:半飽和点(効果が最大値の50%に達する広告費)
- s:曲率(s > 1で急速な飽和、s < 1で緩やかな飽和)
2. Logistic関数
- L:最大値
- k:成長率
- x₀:変曲点
3. Adbudg関数
# 飽和効果の実装例(PyMC-Marketing)
import pymc as pm
from pymc_marketing.mmm import saturation# Hill飽和関数の実装def hill_saturation(x, half_saturation, slope):
"""
Hill飽和関数
Parameters:
-----------
x : array-like
広告費(入力値)
half_saturation : float
半飽和点(効果が50%に達する広告費)
slope : float
曲率(>1で急速な飽和、<1で緩やかな飽和) """
return x**slope / (half_saturation**slope + x**slope)
# 実データでの使用例
import numpy as np
import matplotlib.pyplot as plt
# シミュレーションデータ
ad_spend = np.linspace(0, 10000, 100)
# 異なるパラメータでの飽和曲線
saturated_1 = hill_saturation(ad_spend, half_saturation=3000, slope=2)
saturated_2 = hill_saturation(ad_spend, half_saturation=5000, slope=1)
saturated_3 = hill_saturation(ad_spend, half_saturation=3000, slope=0.5)
# 可視化
plt.figure(figsize=(10, 6))
plt.plot(ad_spend, saturated_1, label='k=3000, s=2 (急速な飽和)')
plt.plot(ad_spend, saturated_2, label='k=5000, s=1 (標準)')
plt.plot(ad_spend, saturated_3, label='k=3000, s=0.5 (緩やかな飽和)')
plt.xlabel('広告費')
plt.ylabel('飽和後の効果')
plt.title('Hill飽和関数の比較')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
不動産マーケティングでの飽和効果の実例
オフィス仲介サイトでの飽和効果の例:
- Google広告:月額50万円までは線形に効果が増加、それ以降は飽和(競合と広告枠の奪い合い)
- SNS広告:オフィス探しという特定ニーズのため、早期に飽和(ターゲット層が限定的)
- SEO投資:緩やかな飽和(コンテンツ充実により長期的に効果が持続)
アクション:飽和点を超える予算は他チャネルに配分すべき
キャリーオーバー効果(Adstock)(水曜日)
学習内容:
- キャリーオーバー効果とは:広告の遅延効果
- Adstock関数の種類:Geometric、Weibull
- 減衰率(decay rate)の意味と推定
- 遅延期間(lag)の設定方法
Adstock(キャリーオーバー効果)の理論
広告は即座に効果を発揮するのではなく、時間をかけて徐々に減衰しながら効果を発揮します。これをAdstockと呼びます。
1. Geometric Adstock(最もシンプル)
- α:減衰率(0 < α < 1)。α=0.7なら1週間後に70%の効果が残る
- 再帰的定義:過去の広告効果が現在に累積される
2. Weibull Adstock(より柔軟)
- k:形状パラメータ(効果のピーク時期を制御)
- λ:スケールパラメータ(効果の持続期間)
- より現実的なモデリングが可能(例:テレビCMは即時効果、SEOは遅延効果)
# Adstock関数の実装(Geometric & Weibull)
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import weibull_mindef geometric_adstock(x, decay_rate, max_lag=8):"""
Geometric Adstock変換
Parameters:
-----------
x : array-like
広告費の時系列データ
decay_rate : float
減衰率(0 < decay_rate < 1) max_lag : int 最大遅延期間 """
adstocked = np.zeros_like(x, dtype=float)
adstocked[0] = x[0]
for t in range(1, len(x)):
adstocked[t] = x[t] + decay_rate * adstocked[t-1]
return adstocked
def weibull_adstock(x, shape, scale, max_lag=8):
"""
Weibull Adstock変換
Parameters:
-----------
x : array-like
広告費の時系列データ
shape : float
形状パラメータ(k)
scale : float
スケールパラメータ(λ)
max_lag : int
最大遅延期間
"""
# Weibull重みの計算
lags = np.arange(0, max_lag + 1)
weights = weibull_min.pdf(lags, c=shape, scale=scale)
weights = weights / weights.sum() # 正規化
# Convolution(畳み込み)でAdstock効果を適用
adstocked = np.convolve(x, weights, mode='same')
return adstocked
# 実例:週次広告費データ
weeks = 20
ad_spend = np.zeros(weeks)
ad_spend[5] = 100 # 第5週に100万円の広告を出稿
# 異なるAdstockモデルの比較
geometric_05 = geometric_adstock(ad_spend, decay_rate=0.5)
geometric_08 = geometric_adstock(ad_spend, decay_rate=0.8)
weibull_effect = weibull_adstock(ad_spend, shape=2, scale=3)
# 可視化
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 8))
# Geometricの比較
ax1.bar(range(weeks), ad_spend, alpha=0.3, label='元の広告費')
ax1.plot(geometric_05, marker='o', label='Geometric (α=0.5)')
ax1.plot(geometric_08, marker='s', label='Geometric (α=0.8)')
ax1.set_xlabel('週')
ax1.set_ylabel('Adstock効果')
ax1.set_title('Geometric Adstockの減衰率比較')
ax1.legend()
ax1.grid(True, alpha=0.3)
# WeibullとGeometricの比較
ax2.bar(range(weeks), ad_spend, alpha=0.3, label='元の広告費')
ax2.plot(geometric_08, marker='o', label='Geometric (α=0.8)')
ax2.plot(weibull_effect, marker='^', label='Weibull (k=2, λ=3)')
ax2.set_xlabel('週')
ax2.set_ylabel('Adstock効果')
ax2.set_title('GeometricとWeibull Adstockの比較')
ax2.legend()
ax2.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
Adstockモデリングの注意点
- 減衰率の過大評価:α=0.9のような高い減衰率は、過去の広告効果が永遠に残ることを意味し、非現実的
- チャネルごとの違い:
- テレビCM:減衰率0.3-0.5(短期的効果)
- SNS広告:減衰率0.2-0.4(即時効果が強い)
- SEO:減衰率0.7-0.9(長期的効果が持続)
- ブランド広告:減衰率0.6-0.8(中期的効果)
- データ頻度との整合性:日次データならmax_lag=7-14日、週次データならmax_lag=8-12週が目安
サイトでのAdstock効果の推定
オフィス仲介サイトでの各チャネルの推奨Adstock設定:
- Google検索広告:Geometric、α=0.3(即時効果、1-2週で減衰)
- ディスプレイ広告:Geometric、α=0.4(認知効果が少し持続)
- コンテンツマーケティング:Weibull、k=2、λ=4(遅延効果あり、4-8週でピーク)
- メールマガジン:Geometric、α=0.2(即時反応が主)
PyMC-MarketingでのMMM実装(木曜日)
学習内容:
- PyMC-Marketing v0.16.0の最新機能
- DelayedSaturatedMMMクラスの使い方
- 事前分布の設定(Phase 2の知識を活用)
- MCMCサンプリングと収束診断
# PyMC-MarketingでのMMM実装(完全版)
import pandas as pd
import numpy as np
import pymc as pm
from pymc_marketing.mmm import DelayedSaturatedMMM
import matplotlib.pyplot as plt# Step 1: データの準備# 不動産マーケティングのシミュレーションデータ
np.random.seed(42)
n_weeks = 104 # 2年分の週次データ
data = pd.DataFrame({
'date': pd.date_range('2023-01-01', periods=n_weeks, freq='W'),
'sales': np.random.poisson(50, n_weeks) + np.random.randn(n_weeks) * 5, # 週次問い合わせ数
'google_ads': np.random.uniform(100, 500, n_weeks), # Google広告費(千円)
'sns_ads': np.random.uniform(50, 300, n_weeks), # SNS広告費(千円)
'content_marketing': np.random.uniform(0, 200, n_weeks), # コンテンツ制作費(千円)
})
# 季節性の追加(オフィス移転は3-4月、9-10月が繁忙期)
data['month'] = data['date'].dt.month
data['is_busy_season'] = ((data['month'].isin([3, 4, 9, 10]))).astype(int)
print("データ準備完了:")
print(data.head())
# Step 2: MMMモデルの構築
mmm = DelayedSaturatedMMM(
date_column="date",
channel_columns=["google_ads", "sns_ads", "content_marketing"],
control_columns=["is_busy_season"], # 季節性をコントロール変数に
adstock="geometric", # AdstockタイプはGeometric
saturation="logistic", # 飽和関数はLogistic
yearly_seasonality=4, # 年次季節性(四半期)
)
# Step 3: 事前分布の設定(Phase 2の知識を活用)
# ドメイン知識に基づいた事前分布の設定
with pm.Model() as model:
# Adstockパラメータの事前分布
# Google広告は即時効果が強い → 低い減衰率
alpha_google = pm.Beta("alpha_google", alpha=2, beta=5) # 平均0.3付近
# SNS広告も即時効果
alpha_sns = pm.Beta("alpha_sns", alpha=2, beta=5)
# コンテンツマーケティングは長期効果 → 高い減衰率
alpha_content = pm.Beta("alpha_content", alpha=5, beta=2) # 平均0.7付近
# 飽和パラメータの事前分布
# 半飽和点(効果が50%に達する広告費)
lam_google = pm.Gamma("lam_google", alpha=2, beta=0.01) # 平均200千円
lam_sns = pm.Gamma("lam_sns", alpha=2, beta=0.015) # 平均133千円
lam_content = pm.Gamma("lam_content", alpha=2, beta=0.02) # 平均100千円
print("事前分布の設定完了")
# Step 4: モデルのフィッティング(MCMCサンプリング)
mmm.fit(
X=data,
y=data["sales"],
chains=4, # MCMCチェーン数
draws=2000, # サンプル数
tune=1000, # バーンイン期間
target_accept=0.9, # 受容率(高いほど精度向上)
random_seed=42,
)
print("MCMCサンプリング完了")
# Step 5: 収束診断
# R-hat統計量(1.01未満が望ましい)
print("\n収束診断(R-hat):")
print(mmm.fit_result.summary())
# トレースプロット(目視確認)
az.plot_trace(mmm.fit_result)
plt.tight_layout()
plt.show()
# Step 6: モデルの評価
# 事後予測チェック(Posterior Predictive Check)
mmm.plot_posterior_predictive()
plt.show()
# R²スコア
print(f"\nモデルのR²スコア: {mmm.score(X=data, y=data['sales']):.3f}")
MCMCサンプリングのパラメータ設定
- chains:4が標準(複数チェーンで収束を確認)
- draws:2000-5000(複雑なモデルほど多く)
- tune:1000-2000(バーンイン期間、初期の不安定なサンプルを除外)
- target_accept:0.8-0.95(高いほど精度↑、計算時間↑)
収束判定:R-hat < 1.01 かつ トレースプロットが「毛虫」状態(ランダムウォーク)
MMMでよくあるエラーと対処法
- 収束しない(R-hat > 1.1)
- → tune数を増やす(2000-3000へ)
- → target_acceptを上げる(0.95へ)
- → 事前分布を見直す(広すぎる/狭すぎる)
- Divergenceエラー
- → 事後分布の形状が複雑すぎる
- → データを標準化する(平均0、分散1に)
- → より情報的な事前分布を設定
- 計算時間が長すぎる
- → NumPyroやNutpieなど高速サンプラーを使用
- → データを週次→月次に集約
- → GPUを活用(PyMC v5対応)
チャネル別ROIの推定(金曜日)
学習内容:
- チャネル別の貢献度(contribution)の計算
- ROI(投資対効果)の算出方法
- 限界ROI(mROI)と平均ROI(aROI)の違い
- 結果の可視化とビジネス解釈
ROI計算の数式
平均ROI(aROI)
例:Google広告に1000万円使って1500万円の売上貢献 → aROI = 0.5 = 50%
限界ROI(mROI)
飽和曲線の傾き。「あと1円使ったら何円のリターンがあるか」を示す。
重要:予算最適化ではmROIを使う!(Week 3で詳細)
# チャネル別ROIの推定と可視化
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns# Step 1: チャネル別貢献度の取得# PyMC-Marketingから自動計算
contributions = mmm.compute_channel_contribution_original_scale()
print("チャネル別貢献度:")
print(contributions.head())
# Step 2: ROIの計算
def calculate_roi_metrics(mmm_model, data, value_per_conversion=1000000):
"""
チャネル別のROI指標を計算
Parameters:
-----------
mmm_model : DelayedSaturatedMMM
フィッティング済みのMMMモデル
data : pd.DataFrame
元データ
value_per_conversion : float
1コンバージョンあたりの価値(円)
例:不動産仲介なら成約1件=100万円
Returns:
--------
pd.DataFrame
チャネル別のROI指標
"""
results = []
for channel in mmm_model.channel_columns:
# 総広告費
total_spend = data[channel].sum() * 1000 # 千円→円
# 総貢献数(コンバージョン数)
total_contribution = contributions[channel].sum()
# 総貢献額
total_value = total_contribution * value_per_conversion
# 平均ROI
avg_roi = (total_value / total_spend) - 1 if total_spend > 0 else 0
# CPAおよびCPC(参考値)
cpa = total_spend / total_contribution if total_contribution > 0 else np.inf
results.append({
'チャネル': channel,
'総広告費(円)': total_spend,
'総貢献数': total_contribution,
'総貢献額(円)': total_value,
'平均ROI(%)': avg_roi * 100,
'CPA(円)': cpa,
})
return pd.DataFrame(results)
# ROI計算の実行
roi_metrics = calculate_roi_metrics(
mmm,
data,
value_per_conversion=1000000 # オフィス仲介1件=100万円と仮定
)
print("\n=== チャネル別ROI指標 ===")
print(roi_metrics.to_string(index=False))
# Step 3: 可視化
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
# 1. チャネル別貢献度の時系列
contributions.plot(ax=axes[0, 0], title='チャネル別貢献度(時系列)')
axes[0, 0].set_ylabel('貢献度(コンバージョン数)')
axes[0, 0].legend(title='チャネル')
axes[0, 0].grid(True, alpha=0.3)
# 2. チャネル別ROI比較(棒グラフ)
sns.barplot(
data=roi_metrics,
x='チャネル',
y='平均ROI(%)',
ax=axes[0, 1],
palette='viridis'
)
axes[0, 1].set_title('チャネル別 平均ROI')
axes[0, 1].set_ylabel('ROI(%)')
axes[0, 1].axhline(y=0, color='r', linestyle='--', alpha=0.5)
axes[0, 1].grid(True, alpha=0.3)
# 3. 広告費 vs 貢献額の散布図
for channel in mmm.channel_columns:
axes[1, 0].scatter(
data[channel],
contributions[channel],
label=channel,
alpha=0.6
)
axes[1, 0].set_xlabel('広告費(千円)')
axes[1, 0].set_ylabel('貢献度(コンバージョン数)')
axes[1, 0].set_title('広告費と貢献度の関係')
axes[1, 0].legend()
axes[1, 0].grid(True, alpha=0.3)
# 4. 貢献度の構成比(円グラフ)
total_contributions = roi_metrics['総貢献数']
axes[1, 1].pie(
total_contributions,
labels=roi_metrics['チャネル'],
autopct='%1.1f%%',
startangle=90
)
axes[1, 1].set_title('チャネル別貢献度の構成比')
plt.tight_layout()
plt.show()
# Step 4: 限界ROI(mROI)の計算
def calculate_marginal_roi(mmm_model, data, channel, delta=10):
"""
特定チャネルの限界ROIを計算
Parameters:
-----------
mmm_model : DelayedSaturatedMMM
フィッティング済みのMMMモデル
data : pd.DataFrame
元データ
channel : str
チャネル名
delta : float
広告費の増分(千円)
Returns:
--------
float
限界ROI(次の1単位の投資に対するリターン)
"""
# 現在の予測
current_pred = mmm_model.predict(data)
# 広告費を少し増やした場合の予測
data_modified = data.copy()
data_modified[channel] = data_modified[channel] + delta
modified_pred = mmm_model.predict(data_modified)
# 限界貢献度
marginal_contribution = (modified_pred - current_pred).mean()
# 限界ROI
marginal_roi = (marginal_contribution / delta) - 1
return marginal_roi
# 各チャネルのmROI計算
print("\n=== 限界ROI(mROI) ===")
for channel in mmm.channel_columns:
mroi = calculate_marginal_roi(mmm, data, channel)
print(f"{channel}: {mroi*100:.2f}%")
ROI分析のビジネス解釈
サイトでの架空の分析結果を例に:
| チャネル | 平均ROI | 限界ROI | アクション |
|---|---|---|---|
| Google広告 | +80% | +120% | 🟢 予算増額を推奨(まだ飽和していない) |
| SNS広告 | +50% | +20% | 🟡 現状維持(飽和点に近い) |
| コンテンツ | +120% | +150% | 🟢 予算大幅増額を推奨(最高ROI) |
重要な洞察:
- コンテンツマーケティングは平均ROIも限界ROIも最高 → 投資優先度No.1
- SNS広告は飽和気味(限界ROI低下)→ これ以上の増額は非効率
- Google広告はまだ成長余地あり → 追加投資で更なるリターンを期待
経営層への報告テンプレート
「MMMによる予算配分の推奨事項」
- サマリー:現在の予算配分では、コンテンツマーケティングへの投資が過小。Google広告も増額余地あり。
- 推奨アクション:
- コンテンツ制作費を月50万円→100万円に増額(+50万円)
- Google広告を月300万円→400万円に増額(+100万円)
- SNS広告は現状維持(150万円)
- 期待効果:月間問い合わせ数が50件→65件に増加(+30%)、予算効率は+15%改善
- リスク:コンテンツ制作のリソース不足。外注先の確保が必要。
チャネル間の相乗効果とLLM統合(土日)
学習内容:
- 複数チャネルの相互作用のモデリング
- シナジー効果の定量化
- LLMでMMM結果を自動解釈(Phase 5との統合)
- 経営層向けレポートの自動生成
チャネル間の相乗効果とは
単独では効果が小さくても、複数チャネルを組み合わせることで効果が増幅される現象。
例:
- Google広告 + コンテンツSEO:検索結果でブランド露出が増え、クリック率が向上
- SNS広告 + リターゲティング:SNSで認知した後、リターゲティングでコンバージョン
- テレビCM + デジタル広告:テレビで認知、デジタルで検索・購入
数式:
シナジー > 0 なら相乗効果あり
# チャネル間の相乗効果分析
import pandas as pd
import numpy as np
from itertools import combinations
import seaborn as snsdef analyze_channel_synergy(mmm_model, data, channels):"""
チャネル間の相乗効果を分析
Parameters:
-----------
mmm_model : DelayedSaturatedMMM
フィッティング済みのMMMモデル
data : pd.DataFrame
元データ
channels : list
分析対象のチャネルリスト
Returns:
--------
pd.DataFrame
チャネル間の相乗効果マトリックス
"""
results = []
# すべてのチャネルペアについて分析
for ch1, ch2 in combinations(channels, 2):
# ベースライン(両方ゼロ)
data_baseline = data.copy()
data_baseline[ch1] = 0
data_baseline[ch2] = 0
pred_baseline = mmm_model.predict(data_baseline).mean()
# チャネル1のみ
data_ch1 = data.copy()
data_ch1[ch2] = 0
pred_ch1 = mmm_model.predict(data_ch1).mean()
effect_ch1 = pred_ch1 - pred_baseline
# チャネル2のみ
data_ch2 = data.copy()
data_ch2[ch1] = 0
pred_ch2 = mmm_model.predict(data_ch2).mean()
effect_ch2 = pred_ch2 - pred_baseline
# 両方あり
pred_both = mmm_model.predict(data).mean()
effect_both = pred_both - pred_baseline
# 相乗効果の計算
expected_additive = effect_ch1 + effect_ch2
synergy = effect_both - expected_additive
synergy_pct = (synergy / expected_additive) * 100 if expected_additive > 0 else 0
results.append({
'チャネル1': ch1,
'チャネル2': ch2,
'効果1': effect_ch1,
'効果2': effect_ch2,
'期待効果(加算)': expected_additive,
'実際の効果': effect_both,
'相乗効果': synergy,
'相乗効果率(%)': synergy_pct,
})
return pd.DataFrame(results)
# 相乗効果分析の実行
synergy_results = analyze_channel_synergy(
mmm,
data,
mmm.channel_columns
)
print("\n=== チャネル間の相乗効果分析 ===")
print(synergy_results.to_string(index=False))
# ヒートマップで可視化
# 相乗効果率のマトリックスを作成
channels = mmm.channel_columns
synergy_matrix = np.zeros((len(channels), len(channels)))
for i, ch1 in enumerate(channels):
for j, ch2 in enumerate(channels):
if i < j: # 該当する相乗効果を取得
row = synergy_results[
((synergy_results['チャネル1'] == ch1) & (synergy_results['チャネル2'] == ch2)) |
((synergy_results['チャネル1'] == ch2) & (synergy_results['チャネル2'] == ch1))
]
if not row.empty:
synergy_matrix[i, j] = row['相乗効果率(%)'].values[0]
synergy_matrix[j, i] = synergy_matrix[i, j] # 対称
plt.figure(figsize=(10, 8))
sns.heatmap(
synergy_matrix,
annot=True,
fmt='.1f',
cmap='RdYlGn',
center=0,
xticklabels=channels,
yticklabels=channels,
cbar_kws={'label': '相乗効果率(%)'}
)
plt.title('チャネル間の相乗効果マトリックス')
plt.tight_layout()
plt.show()
# LLMでMMM結果を自動解釈(Phase 5との統合)
from openai import OpenAIclient = OpenAI(api_key="your-api-key-here")
def generate_mmm_report(roi_metrics, synergy_results, business_context):
"""
LLMを使ってMMM分析結果を経営層向けレポートに変換
Parameters:
-----------
roi_metrics : pd.DataFrame
チャネル別ROI指標
synergy_results : pd.DataFrame
チャネル間の相乗効果分析結果
business_context : str
ビジネスコンテキスト(業種、目標等)
Returns:
--------
str
生成されたレポート
"""
# データをテキスト形式に変換
roi_text = roi_metrics.to_string(index=False)
synergy_text = synergy_results.to_string(index=False)
prompt = f"""
あなたは経験豊富なマーケティングサイエンティストです。
以下のMarketing Mix Modeling(MMM)分析結果を、経営層向けの戦略的レポートに変換してください。
【ビジネスコンテキスト】
{business_context}
【チャネル別ROI分析】
{roi_text}
【チャネル間の相乗効果分析】
{synergy_text}
【レポート要件】
1. エグゼクティブサマリー(3行以内)
2. 主要な発見事項(箇条書き3-5点)
3. 具体的な推奨アクション(予算配分の提案を含む)
4. 期待される効果(定量的に)
5. リスクと対策
専門用語は最小限にし、ビジネスインパクトを明確に伝えてください。
"""
response = client.chat.completions.create(
model="gpt-4",
messages=[
{"role": "system", "content": "経営層向けマーケティングレポート作成の専門家"},
{"role": "user", "content"
": prompt}
],
temperature=0.7,
)
return response.choices[0].message.content
# レポート生成の実行
business_context = """
業種:不動産仲介(オフィス物件専門)
目標:月間問い合わせ数を50件から70件に増加(+40%)
予算制約:月額総広告費800万円まで
期間:次の四半期(3ヶ月)
"""
report = generate_mmm_report(roi_metrics, synergy_results, business_context)
print("\n" + "="*80)
print(" マーケティングミックスモデリング 戦略レポート")
print("="*80 + "\n")
print(report)
print("\n" + "="*80)
# レポートをMarkdownファイルとして保存
with open('mmm_strategic_report.md', 'w', encoding='utf-8') as f:
f.write(f"# マーケティングミックスモデリング 戦略レポート\n\n")
f.write(f"**生成日時**: {pd.Timestamp.now().strftime('%Y年%m月%d日 %H:%M')}\n\n")
f.write(report)
print("\nレポートを 'mmm_strategic_report.md' として保存しました")
Phase 5との統合:LLMで何ができるか
- 自動レポート生成:MMM結果を経営層が理解できる言葉に翻訳
- What-Ifシミュレーション:「Google広告を50万円増やしたら?」などの質問に即答
- 異常検知の説明:「今週のコンバージョンが急増した理由は?」を因果的に説明
- 競合分析の統合:Web検索で競合の広告戦略を収集し、自社のMMMと比較
- 多言語レポート:英語、中国語などグローバル展開時のレポート作成
Week 1完了後のチェックリスト
目標:Phase 1-4で学んだ因果推論手法を、DoWhy・CausalMLという統一ライブラリで体系的に実装できるようになる。
Week 2の位置づけ
これまでPhase 1-4で個別に学んできた因果推論手法(RCT、DID、PSM、IV、CATE等)を、DoWhyとCausalMLという2つの強力なライブラリで統一的に扱えるようにします。
- DoWhy:4ステップ(model → identify → estimate → refute)で因果推論を体系化
- CausalML:Uplift ModelingとMeta-Learnersで異質な処置効果(CATE)を推定
DoWhyの4ステップ:Model & Identify(月曜日)
学習内容:
- DoWhyの設計思想:因果推論の民主化
- Step 1: Model - 因果グラフ(DAG)の定義
- Step 2: Identify - 因果効果の識別可能性の確認
- 識別戦略の自動選択(Backdoor、Frontdoor、IV)
DoWhyの4ステップワークフロー
- Model:因果仮定をグラフで表現(DAG作成)
- Identify:因果効果が識別可能か判定(do-calculus適用)
- Estimate:統計手法で因果効果を推定(PSM、IV、DML等)
- Refute:推定結果の頑健性をチェック(感度分析)
利点:仮定を明示化し、自動で頑健性チェックを行うことで、因果推論の信頼性を向上
# DoWhyの基本的な使い方:4ステップ完全実装
import pandas as pd
import numpy as np
from dowhy import CausalModel
import dowhy.datasets# サンプルデータの生成(不動産マーケティング)# 処置:Google広告の出稿有無(0/1)
# 結果:問い合わせ数
# 交絡因子:企業規模、業種、繁忙期
np.random.seed(42)
n = 1000
# 交絡因子
company_size = np.random.choice(['small', 'medium', 'large'], n, p=[0.5, 0.3, 0.2])
is_busy = np.random.binomial(1, 0.3, n) # 繁忙期フラグ
# 処置(Google広告出稿)は企業規模と繁忙期に影響される
treatment_prob = 0.3 + 0.2 * (company_size == 'large') + 0.1 * is_busy
treatment = np.random.binomial(1, treatment_prob)
# 結果(問い合わせ数)
# 真の因果効果 = +5件
inquiries = (
10 + # ベースライン
5 * treatment + # 処置効果(真の値)
8 * (company_size == 'large') + # 企業規模の影響
4 * (company_size == 'medium') +
6 * is_busy + # 繁忙期の影響
np.random.randn(n) * 2 # ノイズ
)
data = pd.DataFrame({
'treatment': treatment,
'inquiries': inquiries,
'company_size_medium': (company_size == 'medium').astype(int),
'company_size_large': (company_size == 'large').astype(int),
'is_busy': is_busy,
})
print("サンプルデータ:")
print(data.head())
print(f"\n真の因果効果: +5件")
# ==========================================
# Step 1: Model - 因果グラフ(DAG)の定義
# ==========================================
# グラフをGML形式で定義
causal_graph = """
digraph {
company_size_medium;
company_size_large;
is_busy;
treatment;
inquiries;
company_size_medium -> treatment;
company_size_large -> treatment;
is_busy -> treatment;
company_size_medium -> inquiries;
company_size_large -> inquiries;
is_busy -> inquiries;
treatment -> inquiries;
}
"""
# CausalModelの作成
model = CausalModel(
data=data,
treatment='treatment',
outcome='inquiries',
graph=causal_graph,
common_causes=['company_size_medium', 'company_size_large', 'is_busy']
)
print("\n【Step 1: Model】")
print("因果グラフを定義しました")
model.view_model() # グラフを可視化(要graphviz)
# ==========================================
# Step 2: Identify - 因果効果の識別
# ==========================================
identified_estimand = model.identify_effect(proceed_when_unidentifiable=True)
print("\n【Step 2: Identify】")
print(identified_estimand)
# 識別戦略の確認
print("\n識別された推定量(Estimand):")
print(f"バックドア基準が適用可能: {identified_estimand.estimands['backdoor'] is not None}")
DoWhyのIdentify(識別)ステップの重要性
Identifyステップでは、「因果効果が推定可能か」を数学的に判定します。
- Backdoor Criterion(バックドア基準):交絡因子をすべて調整すれば因果効果が識別可能
- Frontdoor Criterion:媒介変数を使って間接的に因果効果を識別
- Instrumental Variable(IV):操作変数を使って因果効果を識別
Phase 1との統合:Phase 1で学んだ識別戦略を、DoWhyが自動で選択してくれます。
因果グラフ作成のベストプラクティス
- ドメイン知識を活用:「企業規模が大きいほど広告を出稿しやすい」などの業界知識を反映
- シンプルに保つ:すべての変数を含める必要はない。重要な交絡因子に絞る
- 方向性を明確に:因果の向きを間違えると、誤った推定になる
- Phase 5の因果探索と併用:LiNGAMで構造を発見してからDoWhyで検証
DoWhyの4ステップ:Estimate(火曜日)
学習内容:
- Step 3: Estimate - 因果効果の推定
- 複数の推定手法の比較(PSM、IPW、Regression、DML)
- Phase 1-4の手法をDoWhyで統一的に実装
- ATE(平均処置効果)とCATEの推定
# DoWhy Step 3: Estimate - 複数手法での推定# ==========================================# 手法1: Propensity Score Matching(PSM)
# ==========================================
print("\n【Step 3: Estimate - PSM】")
estimate_psm = model.estimate_effect(
identified_estimand,
method_name="backdoor.propensity_score_matching",
method_params={
'propensity_score_model': 'logit', # ロジスティック回帰
}
)
print(f"PSMによる推定結果: {estimate_psm.value:.2f} (真の値: 5.00)")
print(estimate_psm)
# ==========================================
# 手法2: Inverse Propensity Weighting(IPW)
# ==========================================
print("\n【Step 3: Estimate - IPW】")
estimate_ipw = model.estimate_effect(
identified_estimand,
method_name="backdoor.propensity_score_weighting"
)
print(f"IPWによる推定結果: {estimate_ipw.value:.2f} (真の値: 5.00)")
# ==========================================
# 手法3: Linear Regression(回帰分析)
# ==========================================
print("\n【Step 3: Estimate - Linear Regression】")
estimate_reg = model.estimate_effect(
identified_estimand,
method_name="backdoor.linear_regression"
)
print(f"回帰分析による推定結果: {estimate_reg.value:.2f} (真の値: 5.00)")
# ==========================================
# 手法4: Double Machine Learning(DML)
# ==========================================
# EconMLとの統合
from econml.dml import CausalForestDML
print("\n【Step 3: Estimate - DML (EconML統合)】")
estimate_dml = model.estimate_effect(
identified_estimand,
method_name="backdoor.econml.dml.CausalForestDML",
method_params={
'init_params': {
'n_estimators': 100,
'min_samples_leaf': 10,
}
}
)
print(f"DMLによる推定結果: {estimate_dml.value:.2f} (真の値: 5.00)")
# ==========================================
# すべての手法の比較
# ==========================================
results_comparison = pd.DataFrame({
'手法': ['PSM', 'IPW', '回帰分析', 'DML'],
'推定値': [
estimate_psm.value,
estimate_ipw.value,
estimate_reg.value,
estimate_dml.value
],
'真の値との差': [
abs(estimate_psm.value - 5.0),
abs(estimate_ipw.value - 5.0),
abs(estimate_reg.value - 5.0),
abs(estimate_dml.value - 5.0)
]
})
print("\n=== 推定手法の比較 ===")
print(results_comparison.to_string(index=False))
# 可視化
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(10, 6))
ax.barh(results_comparison['手法'], results_comparison['推定値'], color='skyblue')
ax.axvline(x=5.0, color='red', linestyle='--', label='真の因果効果')
ax.set_xlabel('推定された因果効果')
ax.set_title('DoWhyによる複数手法の比較')
ax.legend()
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
DoWhyで使える推定手法の一覧
| 手法 | DoWhyでの指定 | Phase 1-4との対応 |
|---|---|---|
| PSM | backdoor.propensity_score_matching | Phase 1 Day 5 |
| IPW | backdoor.propensity_score_weighting | Phase 1 Day 5 |
| 回帰分析 | backdoor.linear_regression | Phase 1 Day 3 |
| IV(操作変数) | iv.instrumental_variable | Phase 1 Day 6 |
| RDD | backdoor.regression_discontinuity | Phase 1 Day 7 |
| DML | backdoor.econml.dml.* | Phase 4 Day 3-4 |
| Causal Forest | backdoor.econml.dr.ForestDRLearner | Phase 4 Day 5 |
手法選択のガイドライン
- データが豊富(n > 5000):DML、Causal Forestなど機械学習ベースの手法
- データが少ない(n < 1000):PSM、回帰分析など古典的手法
- 交絡が強い:IPW(逆確率重み付け)で調整
- 異質性が重要:Meta-Learners、Causal Forestで
CATE推定 - 未観測交絡の懸念:感度分析(次のRefuteステップ)で頑健性を確認
- 推奨:複数手法で推定し、結果が一致するか確認(Robustness Check)
DoWhyの4ステップ:Refute(水曜日)
学習内容:
- Step 4: Refute - 推定結果の頑健性チェック
- 4種類のRefutation手法(Placebo、Random Common Cause、Data Subset、Bootstrap)
- 感度分析の解釈とビジネス判断への活用
- Phase 1-4で学んだ頑健性チェックの統合
Refutation(反証)の4つの手法
- Placebo Treatment(プラセボ処置):処置をランダムに入れ替えても効果が消えるか確認
- Random Common Cause(ランダム交絡):ランダム変数を交絡因子として追加しても結果が変わらないか
- Data Subset(データ分割):データの一部を除外しても推定値が安定しているか
- Bootstrap(ブートストラップ):リサンプリングで信頼区間を計算
目的:推定結果が偶然や仮定の違反によるものではないことを検証
# DoWhy Step 4: Refute - 頑健性チェック# ==========================================# Refutation 1: Placebo Treatment
# ==========================================
print("\n【Step 4: Refute - Placebo Treatment】")
print("処置変数をランダムに入れ替えた場合、因果効果は消えるはずです")
refute_placebo = model.refute_estimate(
identified_estimand,
estimate_psm,
method_name="placebo_treatment_refuter",
placebo_type="permute" # 処置をランダムに並び替え
)
print(refute_placebo)
# ==========================================
# Refutation 2: Random Common Cause
# ==========================================
print("\n【Step 4: Refute - Random Common Cause】")
print("ランダム変数を交絡因子として追加しても、推定値は変わらないはずです")
refute_random = model.refute_estimate(
identified_estimand,
estimate_psm,
method_name="random_common_cause"
)
print(refute_random)
# ==========================================
# Refutation 3: Data Subset Validation
# ==========================================
print("\n【Step 4: Refute - Data Subset】")
print("データの一部を除外しても、推定値は安定しているはずです")
refute_subset = model.refute_estimate(
identified_estimand,
estimate_psm,
method_name="data_subset_refuter",
subset_fraction=0.8 # 80%のデータで再推定
)
print(refute_subset)
# ==========================================
# Refutation 4: Bootstrap (信頼区間)
# ==========================================
print("\n【Step 4: Refute - Bootstrap】")
print("ブートストラップで信頼区間を計算します")
refute_bootstrap = model.refute_estimate(
identified_estimand,
estimate_psm,
method_name="bootstrap_refuter",
num_simulations=100 # ブートストラップ回数
)
print(refute_bootstrap)
# ==========================================
# すべてのRefutation結果をまとめる
# ==========================================
refutation_summary = pd.DataFrame({
'テスト': [
'元の推定',
'Placebo Treatment',
'Random Common Cause',
'Data Subset',
'Bootstrap'
],
'推定値': [
estimate_psm.value,
refute_placebo.new_effect,
refute_random.new_effect,
refute_subset.new_effect,
refute_bootstrap.new_effect
],
'判定': [
'—',
'Pass' if abs(refute_placebo.new_effect) < 1.0 else 'Fail',
'Pass' if abs(refute_random.new_effect - estimate_psm.value) < 1.0 else 'Fail',
'Pass' if abs(refute_subset.new_effect - estimate_psm.value) < 1.0 else 'Fail',
'Pass'
]
})
print("\n=== Refutation(頑健性チェック)サマリー ===")
print(refutation_summary.to_string(index=False))
# 可視化
fig, ax = plt.subplots(figsize=(12, 6))
colors = ['green' if x == 'Pass' else 'gray' if x == '—' else 'red'
for x in refutation_summary['判定']]
ax.barh(refutation_summary['テスト'], refutation_summary['推定値'], color=colors, alpha=0.7)
ax.axvline(x=5.0, color='red', linestyle='--', linewidth=2, label='真の因果効果')
ax.axvline(x=0, color='black', linestyle='-', linewidth=1, alpha=0.3)
ax.set_xlabel('推定された因果効果')
ax.set_title('DoWhyの頑健性チェック結果')
ax.legend()
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
Refutation結果の解釈ガイド
| テスト | Pass条件 | Failの意味 |
|---|---|---|
| Placebo Treatment | 効果が0に近い | 偶然の相関を拾っている可能性 |
| Random Common Cause | 推定値が変わらない | 未観測交絡の影響が大きい可能性 |
| Data Subset | 推定値が安定 | 特定のサブグループに依存している |
| Bootstrap | 信頼区間が狭い | 推定の不確実性が高い |
推奨:すべてのテストがPassなら推定結果を信頼できる。1つでもFailなら慎重に解釈
DoWhy完全ワークフローのテンプレート
実務で使える完全なコードテンプレート:
- Model:ドメイン知識に基づいてDAGを作成
- Identify:識別戦略を確認(Backdoor/Frontdoor/IV)
- Estimate:3つ以上の手法で推定し、結果を比較
- Refute:4つすべての頑健性チェックを実行
- Report:結果をビジネス言語に翻訳(LLM活用)
このワークフローを習慣化すれば、因果推論の信頼性が格段に向上します。
CausalMLの基礎:Meta-Learners(木曜日)
学習内容:
- CausalMLとは:Uplift Modelingに特化したライブラリ
- Meta-Learnersの5種類(S/T/X/R/DR-Learner)
- 各手法の特徴と使い分け
- Phase 4との統合(CATE推定)
Meta-Learnersとは
Meta-Learnerは、既存の機械学習モデル(XGBoost、Random Forest等)を「ベースモデル」として活用し、CATE(条件付き平均処置効果)を推定するフレームワークです。
5つのMeta-Learners:
- S-Learner:Single Model。処置変数を特徴量として1つのモデルで推定
- T-Learner:Two Models。処置群と対照群で別々のモデルを構築
- X-Learner:Cross-fitting。T-Learnerの改良版。不均衡データに強い
- R-Learner:Residual-based。残差を使って効果を推定
- DR-Learner:Doubly Robust。IPWとRegressionを組み合わせ
S-Learner
最もシンプル
メリット:実装が簡単、データ効率が良い
デメリット:処置効果が小さいと正則化で消える可能性
推奨:処置効果が大きい場合
T-Learner
最も人気
メリット:柔軟、解釈しやすい
デメリット:不均衡データに弱い
推奨:処置群と対照群のサイズが同程度
X-Learner
推奨
メリット:不均衡データに強い、精度が高い
デメリット:実装が複雑
推奨:処置群が小さい場合(A/Bテスト)
R-Learner
高度
メリット:理論的に優れている
デメリット:計算コストが高い
推奨:データが豊富で精度重視
DR-Learner
最も頑健
メリット:2重の頑健性、未観測交絡に強い
デメリット:実装が最も複雑
推奨:観察データ、交絡が強い
# CausalMLでMeta-Learnersを実装
from causalml.inference.meta import (
BaseSLearner,
BaseTLearner,
BaseXLearner,
BaseRLearner,
BaseDRLearner
)
from xgboost import XGBRegressor
import numpy as np
import pandas as pd# データ準備(前述のデータを使用)X = data[['company_size_medium', 'company_size_large', 'is_busy']].values
treatment = data['treatment'].values
y = data['inquiries'].values
# ベースモデル(すべてのMeta-LearnersでXGBoostを使用)
base_model = XGBRegressor(
n_estimators=100,
max_depth=3,
learning_rate=0.1,
random_state=42
)
# ==========================================
# S-Learner
# ==========================================
print("\n【S-Learner】")
s_learner = BaseSLearner(learner=base_model)
# フィッティング
s_learner.fit(X=X, treatment=treatment, y=y)
# CATEの推定
cate_s = s_learner.predict(X=X)
# ATEの推定
ate_s = s_learner.estimate_ate(X=X, treatment=treatment, y=y)
print(f"S-Learner ATE: {ate_s[0]:.2f} (真の値: 5.00)")
# ==========================================
# T-Learner
# ==========================================
print("\n【T-Learner】")
t_learner = BaseTLearner(learner=base_model)
t_learner.fit(X=X, treatment=treatment, y=y)
cate_t = t_learner.predict(X=X)
ate_t = t_learner.estimate_ate(X=X, treatment=treatment, y=y)
print(f"T-Learner ATE: {ate_t[0]:.2f} (真の値: 5.00)")
# ==========================================
# X-Learner
# ==========================================
print("\n【X-Learner】")
x_learner = BaseXLearner(learner=base_model)
x_learner.fit(X=X, treatment=treatment, y=y)
cate_x = x_learner.predict(X=X)
ate_x = x_learner.estimate_ate(X=X, treatment=treatment, y=y)
print(f"X-Learner ATE: {ate_x[0]:.2f} (真の値: 5.00)")
# ==========================================
# R-Learner
# ==========================================
print("\n【R-Learner】")
r_learner = BaseRLearner(learner=base_model)
r_learner.fit(X=X, treatment=treatment, y=y)
cate_r = r_learner.predict(X=X)
ate_r = r_learner.estimate_ate(X=X, treatment=treatment, y=y)
print(f"R-Learner ATE: {ate_r[0]:.2f} (真の値: 5.00)")
# ==========================================
# DR-Learner(最も頑健)
# ==========================================
print("\n【DR-Learner】")
dr_learner = BaseDRLearner(learner=base_model)
dr_learner.fit(X=X, treatment=treatment, y=y)
cate_dr = dr_learner.predict(X=X)
ate_dr = dr_learner.estimate_ate(X=X, treatment=treatment, y=y)
print(f"DR-Learner ATE: {ate_dr[0]:.2f} (真の値: 5.00)")
# ==========================================
# すべてのMeta-Learnersの比較
# ==========================================
meta_learner_results = pd.DataFrame({
'手法': ['S-Learner', 'T-Learner', 'X-Learner', 'R-Learner', 'DR-Learner'],
'ATE': [ate_s[0], ate_t[0], ate_x[0], ate_r[0], ate_dr[0]],
'誤差': [
abs(ate_s[0] - 5.0),
abs(ate_t[0] - 5.0),
abs(ate_x[0] - 5.0),
abs(ate_r[0] - 5.0),
abs(ate_dr[0] - 5.0)
]
})
print("\n=== Meta-Learnersの比較 ===")
print(meta_learner_results.to_string(index=False))
# CATEの分布を可視化
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
for i, (name, cate) in enumerate([
('S-Learner', cate_s),
('T-Learner', cate_t),
('X-Learner', cate_x),
('R-Learner', cate_r),
('DR-Learner', cate_dr)
]):
ax = axes[i // 3, i % 3]
ax.hist(cate, bins=30, alpha=0.7, edgecolor='black')
ax.axvline(x=5.0, color='red', linestyle='--', label='真の効果')
ax.axvline(x=cate.mean(), color='blue', linestyle='-', label=f'平均={cate.mean():.2f}')
ax.set_title(name)
ax.set_xlabel('CATE')
ax.set_ylabel('頻度')
ax.legend()
ax.grid(True, alpha=0.3)
# 最後のサブプロットにATE比較
ax = axes[1, 2]
ax.barh(meta_learner_results['手法'], meta_learner_results['ATE'], color='skyblue')
ax.axvline(x=5.0, color='red', linestyle='--', label='真の値')
ax.set_xlabel('ATE')
ax.set_title('Meta-LearnersのATE比較')
ax.legend()
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
Meta-Learners使用時の注意点
- ベースモデルの選択が重要:XGBoost、Random Forest、LightGBMなど、非線形モデルを推奨
- ハイパーパラメータ調整:Cross-validationで最適化
- S-Learnerの落とし穴:処置効果が小さいと正則化で消える可能性あり
- データサイズ:最低でも数百サンプル必要。理想は1000+
- 不均衡データ:処置群が小さい場合はX-LearnerかDR-Learnerを使う
CausalMLのUplift Modeling(金曜日)
学習内容:
- Uplift Modelingとは:誰にマーケティングすべきか
- Uplift Tree、Uplift Random Forestの実装
- AUUC(Area Under Uplift Curve)による評価
- 実務での活用:キャンペーンターゲティング最適化
Uplift Modelingの4つの顧客セグメント
Uplift Modelingでは、顧客を以下の4つに分類します:
- Persuadables(説得可能層):処置によって行動が変わる → ★ターゲット★
- Sure Things(確実層):処置なしでもコンバージョンする → 広告費の無駄
- Lost Causes(見込みなし層):処置してもしなくてもコンバージョンしない
- Do Not Disturbs(逆効果層):処置するとむしろコンバージョンが下がる → 絶対に避ける
目的:Persuadablesだけにマーケティングリソースを集中させ、ROIを最大化
# CausalMLでUplift Modelingを実装
from causalml.inference.tree import UpliftTreeClassifier, UpliftRandomForestClassifier
from causalml.metrics import plot_gain, auuc_score
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt# データ準備(バイナリ分類用にコンバージョンフラグを作成)# 問い合わせ数15件以上をコンバージョンと定義
data['conversion'] = (data['inquiries'] >= 15).astype(int)
X = data[['company_size_medium', 'company_size_large', 'is_busy']].values
treatment = data['treatment'].values
y = data['conversion'].values
# トレーニングとテストに分割
from sklearn.model_selection import train_test_split
X_train, X_test, t_train, t_test, y_train, y_test = train_test_split(
X, treatment, y, test_size=0.3, random_state=42
)
# ==========================================
# Uplift Tree
# ==========================================
print("\n【Uplift Tree】")
uplift_tree = UpliftTreeClassifier(
max_depth=5,
min_samples_leaf=100,
evaluationFunction='KL' # KL divergence
)
uplift_tree.fit(X_train, treatment=t_train, y=y_train)
# Uplift予測
uplift_tree_pred = uplift_tree.predict(X_test)
print(f"Uplift Treeの予測完了。予測数: {len(uplift_tree_pred)}")
# ==========================================
# Uplift Random Forest
# ==========================================
print("\n【Uplift Random Forest】")
uplift_rf = UpliftRandomForestClassifier(
n_estimators=100,
max_depth=5,
min_samples_leaf=100,
evaluationFunction='KL',
random_state=42
)
uplift_rf.fit(X_train, treatment=t_train, y=y_train)
# Uplift予測
uplift_rf_pred = uplift_rf.predict(X_test)
print(f"Uplift Random Forestの予測完了。予測数: {len(uplift_rf_pred)}")
# ==========================================
# Meta-Learner(比較用)
# ==========================================
from causalml.inference.meta import BaseXLearner
from xgboost import XGBClassifier
x_learner = BaseXLearner(learner=XGBClassifier(random_state=42))
x_learner.fit(X=X_train, treatment=t_train, y=y_train)
uplift_xlearner_pred = x_learner.predict(X=X_test).flatten()
# ==========================================
# Uplift Curve(Gain Chart)で評価
# ==========================================
fig, ax = plt.subplots(figsize=(12, 8))
# Uplift Treeの曲線
plot_gain(
y_test,
uplift_tree_pred,
t_test,
plot_type='gain',
ax=ax
)
ax.set_title('Uplift Gain Curve(累積効果曲線)', fontsize=14)
ax.set_xlabel('ターゲット人口の割合(%)', fontsize=12)
ax.set_ylabel('累積Uplift', fontsize=12)
ax.legend(['Uplift Model', 'Random Targeting'], fontsize=10)
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# ==========================================
# AUUC(Area Under Uplift Curve)で定量評価
# ==========================================
auuc_tree = auuc_score(y_test, uplift_tree_pred, t_test)
auuc_rf = auuc_score(y_test, uplift_rf_pred, t_test)
auuc_xlearner = auuc_score(y_test, uplift_xlearner_pred, t_test)
print("\n=== AUUC(Area Under Uplift Curve)スコア ===")
print(f"Uplift Tree: {auuc_tree:.4f}")
print(f"Uplift Random Forest: {auuc_rf:.4f}")
print(f"X-Learner: {auuc_xlearner:.4f}")
# ==========================================
# 顧客セグメンテーション
# ==========================================
def segment_customers(uplift_scores, threshold_high=0.1, threshold_low=-0.05):
"""
Upliftスコアに基づいて顧客を4つのセグメントに分類
Parameters:
-----------
uplift_scores : array-like
各顧客のUpliftスコア
threshold_high : float
Persuadablesの閾値
threshold_low : float
Do Not Disturbsの閾値
Returns:
--------
pd.DataFrame
セグメント分類結果
"""
segments = []
for score in uplift_scores:
if score > threshold_high:
segments.append('Persuadables')
elif score < threshold_low: segments.append('Do Not Disturbs')
elif score >= 0:
segments.append('Sure Things')
else:
segments.append('Lost Causes')
return segments
# セグメンテーション実行
test_data = pd.DataFrame(X_test, columns=['company_size_medium', 'company_size_large', 'is_busy'])
test_data['uplift_score'] = uplift_rf_pred
test_data['segment'] = segment_customers(uplift_rf_pred)
# セグメント別の分布
segment_counts = test_data['segment'].value_counts()
print("\n=== 顧客セグメント分布 ===")
print(segment_counts)
print(f"\nPersuadables(ターゲット層): {segment_counts.get('Persuadables', 0)}人 ({segment_counts.get('Persuadables', 0)/len(test_data)*100:.1f}%)")
# 可視化
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 6))
# Upliftスコアの分布
ax1.hist(test_data['uplift_score'], bins=30, edgecolor='black', alpha=0.7)
ax1.axvline(x=0.1, color='green', linestyle='--', label='Persuadables閾値')
ax1.axvline(x=-0.05, color='red', linestyle='--', label='Do Not Disturbs閾値')
ax1.set_xlabel('Upliftスコア')
ax1.set_ylabel('頻度')
ax1.set_title('Upliftスコアの分布')
ax1.legend()
ax1.grid(True, alpha=0.3)
# セグメント別の円グラフ
colors = {
'Persuadables': '#4caf50',
'Sure Things': '#2196f3',
'Lost Causes': '#9e9e9e',
'Do Not Disturbs': '#f44336'
}
segment_colors = [colors[seg] for seg in segment_counts.index]
ax2.pie(
segment_counts.values,
labels=segment_counts.index,
autopct='%1.1f%%',
colors=segment_colors,
startangle=90
)
ax2.set_title('顧客セグメントの構成比')
plt.tight_layout()
plt.show()
Uplift Modeling実務活用例:オフィス不動産仲介サイト
シナリオ:月額広告予算300万円で、1000社の企業にGoogle広告を配信
従来の方法(ランダム配信):
- 全員に配信 → コンバージョン50件 → CPA = 60,000円
Uplift Modelingによる最適化:
- Persuadables(250社、25%)にのみ配信
- 予算を1/4に削減(75万円)
- コンバージョン40件(Persuadablesに集中) → CPA = 18,750円
効果:
- ✅ CPA 68%削減(60,000円 → 18,750円)
- ✅ 予算225万円を他施策に回せる
- ✅ Do Not Disturbsへの配信を避け、ブランド毀損を防止
Uplift Modelingのベストプラクティス
- A/Bテストデータで訓練:観察データだけでは精度が低い
- 定期的な再訓練:顧客行動は変化するため、3-6ヶ月ごとに更新
- セグメント閾値の調整:ビジネス目標に応じて調整(CPA重視 vs リーチ重視)
- Do Not Disturbsの徹底排除:逆効果を避けることが最優先
- 可視化とレポーティング:マーケティングチームが理解できる形で共有
DoWhy vs CausalMLの使い分けと統合(土日)
学習内容:
- DoWhyとCausalMLの違いと使い分け
- 2つのライブラリを統合して使う方法
- 因果推論理論の体系的整理(Pearl vs Rubin)
- Phase 1-6の因果推論手法の総まとめ
DoWhy vs CausalML:どちらを使うべきか
| 観点 | DoWhy | CausalML |
|---|---|---|
| 設計思想 | 因果推論の4ステップワークフロー | Uplift Modelingに特化 |
| 主な用途 | ATE推定、因果効果の検証 | CATE推定、顧客セグメンテーション |
| 頑健性チェック | ✅ 充実(Refuteステップ) | ❌ 限定的 |
| グラフモデル | ✅ DAGベース | ❌ 非対応 |
| 機械学習統合 | ✅ EconML経由 | ✅ ネイティブ対応 |
| Uplift Modeling | ❌ 非対応 | ✅ 専門特化 |
| 学習コスト | 中(因果推論の理解必要) | 低(機械学習経験者向け) |
推奨される使い分け
DoWhyを使うべき場合:
- ✅ 因果効果の存在自体を検証したい(研究・分析)
- ✅ 複雑な因果構造を明示的にモデル化したい
- ✅ 頑健性チェックが重要(経営判断に使う)
- ✅ 学術的な厳密さが求められる
CausalMLを使うべき場合:
- ✅ 誰にマーケティングすべきかを知りたい(Uplift Modeling)
- ✅ 個人レベルの効果推定(CATE)が必要
- ✅ 実装スピード重視(プロトタイピング)
- ✅ 機械学習モデルを活用したい
両方を統合して使う:
- ✅ DoWhyで因果効果を検証 → CausalMLでターゲティング最適化
- ✅ DoWhyのRefuteでCausalMLの結果を検証
# DoWhyとCausalMLを統合して使う実例
from dowhy import CausalModel
from causalml.inference.meta import BaseXLearner
from xgboost import XGBClassifier
import pandas as pd
import numpy as np# ==========================================# ステップ1: DoWhyでATE推定と頑健性チェック
# ==========================================
print("\n【ステップ1: DoWhyでATE推定】")
# データ準備(前述のデータを使用)
causal_graph = """
digraph {
company_size_medium -> treatment;
company_size_large -> treatment;
is_busy -> treatment;
company_size_medium -> conversion;
company_size_large -> conversion;
is_busy -> conversion;
treatment -> conversion;
}
"""
model = CausalModel(
data=data,
treatment='treatment',
outcome='conversion',
graph=causal_graph,
common_causes=['company_size_medium', 'company_size_large', 'is_busy']
)
# Identify & Estimate
identified_estimand = model.identify_effect()
estimate = model.estimate_effect(
identified_estimand,
method_name="backdoor.propensity_score_matching"
)
print(f"\nDoWhyによるATE推定: {estimate.value:.4f}")
# 頑健性チェック(簡易版)
refute_result = model.refute_estimate(
identified_estimand,
estimate,
method_name="random_common_cause"
)
print(f"頑健性チェック: {refute_result.new_effect:.4f}")
if abs(refute_result.new_effect - estimate.value) < 0.05:
print("✅ 頑健性チェックPass → CATEの推定に進む")
proceed_to_cate = True
else:
print("❌ 頑健性チェックFail → 因果グラフの見直しが必要")
proceed_to_cate = False
# ==========================================
# ステップ2: CausalMLでCATE推定とUplift Modeling
# ==========================================
if proceed_to_cate:
print("\n【ステップ2: CausalMLでCATE推定】")
# データ準備
X = data[['company_size_medium', 'company_size_large', 'is_busy']].values
treatment = data['treatment'].values
y = data['conversion'].values
# X-Learnerでカスタマイズされた効果を推定
x_learner = BaseXLearner(learner=XGBClassifier(random_state=42))
x_learner.fit(X=X, treatment=treatment, y=y)
cate = x_learner.predict(X=X).flatten()
# 結果をデータフレームに追加
data['cate'] = cate
print(f"CATE推定完了。平均CATE: {cate.mean():.4f}")
# ==========================================
# ステップ3: セグメンテーションと意思決定
# ==========================================
print("\n【ステップ3: ビジネス意思決定】")
# 高CATEセグメントを特定
data['segment'] = pd.cut(
data['cate'],
bins=[-np.inf, 0, 0.1, np.inf],
labels=['Low', 'Medium', 'High']
)
# セグメント別の分析
segment_analysis = data.groupby('segment').agg({
'cate': ['count', 'mean'],
'conversion': 'mean'
}).round(4)
print("\nセグメント別分析:")
print(segment_analysis)
# 推奨アクション
high_cate_count = (data['segment'</span >] == 'High').sum()
total_count = len(data)
print(f"\n【推奨アクション】")
print(f"高CATE層: {high_cate_count}社 ({high_cate_count/total_count*100:.1f}%)")
print(f"→ この層に広告予算を集中させることで、ROIを最大化できます")
# ==========================================
# ステップ4: LLMで最終レポート生成(Phase 5統合)
# ==========================================
print("\n【ステップ4: LLMでレポート生成】")
from openai import OpenAI
client = OpenAI(api_key="your-api-key-here")
report_prompt = f"""
あなたはマーケティングサイエンティストです。以下の因果推論分析結果を経営層向けレポートに変換してください。
【DoWhyによるATE分析】
- 平均処置効果(ATE): {estimate.value:.4f}
- 頑健性チェック: Pass
【CausalMLによるCATE分析】
- 平均CATE: {cate.mean():.4f}
- 高CATE層: {high_cate_count}社 ({high_cate_count/total_count*100:.1f}%)
【セグメント別分析】
{segment_analysis.to_string()}
【要件】
1. 3行以内のエグゼクティブサマリー
2. 具体的な予算配分提案
3. 期待されるROI改善効果
"""
response = client.chat.completions.create(
model="gpt-4",
messages=[
{"role": "system", "content": "経営層向けレポート作成の専門家"},
{"role": "user", "content": report_prompt}
],
temperature=0.7
)
print("\n" + "="*80)
print(" 因果推論分析 最終レポート")
print("="*80 + "\n")
print(response.choices[0].message.content)
print("\n" + "="*80)
因果推論の3つの理論的枠組み
因果推論には3つの主要な理論的枠組みがあります。Phase 1-6で学んだ手法を整理します。
1. Pearl(グラフィカルモデル)
- 核心概念:DAG(有向非巡回グラフ)とdo-calculus
- Phase 1-6での対応:
- Phase 1 Day 2: 因果グラフの基礎
- Phase 5 Day 5-7: LiNGAMによる因果探索
- DoWhyのModelステップ
- 強み:因果構造を明示化、識別可能性の判定が可能
- 弱み:DAGの構築に専門知識が必要
2. Rubin(ポテンシャルアウトカム)
- 核心概念:反事実(Counterfactual)とATT/ATE
- Phase 1-6での対応:
- Phase 1 Day 3-7: RCT、DID、PSM、IV、RDD
- Phase 4 Day 3-5: DML、Causal Forest
- DoWhyのEstimateステップ
- 強み:直感的、統計手法と相性が良い
- 弱み:複雑な因果構造の表現が困難
3. 構造因果モデル(SCM)
- 核心概念:因果メカニズムの数式表現
- Phase 1-6での対応:
- Phase 2 Day 4-7: ベイズネットワーク
- Phase 3 Day 4-7: VAR、SVAR
- Phase 6 Week 1: MMMのAdstock/Saturation
- 強み:メカニズムを明示的にモデル化、予測と介入の両立
- 弱み:正確なメカニズムの特定が困難
📊 Phase 1-6の因果推論手法 完全マップ
| Phase | 主要手法 | DoWhy/CausalML対応 | 理論的枠組み |
|---|---|---|---|
| Phase 1 | RCT、DID、PSM、IV、RDD | DoWhy: backdoor.*、iv.* | Rubin |
| Phase 2 | ベイズ推論、階層モデル | PyMCとの統合 | SCM |
| Phase 3 | VAR、SVAR、因果性検定 | 時系列特化(DoWhy非対応) | SCM |
| Phase 4 | DML、Causal Forest、CATE | DoWhy: econml.* CausalML: Meta-Learners |
Rubin + ML |
| Phase 5 | LiNGAM、PC、GES | 因果探索(DoWhy-GCM) | Pearl |
| Phase 6 | MMM、Uplift Modeling | PyMC-Marketing CausalML: Uplift |
SCM + Rubin |
重要な洞察:すべての手法は相互補完的。実務では複数の手法を組み合わせて、因果推論の信頼性を高めることが重要です。
Week 2完了後のチェックリスト
目標:18週間の学習すべてを統合し、実務で使える完全な意思決定支援システムを構築する。
Week 3は18週間の集大成
このWeekでは、Phase 0-6のすべての技術を1つのプロジェクトに統合します。
- Day 1-3:MMMの結果から予算を最適化
- Day 4-5:18週間の全技術を統合した総合プロジェクト
- Day 6-7:最終レポートとキャリアパス
予算最適化の数理モデル(月曜日)
学習内容:
- 予算最適化問題の定式化
- 制約条件の設定(予算上限、最小出稿額等)
- 目的関数の選択(ROI最大化 vs コンバージョン最大化)
- scipy.optimizeによる実装
予算最適化の数学的定式化
目的関数:総コンバージョン数(または総利益)を最大化
where fc(xc) = saturation(adstock(xc)) (MMMで推定された関数)
制約条件:
- 予算制約:Σc xc ≤ B (総予算B以内)
- 非負制約:xc ≥ 0 (広告費は0以上)
- 最小出稿額:xc ≥ Lc (チャネルcの最小出稿額)
- 最大出稿額:xc ≤ Uc (チャネルcの最大出稿額)
これは非線形制約付き最適化問題であり、scipy.optimizeのSLSQP法で解けます。
# 予算最適化の実装
import numpy as np
from scipy.optimize import minimize, LinearConstraint, Bounds
import pandas as pd
import matplotlib.pyplot as plt# ==========================================# ステップ1: MMMの結果からレスポンス関数を定義
# ==========================================
def channel_response(spend, channel_params):
"""
チャネル別のレスポンス関数(MMMで推定)
Parameters:
-----------
spend : float
広告費(千円)
channel_params : dict
チャネルのパラメータ(adstock、saturation)
Returns:
--------
float
予測されるコンバージョン数
"""
# Adstock(キャリーオーバー効果)
decay = channel_params['decay']
adstocked = spend # 簡易版:1期間のみ
# Saturation(飽和効果) - Hill関数
k = channel_params['half_saturation']
s = channel_params['slope']
saturated = adstocked**s / (k**s + adstocked**s)
# スケール調整
scale = channel_params['scale']
return saturated * scale
# MMMから推定されたチャネルパラメータ(例)
channel_params = {
'google_ads': {
'decay': 0.3,
'half_saturation': 300, # 300千円で50%の効果
'slope': 2.0,
'scale': 15 # 最大15件のコンバージョン
},
'sns_ads': {
'decay': 0.4,
'half_saturation': 150,
'slope': 1.5,
'scale': 10
},
'content_marketing': {
'decay': 0.8, # 長期効果
'half_saturation': 100,
'slope': 1.2,
'scale': 20 # 最も高い効果
}
}
# ==========================================
# ステップ2: 最適化問題の定義
# ==========================================
def objective(x):
"""
目的関数:総コンバージョン数を最大化
(最小化問題に変換するため負の値を返す)
Parameters:
-----------
x : array-like
各チャネルの広告費 [google, sns, content]
Returns:
--------
float
負の総コンバージョン数
"""
channels = ['google_ads', 'sns_ads', 'content_marketing']
total_conversions = sum(
channel_response(x[i], channel_params[channels[i]])
for i in range(len(channels))
)
return -total_conversions # 最大化→最小化
# ==========================================
# ステップ3: 制約条件の設定
# ==========================================
# 総予算制約
total_budget = 800 # 800千円(80万円)
# 予算制約:x1 + x2 + x3 <= 800
budget_constraint = LinearConstraint(
np.ones(3), # [1, 1, 1]
0, # 下限
total_budget # 上限
)
# 各チャネルの予算範囲
# [最小出稿額、最大出稿額]
bounds = Bounds(
[50, 50, 0], # 最小:Google 50k、SNS 50k、Content 0k
[500, 300, 400] # 最大:Google 500k、SNS 300k、Content 400k
)
# ==========================================
# ステップ4: 最適化の実行
# ==========================================
# 初期値(現在の予算配分)
x0 = np.array([300, 200, 100]) # Google 300k、SNS 200k、Content 100k
print("\n【現在の予算配分】")
print(f"Google広告: {x0[0]:.0f}千円")
print(f"SNS広告: {x0[1]:.0f}千円")
print(f"コンテンツ: {x0[2]:.0f}千円")
print(f"総予算: {x0.sum():.0f}千円")
current_conversions = -objective(x0)
print(f"予測コンバージョン数: {current_conversions:.1f}件\n")
# 最適化実行
result = minimize(
objective,
x0,
method='SLSQP', # Sequential Least Squares Programming
bounds=bounds,
constraints=[budget_constraint],
options={'ftol': 1e-9, 'maxiter': 1000}
)
print("\n【最適化後の予算配分】")
print(f"Google広告: {result.x[0]:.0f}千円 ({(result.x[0]-x0[0])/x0[0]*100:+.1f}%)")
print(f"SNS広告: {result.x[1]:.0f}千円 ({(result.x[1]-x0[1])/x0[1]*100:+.1f}%)")
print(f"コンテンツ: {result.x[2]:.0f}千円 ({(result.x[2]-x0[2])/x0[2]*100:+.1f}%)")
print(f"総予算: {result.x.sum():.0f}千円")
optimal_conversions = -result.fun
print(f"\n予測コンバージョン数: {optimal_conversions:.1f}件")
print(f"改善: +{optimal_conversions - current_conversions:.1f}件 ({(optimal_conversions/current_conversions-1)*100:.1f}%)")
# ==========================================
# ステップ5: 結果の可視化
# ==========================================
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
channels = ['Google広告', 'SNS広告', 'コンテンツ']
# 1. 予算配分の比較(棒グラフ)
x_pos = np.arange(len(channels))
width = 0.35
axes[0, 0].bar(x_pos - width/2, x0, width, label='現在', alpha=0.8)
axes[0, 0].bar(x_pos + width/2, result.x, width, label='最適化後', alpha=0.8)
axes[0, 0].set_xlabel('チャネル')
axes[0, 0].set_ylabel('予算(千円)')
axes[0, 0].set_title('予算配分の比較')
axes[0, 0].set_xticks(x_pos)
axes[0, 0].set_xticklabels(channels)
axes[0, 0].legend()
axes[0, 0].grid(True, alpha=0.3)
# 2. レスポンス曲線
spend_range = np.linspace(0, 500, 100)
for i, (ch_name, ch_key) in enumerate(zip(channels, channel_params.keys())):
responses = [channel_response(s, channel_params[ch_key]) for s in spend_range]
axes[0, 1].plot(spend_range, responses, label=ch_name)
# 現在の予算位置
axes[0, 1].scatter(x0[i], channel_response(x0[i], channel_params[ch_key]), marker='o', s=100)
# 最適化後の予算位置
axes[0, 1].scatter(result.x[i], channel_response(result.x
[i], channel_params[ch_key]), marker='*', s=200)
axes[0, 1].set_xlabel('広告費(千円)')
axes[0, 1].set_ylabel('コンバージョン数')
axes[0, 1].set_title('チャネル別レスポンス曲線')
axes[0, 1].legend()
axes[0, 1].grid(True, alpha=0.3)
# 3. 予算配分比率(円グラフ)
axes[1, 0].pie(result.x, labels=channels, autopct='%1.1f%%', startangle=90)
axes[1, 0].set_title('最適化後の予算配分比率')
# 4. ROI比較
current_roi = [(current_conversions * 1000000 / x0.sum() / 1000) - 1] # 1CV=100万円と仮定
optimal_roi = [(optimal_conversions * 1000000 / result.x.sum() / 1000) - 1]
axes[1, 1].bar(['現在', '最適化後'], [current_roi[0]*100, optimal_roi[0]*100], color=['orange', 'green'], alpha=0.8)
axes[1, 1].set_ylabel('ROI(%)')
axes[1, 1].set_title('ROIの改善')
axes[1, 1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
予算最適化の実務ポイント
- 制約条件は現実的に:最小出稿額はチャネルの技術的制約を反映(例:Google広告は月5万円未満だと効果が薄い)
- 定期的な再最適化:市場環境が変われば最適配分も変わる。月次で再計算を推奨
- 段階的な実行:一度に大きく予算を変えるとリスクが高い。10-20%ずつ段階的に
- A/Bテストでの検証:最適化結果が本当に正しいか、小規模テストで検証
- 季節性の考慮:繁忙期・閑散期で最適配分は異なる。時期別に最適化
What-Ifシナリオ分析(火曜日)
学習内容:
- What-Ifシナリオとは:「もし~だったら?」の分析
- 複数シナリオの比較(予算増額、チャネル削減等)
- 感度分析:パラメータの不確実性への対処
- LLMを使った対話的シナリオ分析
What-Ifシナリオ分析の4つの使い方
- 予算増減シナリオ:「予算を20%増やしたら何件増えるか?」
- チャネルミックス:「SNS広告をやめてコンテンツに振ったら?」
- 外部環境変化:「競合が広告を強化したら?」
- 新規チャネル追加:「YouTube広告を追加したら?」
# What-Ifシナリオ分析の実装
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.optimize import minimize, LinearConstraint, Bounds# ==========================================# シナリオ1: 予算増減の影響
# ==========================================
print("\n【シナリオ1: 予算増減の影響】")
def optimize_for_budget(budget, channel_params):
"""特定の予算で最適配分を計算"""
def objective(x):
channels = list(channel_params.keys())
total = sum(channel_response(x[i], channel_params[channels[i]])
for i in range(len(channels)))
return -total
budget_constraint = LinearConstraint(np.ones(3), 0, budget)
bounds = Bounds([50, 50, 0], [500, 300, 400])
x0 = np.array([budget/3, budget/3, budget/3])
result = minimize(objective, x0, method='SLSQP',
bounds=bounds, constraints=[budget_constraint])
return result.x, -result.fun
# 複数の予算水準でシミュレーション
budget_scenarios = np.linspace(400, 1200, 9) # 40万円〜120万円
results = []
for budget in budget_scenarios:
allocation, conversions = optimize_for_budget(budget, channel_params)
roi = (conversions * 1000000 / (budget * 1000)) - 1 # 1CV=100万円
results.append({
'予算': budget,
'コンバージョン': conversions,
'ROI': roi * 100,
'Google': allocation[0],
'SNS': allocation[1],
'Content': allocation[2]
})
scenario_df = pd.DataFrame(results)
print("\n予算シナリオ分析結果:")
print(scenario_df.to_string(index=False))
# 可視化
fig, axes = plt.subplots(1, 2, figsize=(14, 6))
# コンバージョン数とROI
ax1 = axes[0]
ax1_twin = ax1.twinx()
ax1.plot(scenario_df['予算'], scenario_df['コンバージョン'],
marker='o', color='blue', label='コンバージョン数')
ax1_twin.plot(scenario_df['予算'], scenario_df['ROI'],
marker='s', color='red', label='ROI(%)')
ax1.set_xlabel('予算(千円)')
ax1.set_ylabel('コンバージョン数', color='blue')
ax1_twin.set_ylabel('ROI(%)', color='red')
ax1.set_title('予算増減シナリオ:効果とROI')
ax1.grid(True, alpha=0.3)
ax1.legend(loc='upper left')
ax1_twin.legend(loc='upper right')
# チャネル別予算配分の変化
axes[1].stackplot(
scenario_df['予算'],
scenario_df['Google'],
scenario_df['SNS'],
scenario_df['Content'],
labels=['Google広告', 'SNS広告', 'コンテンツ'],
alpha=0.8
)
axes[1].set_xlabel('総予算(千円)')
axes[1].set_ylabel('チャネル別配分(千円)')
axes[1].set_title('予算増減に伴う最適配分の変化')
axes[1].legend(loc='upper left')
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# ==========================================
# シナリオ2: チャネル削減の影響
# ==========================================
print("\n【シナリオ2: チャネル削減の影響】")
def scenario_without_channel(budget, channel_params, exclude_channel):
"""特定チャネルを除外した場合の最適配分"""
available_channels = {k: v for k, v in channel_params.items() if k != exclude_channel}
n_channels = len(available_channels)
def objective(x):
channels = list(available_channels.keys())
total = sum(channel_response(x[i], available_channels[channels[i]])
for i in range(n_channels))
return -total
budget_constraint = LinearConstraint(np.ones(n_channels), 0, budget)
# チャネルごとのbounds設定
if exclude_channel == 'google_ads':
bounds = Bounds([50, 0], [300, 400]) # SNS, Content
elif exclude_channel == 'sns_ads':
bounds = Bounds([50, 0], [500, 400]) # Google, Content
else: # content_marketing
bounds = Bounds([50, 50], [500, 300]) # Google, SNS
x0 = np.ones(n_channels) * (budget / n_channels)
result = minimize(objective, x0, method='SLSQP',
bounds=bounds, constraints=[budget_constraint])
return result.x, -result.fun
budget = 800
exclusion_scenarios = []
# 全チャネル使用(ベースライン)
baseline_alloc, baseline_conv = optimize_for_budget(budget, channel_params)
exclusion_scenarios.append({
'シナリオ': '全チャネル(ベースライン)',
'コンバージョン': baseline_conv,
'損失': 0,
'損失率': 0
})
# 各チャネルを除外
for channel in channel_params.keys():
alloc, conv = scenario_without_channel(budget, channel_params, channel)
loss = baseline_conv - conv
exclusion_scenarios.append({
'シナリオ': f'{channel} 除外',
'コンバージョン': conv,
'損失': loss,
'損失率': (loss / baseline_conv) * 100
})
exclusion_df = pd.DataFrame(exclusion_scenarios)
print("\nチャネル削減シナリオ:")
print(exclusion_df.to_string(index=False))
# ==========================================
# シナリオ3: LLMによる対話的分析
# ==========================================
print("\n【シナリオ3: LLMによる対話的What-If分析】")
from openai import OpenAI
client = OpenAI(api_key="your-api-key-here")
def llm_whatif_analysis(question, scenario_df, exclusion_df):
"""LLMを使ってWhat-Ifシナリオに回答"""
context = f"""
あなたはマーケティング予算最適化の専門家です。以下のシミュレーション結果に基づいて、ユーザーの質問に答えてください。
【予算増減シナリオ】
{scenario_df.to_string(index=False)}
【チャネル削減シナリオ】
{exclusion_df.to_string(index=False)}
現在の予算: 800千円(80万円)
現在のコンバージョン: {baseline_conv:.1f}件
ユーザーの質問: {question}
具体的な数値を使って、わかりやすく回答してください。
"""
response = client.chat.completions.create(
model="gpt-4",
messages=[
{"role": "system", "content": "マーケティング予算最適化の専門家"},
{"role": "user", "content": context}
],
temperature=0.5
)
return response.choices[0].message.content
# サンプル質問
questions = [
"予算を100万円に増やすべきですか?それとも現状維持が良いですか?",
"SNS広告をやめてその分をコンテンツに回したらどうなりますか?",
"最もコストパフォーマンスが良い予算水準はいくらですか?"
]
for i, q in enumerate(questions, 1):
print(f"\n質問{i}: {q}")
answer = llm_whatif_analysis(q, scenario_df, exclusion_df)
print(f"\n回答:\n{answer}")
print("\n" + "="*80)
What-Ifシナリオ分析の実務活用
- 経営会議での説得材料:「予算を20%増やせば、コンバージョンは15%増えます」と数値で示す
- リスク評価:「最悪のシナリオでもROIは50%以上確保できます」と安心感を与える
- チャネル戦略の検証:「このチャネルを削減しても影響は軽微」とリソース再配分を提案
- 感度分析:「パラメータが20%ずれても結論は変わらない」と頑健性を示す
- LLMとの統合:経営層が自然言語で質問でき、即座に回答を得られる
予算最適化の実装演習(水曜日)
学習内容:
- 実際のビジネスデータでの予算最適化
- 複雑な制約条件の実装(相互依存、期間制約等)
- 動的な予算配分(時期による最適化)
- 経営層向けダッシュボードの作成
予算最適化の落とし穴
- 過学習:過去データに最適化しすぎると、未来で失敗する
- 局所最適解:初期値によって異なる最適解に収束する可能性
- 外部要因の無視:競合の動き、季節性、経済状況を考慮していない
- ブランド価値の軽視:短期ROI重視で、長期的なブランド構築を無視
- 実行可能性の欠如:数学的には最適でも、実務では実行不可能な配分
対策:定期的な見直し、複数の初期値での最適化、ドメイン知識の組み込み
Day 3の実装演習課題
以下の課題に取り組んで、予算最適化の実践力を身につけましょう:
- サイトのデータで最適化:実際のサイトデータ(もしくはシミュレーション)で予算最適化を実装
- 季節性を考慮:繁忙期(3-4月、9-10月)と閑散期で異なる最適配分を計算
- 競合対応:競合が広告を強化した場合のシナリオ分析
- 新規チャネル追加:YouTube広告を追加した場合の最適配分を計算
- ダッシュボード作成:Streamlit等で対話的なダッシュボードを作成
Phase 0-6完全統合プロジェクト(木金)
学習内容:
- 18週間のすべての技術を1つのプロジェクトに統合
- データ収集 → 分析 → 意思決定 → 効果測定の全サイクル
- 不動産マーケティングの完全な意思決定支援システム
- LLMで経営層向けの最終レポート生成
🎯 18週間統合プロジェクト:不動産マーケティング意思決定支援システム
プロジェクト概要:
不動産仲介サイトのマーケティング担当者が、データに基づいて意思決定できる完全なシステムを構築します。
システムの全体フロー
Phase 0-1: データ収集と因果推論
Google Analytics、広告プラットフォームからデータ収集 → RCT/DID/PSMで因果効果を検証
Phase 2: ベイズ統計で不確実性を定量化
効果推定の信頼区間を計算 → 意思決定のリスクを評価
Phase 3: 時系列分析で季節性を把握
VARモデルで繁忙期・閑散期を予測 → 時期別の戦略立案
Phase 4: 機械学習でCATEを推定
DML/Causal Forestで顧客セグメント別効果を推定 → ターゲティング最適化
Phase 5: LLMで因果探索と自動化
LiNGAMで未知の因果関係を発見 → LLMでレポート
Phase 5: LLMで因果探索と自動化
LiNGAMで未知の因果関係を発見 → LLMでレポート自動生成
Phase 6: MMM & 予算最適化
すべてを統合 → 最適な意思決定支援
📊 統合プロジェクト実装(全フェーズ統合)
目標:18週間で学んだすべての技術を1つのプロジェクトに統合
ステップ1: データ統合基盤の構築
import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
import pymc as pm
import arviz as az
from dowhy import CausalModel
from causalml.inference.meta import XLearner
from scipy.optimize import minimize
import anthropic
import os
# Phase 0: 統合データ基盤の構築
class MarketingDataPipeline:
"""18週間で学んだすべてを統合するデータパイプライン"""
def __init__(self):
self.raw_data = {}
self.processed_data = {}
self.causal_graph = None
self.mmm_results = None
self.optimization_results = None
def load_all_data_sources(self):
"""Phase 0: 複数データソースの統合"""
# Google Ads, Facebook Ads, GA4, CRM, 売上データなど
self.raw_data['ads'] = pd.read_csv('google_ads_data.csv')
self.raw_data['social'] = pd.read_csv('facebook_ads_data.csv')
self.raw_data['web'] = pd.read_csv('ga4_data.csv')
self.raw_data['sales'] = pd.read_csv('sales_data.csv')
self.raw_data['crm'] = pd.read_csv('crm_data.csv')
# データクリーニング
for key in self.raw_data:
self.raw_data[key]['date'] = pd.to_datetime(self.raw_data[key]['date'])
self.raw_data[key].set_index('date', inplace=True)
return self
def engineer_features(self):
"""Phase 1: 特徴量エンジニアリング"""
# すべてのデータを日次で結合
df = pd.concat([
self.raw_data['ads'][['spend', 'impressions', 'clicks']].add_prefix('google_'),
self.raw_data['social'][['spend', 'impressions', 'clicks']].add_prefix('fb_'),
self.raw_data['web'][['sessions', 'pageviews', 'bounce_rate']],
self.raw_data['sales'][['revenue', 'orders', 'aov']],
self.raw_data['crm'][['leads', 'mql', 'sql']]
], axis=1)
# Adstock変換(Phase 6で学習)
df['google_spend_adstock'] = self.apply_adstock(df['google_spend'], decay=0.7)
df['fb_spend_adstock'] = self.apply_adstock(df['fb_spend'], decay=0.6)
# 飽和効果(Phase 6で学習)
df['google_spend_saturated'] = self.apply_saturation(df['google_spend_adstock'], alpha=2.5)
df['fb_spend_saturated'] = self.apply_saturation(df['fb_spend_adstock'], alpha=3.0)
# 相乗効果の計算
df['channel_synergy'] = df['google_spend_saturated'] * df['fb_spend_saturated']
# 時系列特徴量
df['day_of_week'] = df.index.dayofweek
df['is_weekend'] = (df['day_of_week'] >= 5).astype(int)
df['month'] = df.index.month
df['quarter'] = df.index.quarter
# ラグ特徴量(Phase 1で学習)
for lag in [1, 7, 14, 30]:
df[f'revenue_lag_{lag}'] = df['revenue'].shift(lag)
df[f'google_spend_lag_{lag}'] = df['google_spend'].shift(lag)
# 移動平均(Phase 1で学習)
df['revenue_ma7'] = df['revenue'].rolling(7).mean()
df['revenue_ma30'] = df['revenue'].rolling(30).mean()
self.processed_data['full'] = df.dropna()
return self
def apply_adstock(self, x, decay=0.7, max_lag=8):
"""Adstock変換"""
adstocked = np.zeros(len(x))
for t in range(len(x)):
for lag in range(min(t + 1, max_lag)):
adstocked[t] += x.iloc[t - lag] * (decay ** lag)
return adstocked
def apply_saturation(self, x, alpha=2.0, lam=1.0):
"""Hill関数による飽和効果"""
return lam * (x ** alpha) / (1 + x ** alpha)
def build_causal_model(self):
"""Phase 2-4: 因果推論の実行"""
df = self.processed_data['full']
# DoWhy因果モデル(Phase 2-3で学習)
causal_graph = """
digraph {
google_spend_saturated -> revenue;
fb_spend_saturated -> revenue;
channel_synergy -> revenue;
sessions -> revenue;
leads -> revenue;
day_of_week -> revenue;
is_weekend -> revenue;
google_spend_saturated -> sessions;
fb_spend_saturated -> sessions;
}
"""
model = CausalModel(
data=df,
treatment='google_spend_saturated',
outcome='revenue',
graph=causal_graph
)
# 因果効果の推定
identified_estimand = model.identify_effect(proceed_when_unidentifiable=True)
estimate = model.estimate_effect(
identified_estimand,
method_name="backdoor.linear_regression"
)
# Refutation(Phase 3で学習)
refute_results = {
'random_common_cause': model.refute_estimate(
identified_estimand, estimate, method_name="random_common_cause"
),
'placebo_treatment': model.refute_estimate(
identified_estimand, estimate, method_name="placebo_treatment_refuter"
),
'data_subset': model.refute_estimate(
identified_estimand, estimate, method_name="data_subset_refuter"
)
}
self.causal_graph = {
'model': model,
'estimate': estimate,
'refutation': refute_results
}
return self
def run_uplift_modeling(self):
"""Phase 4: Uplift Modelingでセグメント別効果を推定"""
df = self.processed_data['full']
# 特徴量の準備
X = df[[
'sessions', 'pageviews', 'bounce_rate',
'day_of_week', 'is_weekend', 'month'
]].values
# 処置(広告出稿)
treatment = (df['google_spend'] > df['google_spend'].median()).astype(int).values
# アウトカム(収益)
y = df['revenue'].values
# X-Learner(Phase 4で学習)
xl = XLearner(learner=GradientBoostingRegressor())
xl.fit(X=X, treatment=treatment, y=y)
# CATE推定
cate = xl.predict(X=X)
# セグメント別効果
df['cate'] = cate
df['cate_segment'] = pd.qcut(df['cate'], q=4, labels=['Low', 'Medium', 'High', 'Very High'])
self.processed_data['uplift'] = df
return self
def run_mmm(self):
"""Phase 6: Marketing Mix Modelingの実行"""
df = self.processed_data['full']
with pm.Model() as mmm_model:
# チャネル別のパラメータ
google_coef = pm.Normal('google_coef', mu=0, sigma=10)
fb_coef = pm.Normal('fb_coef', mu=0, sigma=10)
synergy_coef = pm.Normal('synergy_coef', mu=0, sigma=5)
# セッション効果
sessions_coef = pm.Normal('sessions_coef', mu=0, sigma=10)
# ベースライン
baseline = pm.Normal('baseline', mu=df['revenue'].mean(), sigma=df['revenue'].std())
# 曜日効果
dow_effect = pm.Normal('dow_effect', mu=0, sigma=5, shape=7)
# 予測
mu = (baseline +
google_coef * df['google_spend_saturated'].values +
fb_coef * df['fb_spend_saturated'].values +
synergy_coef * df['channel_synergy'].values +
sessions_coef * df['sessions'].values +
dow_effect[df['day_of_week'].values])
# ノイズ
sigma = pm.HalfNormal('sigma', sigma=df['revenue'].std())
# 尤度
likelihood = pm.Normal('revenue', mu=mu, sigma=sigma, observed=df['revenue'].values)
# サンプリング
trace = pm.sample(2000, tune=1000, return_inferencedata=True, random_seed=42)
self.mmm_results = {
'model': mmm_model,
'trace': trace,
'summary': az.summary(trace)
}
return self
def optimize_budget(self, total_budget=100000):
"""Phase 6: 予算最適化"""
trace = self.mmm_results['trace']
# 事後分布の平均値を使用
google_coef = trace.posterior['google_coef'].mean().values
fb_coef = trace.posterior['fb_coef'].mean().values
synergy_coef = trace.posterior['synergy_coef'].mean().values
baseline = trace.posterior['baseline'].mean().values
def objective(budget_allocation):
"""最大化すべき収益関数"""
google_budget, fb_budget = budget_allocation
# Adstock + 飽和効果
google_effect = self.apply_saturation(
self.apply_adstock(np.array([google_budget]), decay=0.7), alpha=2.5
)[0]
fb_effect = self.apply_saturation(
self.apply_adstock(np.array([fb_budget]), decay=0.6), alpha=3.0
)[0]
# 相乗効果
synergy = google_effect * fb_effect
# 予測収益
revenue = baseline + google_coef * google_effect + fb_coef * fb_effect + synergy_coef * synergy
return -revenue # 最小化問題に変換
# 制約条件
constraints = [
{'type': 'eq', 'fun': lambda x: x[0] + x[1] - total_budget}, # 合計予算
]
bounds = [(0, total_budget), (0, total_budget)]
# 初期値(現在の配分)
current_google = self.processed_data['full']['google_spend'].mean()
current_fb = self.processed_data['full']['fb_spend'].mean()
x0 = [current_google, current_fb]
# 最適化実行
result = minimize(
objective,
x0=x0,
method='SLSQP',
bounds=bounds,
constraints=constraints
)
self.optimization_results = {
'optimal_allocation': {
'google': result.x[0],
'facebook': result.x[1]
},
'expected_revenue': -result.fun,
'current_allocation': {
'google': current_google,
'facebook': current_fb
}
}
return self
def generate_llm_insights(self):
"""Phase 5: LLMで洞察を自動生成"""
client = anthropic.Anthropic(api_key=os.environ.get("ANTHROPIC_API_KEY"))
# 分析結果をまとめる
analysis_summary = f"""
【因果推論結果】
- Google広告の因果効果: {self.causal_graph['estimate'].value:.2f}
- Refutationテスト: すべてPass
【MMM結果】
- Google広告係数: {self.mmm_results['summary'].loc['google_coef', 'mean']:.2f}
- Facebook広告係数: {self.mmm_results['summary'].loc['fb_coef', 'mean']:.2f}
- 相乗効果係数: {self.mmm_results['summary'].loc['synergy_coef', 'mean']:.2f}
【予算最適化結果】
- 最適なGoogle予算: ${self.optimization_results['optimal_allocation']['google']:,.0f}
- 最適なFacebook予算: ${self.optimization_results['optimal_allocation']['facebook']:,.0f}
- 期待収益: ${self.optimization_results['expected_revenue']:,.0f}
【Uplift Modeling結果】
- High CATEセグメントの平均効果: ${self.processed_data['uplift'][self.processed_data['uplift']['cate_segment']=='High']['cate'].mean():,.0f}
"""
message = client.messages.create(
model="claude-sonnet-4-5-20250929",
max_tokens=2000,
messages=[{
"role": "user",
"content": f"""
以下のマーケティング分析結果から、経営層向けの戦略的インサイトを3つ抽出してください。
{analysis_summary}
各インサイトには:
1. 発見事項
2. ビジネスインパクト
3. 具体的なアクションプラン
を含めてください。
"""
}]
)
return message.content[0].text
# 実行例
pipeline = MarketingDataPipeline()
pipeline.load_all_data_sources()
pipeline.engineer_features()
pipeline.build_causal_model()
pipeline.run_uplift_modeling()
pipeline.run_mmm()
pipeline.optimize_budget(total_budget=100000)
insights = pipeline.generate_llm_insights()
print(insights)
📈 統合ダッシュボードの構築
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import streamlit as st
class MarketingDashboard:
"""18週間の学習成果を可視化する統合ダッシュボード"""
def __init__(self, pipeline):
self.pipeline = pipeline
def create_executive_dashboard(self):
"""経営層向けダッシュボード"""
fig = make_subplots(
rows=3, cols=2,
subplot_titles=(
'チャネル別ROI比較',
'予算最適化シミュレーション',
'因果効果の時系列推移',
'セグメント別CATE分布',
'MMM係数の事後分布',
'相乗効果のヒートマップ'
),
specs=[
[{'type': 'bar'}, {'type': 'scatter'}],
[{'type': 'scatter'}, {'type': 'box'}],
[{'type': 'violin'}, {'type': 'heatmap'}]
]
)
# 1. チャネル別ROI
df = self.pipeline.processed_data['full']
google_roi = df['revenue'].sum() / df['google_spend'].sum()
fb_roi = df['revenue'].sum() / df['fb_spend'].sum()
fig.add_trace(
go.Bar(
x=['Google Ads', 'Facebook Ads'],
y=[google_roi, fb_roi],
marker_color=['#4285F4', '#1877F2']
),
row=1, col=1
)
# 2. 予算最適化シミュレーション
budget_range = np.linspace(50000, 150000, 50)
expected_revenues = []
for budget in budget_range:
self.pipeline.optimize_budget(total_budget=budget)
expected_revenues.append(
self.pipeline.optimization_results['expected_revenue']
)
fig.add_trace(
go.Scatter(
x=budget_range,
y=expected_revenues,
mode='lines+markers',
name='期待収益'
),
row=1, col=2
)
# 3. 因果効果の時系列推移
rolling_effect = []
for i in range(30, len(df)):
subset = df.iloc[i-30:i]
# ここで局所的な因果効果を推定
effect = subset['google_spend_saturated'].corr(subset['revenue'])
rolling_effect.append(effect)
fig.add_trace(
go.Scatter(
x=df.index[30:],
y=rolling_effect,
mode='lines',
name='因果効果'
),
row=2, col=1
)
# 4. セグメント別CATE分布
uplift_df = self.pipeline.processed_data['uplift']
for segment in ['Low', 'Medium', 'High', 'Very High']:
fig.add_trace(
go.Box(
y=uplift_df[uplift_df['cate_segment']==segment]['cate'],
name=segment
),
row=2, col=2
)
# 5. MMM係数の事後分布
trace = self.pipeline.mmm_results['trace']
fig.add_trace(
go.Violin(
y=trace.posterior['google_coef'].values.flatten(),
name='Google係数'
),
row=3, col=1
)
fig.add_trace(
go.Violin(
y=trace.posterior['fb_coef'].values.flatten(),
name='FB係数'
),
row=3, col=1
)
# 6. 相乗効果のヒートマップ
google_levels = np.linspace(0, 50000, 20)
fb_levels = np.linspace(0, 50000, 20)
synergy_matrix = np.zeros((20, 20))
for i, g in enumerate(google_levels):
for j, f in enumerate(fb_levels):
synergy_matrix[i, j] = self.pipeline.apply_saturation(
np.array([g]), alpha=2.5
)[0] * self.pipeline.apply_saturation(
np.array([f]), alpha=3.0
)[0]
fig.add_trace(
go.Heatmap(
z=synergy_matrix,
x=fb_levels,
y=google_levels,
colorscale='Viridis'
),
row=3, col=2
)
fig.update_layout(
height=1200,
showlegend=False,
title_text="マーケティング統合分析ダッシュボード"
)
return fig
def create_streamlit_app(self):
"""Streamlitアプリの作成"""
st.set_page_config(page_title="Marketing Analytics Hub", layout="wide")
st.title("🚀 マーケティングアナリティクスハブ")
st.markdown("18週間で構築した統合分析プラットフォーム")
# サイドバー
st.sidebar.header("分析設定")
analysis_type = st.sidebar.selectbox(
"分析タイプ",
["統合ダッシュボード", "因果推論", "Uplift Modeling", "MMM", "予算最適化", "LLM洞察"]
)
if analysis_type == "統合ダッシュボード":
fig = self.create_executive_dashboard()
st.plotly_chart(fig, use_container_width=True)
elif analysis_type == "因果推論":
st.subheader("因果推論結果")
st.write(self.pipeline.causal_graph['estimate'])
st.subheader("Refutationテスト")
for test_name, result in self.pipeline.causal_graph['refutation'].items():
st.write(f"**{test_name}:** {result.refutation_result}")
elif analysis_type == "Uplift Modeling":
st.subheader("セグメント別CATE分析")
uplift_df = self.pipeline.processed_data['uplift']
col1, col2 = st.columns(2)
with col1:
st.metric("High CATEセグメント平均効果",
f"${uplift_df[uplift_df['cate_segment']=='High']['cate'].mean():,.0f}")
with col2:
st.metric("Low CATEセグメント平均効果",
f"${uplift_df[uplift_df['cate_segment']=='Low']['cate'].mean():,.0f}")
fig = go.Figure()
for segment in ['Low', 'Medium', 'High', 'Very High']:
fig.add_trace(go.Box(
y=uplift_df[uplift_df['cate_segment']==segment]['cate'],
name=segment
))
st.plotly_chart(fig, use_container_width=True)
elif analysis_type == "MMM":
st.subheader("Marketing Mix Modeling結果")
summary = self.pipeline.mmm_results['summary']
st.dataframe(summary)
# トレースプロット
trace = self.pipeline.mmm_results['trace']
fig = go.Figure()
fig.add_trace(go.Histogram(
x=trace.posterior['google_coef'].values.flatten(),
name='Google係数'
))
fig.add_trace(go.Histogram(
x=trace.posterior['fb_coef'].values.flatten(),
name='FB係数'
))
st.plotly_chart(fig, use_container_width=True)
elif analysis_type == "予算最適化":
st.subheader("予算最適化シミュレーション")
total_budget = st.slider("総予算", 50000, 200000, 100000, 10000)
if st.button("最適化実行"):
self.pipeline.optimize_budget(total_budget=total_budget)
results = self.pipeline.optimization_results
col1, col2, col3 = st.columns(3)
with col1:
st.metric("最適Google予算", f"${results['optimal_allocation']['google']:,.0f}")
with col2:
st.metric("最適Facebook予算", f"${results['optimal_allocation']['facebook']:,.0f}")
with col3:
st.metric("期待収益", f"${results['expected_revenue']:,.0f}")
elif analysis_type == "LLM洞察":
st.subheader("AI生成インサイト")
if st.button("洞察を生成"):
with st.spinner("Claude Sonnetが分析中..."):
insights = self.pipeline.generate_llm_insights()
st.markdown(insights)
# 実行
dashboard = MarketingDashboard(pipeline)
dashboard.create_streamlit_app()
📊 Phase 0-6 技術スタック統合比較
| Phase | 主要技術 | 統合時の役割 | 他フェーズとの連携 |
|---|---|---|---|
| Phase 0 | Pandas, NumPy, Scikit-learn | データ基盤とクリーニング | すべてのフェーズの入力データを準備 |
| Phase 1 | Prophet, ARIMA, LSTM | トレンド予測とベースライン設定 | Phase 6のMMMでベースライン需要として使用 |
| Phase 2-3 | DoWhy, EconML | 因果効果の厳密な推定 | Phase 6のMMM係数の妥当性検証 |
| Phase 4 | CausalML, Uplift Modeling | セグメント別効果の推定 | Phase 6の予算最適化でセグメント別配分 |
| Phase 5 | LiNGAM, Claude API | 未知の因果関係発見と自動レポート | すべてのフェーズの結果を統合解釈 |
| Phase 6 | PyMC-Marketing, SciPy | 最終的な意思決定支援 | すべてのフェーズの結果を統合 |
🎯 経営層向け最終レポート自動生成
import anthropic
from datetime import datetime
import json
class ExecutiveReportGenerator:
"""経営層向けレポートの自動生成(LLM活用)"""
def __init__(self, pipeline, api_key):
self.pipeline = pipeline
self.client = anthropic.Anthropic(api_key=api_key)
def generate_comprehensive_report(self):
"""包括的なレポート生成"""
# すべての分析結果を構造化データとして準備
analysis_results = {
"date": datetime.now().strftime("%Y-%m-%d"),
"causal_inference": {
"google_effect": float(self.pipeline.causal_graph['estimate'].value),
"refutation_passed": all([
r.refutation_result['is_statistically_significant'] == False
for r in self.pipeline.causal_graph['refutation'].values()
])
},
"mmm": {
"google_coef": float(self.pipeline.mmm_results['summary'].loc['google_coef', 'mean']),
"fb_coef": float(self.pipeline.mmm_results['summary'].loc['fb_coef', 'mean']),
"synergy_coef": float(self.pipeline.mmm_results['summary'].loc['synergy_coef', 'mean'])
},
"optimization": {
"current_google": float(self.pipeline.optimization_results['current_allocation']['google']),
"current_fb": float(self.pipeline.optimization_results['current_allocation']['facebook']),
"optimal_google": float(self.pipeline.optimization_results['optimal_allocation']['google']),
"optimal_fb": float(self.pipeline.optimization_results['optimal_allocation']['facebook']),
"expected_revenue": float(self.pipeline.optimization_results['expected_revenue'])
},
"uplift": {
"high_cate_avg": float(self.pipeline.processed_data['uplift'][
self.pipeline.processed_data['uplift']['cate_segment']=='High'
]['cate'].mean()),
"segments": self.pipeline.processed_data['uplift']['cate_segment'].value_counts().to_dict()
}
}
# Claude APIで高度なレポート生成
message = self.client.messages.create(
model="claude-sonnet-4-5-20250929",
max_tokens=4000,
messages=[{
"role": "user",
"content": f"""
あなたは経営層向けのマーケティングアナリティクスレポートを作成する専門家です。
以下の分析結果から、CEO/CFO/CMO向けの包括的なレポートを作成してください。
【分析結果データ】
{json.dumps(analysis_results, indent=2)}
【レポート構成】
1. エグゼクティブサマリー(3つの重要な発見)
2. ビジネスインパクト分析
- 現状の課題
- 改善機会の定量化
3. データ駆動型の推奨事項
- 短期アクション(今月実施)
- 中期戦略(3-6ヶ月)
- 長期投資(6-12ヶ月)
4. 予想されるROI改善
5. リスク評価と対策
【スタイル指定】
- 専門用語は最小限に、ビジネス言語で
- 各推奨事項には具体的な数値目標を含める
- 実行可能性を重視
- 箇条書きで簡潔に
"""
}]
)
return message.content[0].text
def generate_technical_appendix(self):
"""技術者向け詳細付録"""
message = self.client.messages.create(
model="claude-sonnet-4-5-20250929",
max_tokens=3000,
messages=[{
"role": "user",
"content": f"""
マーケティングアナリティクスチーム向けの技術詳細付録を作成してください。
【使用した手法】
- 因果推論: DoWhy (Backdoor Criterion)
- MMM: PyMC-Marketing (Bayesian Inference)
- Uplift: CausalML X-Learner
- 最適化: SciPy SLSQP
【モデル検証結果】
- Refutation Tests: {list(self.pipeline.causal_graph['refutation'].keys())}
- MMM R-hat: < 1.01 (収束確認)
- Cross-validation RMSE: 記載
以下を含めてください:
1. 各手法の選択理由
2. モデルの仮定と制約
3. 検証結果の詳細
4. 今後の改善案
5. 参考文献・実装コード
"""
}]
)
return message.content[0].text
def create_stakeholder_specific_summaries(self):
"""ステークホルダー別サマリー"""
stakeholders = {
"CEO": "全体的なビジネスインパクトとROI改善",
"CFO": "予算配分の最適化と財務的リターン",
"CMO": "マーケティング戦略と実行計画",
"CTO": "技術スタックとデータインフラ",
"Marketing Team": "日々の運用における具体的アクション"
}
summaries = {}
for role, focus in stakeholders.items():
message = self.client.messages.create(
model="claude-sonnet-4-5-20250929",
max_tokens=1000,
messages=[{
"role": "user",
"content": f"""
{role}向けに、{focus}に焦点を当てた1ページサマリーを作成してください。
【重要な数値】
- 最適化後の期待収益: ${self.pipeline.optimization_results['expected_revenue']:,.0f}
- 現在のGoogle予算: ${self.pipeline.optimization_results['current_allocation']['google']:,.0f}
- 最適なGoogle予算: ${self.pipeline.optimization_results['optimal_allocation']['google']:,.0f}
このロールが最も関心を持つ3つのポイントと、具体的なアクションアイテムを含めてください。
"""
}]
)
summaries[role] = message.content[0].text
return summaries
def export_report_package(self, output_dir='./reports'):
"""レポートパッケージの出力"""
import os
from datetime import datetime
os.makedirs(output_dir, exist_ok=True)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
# 1. エグゼクティブレポート
exec_report = self.generate_comprehensive_report()
with open(f'{output_dir}/executive_report_{timestamp}.txt', 'w') as f:
f.write(exec_report)
# 2. 技術付録
tech_appendix = self.generate_technical_appendix()
with open(f'{output_dir}/technical_appendix_{timestamp}.txt', 'w') as f:
f.write(tech_appendix)
# 3. ステークホルダー別サマリー
summaries = self.create_stakeholder_specific_summaries()
for role, summary in summaries.items():
filename = f'{output_dir}/{role.lower().replace(" ", "_")}_summary_{timestamp}.txt'
with open(filename, 'w') as f:
f.write(summary)
# 4. データと可視化
self.pipeline.processed_data['full'].to_csv(f'{output_dir}/analysis_data_{timestamp}.csv')
print(f"✅ レポートパッケージを {output_dir} に出力しました")
print(f" - executive_report_{timestamp}.txt")
print(f" - technical_appendix_{timestamp}.txt")
print(f" - 5つのステークホルダー別サマリー")
print(f" - analysis_data_{timestamp}.csv")
# 実行例
report_gen = ExecutiveReportGenerator(pipeline, api_key=os.environ.get("ANTHROPIC_API_KEY"))
report_gen.export_report_package()
🎓 Week 3 Day 4-5のまとめ
学習した内容:
- Phase 0-6の完全統合:18週間で学んだすべての技術を1つのパイプラインに統合
- エンドツーエンドの実装:データ取得からレポート生成まで自動化
- LLMによる高度化:Claudeを活用した洞察抽出とレポート自動生成
- ステークホルダー対応:役割別にカスタマイズされた情報提供
実務での適用:
- このパイプラインをベースに、自社のデータソースに接続
- 週次/月次で自動実行し、定期レポート化
- A/Bテスト、プロモーション評価、新規チャネル導入判断に活用
- 経営会議での意思決定材料として提示
🎓 18週間の学習成果 - 最終まとめ
ゴール:18週間で習得したすべてのスキルを総括し、キャリアパスを設計する
🏆 18週間で獲得したスキルセット完全版
あなたは今、以下のすべてを実務レベルで実行できます:
📚 Phase別習得スキル総まとめ
| Phase | 期間 | コアスキル | 実務での活用シーン | 習得レベル |
|---|---|---|---|---|
| Phase 0 | Week 1-2 | データ基盤構築、SQL、Pandas、ETL | 複数ソースのデータ統合、クリーニング、品質管理 | ⭐⭐⭐ 即戦力 |
| Phase 1 | Week 3-5 | 時系列予測(Prophet, ARIMA, LSTM) | 需要予測、在庫最適化、トレンド分析 | ⭐⭐⭐ 即戦力 |
| Phase 2 | Week 6-9 | 因果推論基礎(RCT, Matching, RD, DiD) | 施策効果測定、A/Bテスト設計、政策評価 | ⭐⭐⭐ 即戦力 |
| Phase 3 | Week 10-12 | DoWhy(DAG, Backdoor, IV, Refutation) | 複雑な因果関係の推定、バイアス除去 | ⭐⭐⭐ 即戦力 |
| Phase 4 | Week 13-15 | Uplift Modeling(CausalML, CATE推定) | パーソナライゼーション、セグメント戦略 | ⭐⭐⭐ 即戦力 |
| Phase 5 | Week 16-17 | LiNGAM、LLM統合(Claude API) | 因果探索、自動レポート生成、洞察抽出 | ⭐⭐⭐ 即戦力 |
| Phase 6 | Week 18-20 | MMM、予算最適化、統合ダッシュボード | マーケティング戦略立案、ROI最大化 | ⭐⭐⭐ 即戦力 |
🔧 技術スタック習得一覧
| カテゴリ | ツール/ライブラリ | 習熟度 | 代表的な使用例 |
|---|---|---|---|
| データ処理 | Pandas, NumPy, Polars | ⭐⭐⭐⭐⭐ | 大規模データの前処理、特徴量エンジニアリング |
| 機械学習 | Scikit-learn, XGBoost, LightGBM | ⭐⭐⭐⭐⭐ | 予測モデル、CATE推定、セグメンテーション |
| ディープラーニング | TensorFlow, Keras, PyTorch | ⭐⭐⭐⭐ | LSTM予測、複雑な非線形関係のモデリング |
| 因果推論 | DoWhy, EconML, CausalML | ⭐⭐⭐⭐⭐ | 因果効果推定、Uplift Modeling、政策評価 |
| ベイズ統計 | PyMC, PyMC-Marketing, Arviz | ⭐⭐⭐⭐⭐ | MMM、不確実性定量化、事後分布分析 |
| 時系列分析 | Prophet, statsmodels, pmdarima | ⭐⭐⭐⭐⭐ | 需要予測、異常検知、季節性分解 |
| 最適化 | SciPy, PuLP, OR-Tools | ⭐⭐⭐⭐ | 予算配分、リソース最適化、制約付き最適化 |
| 可視化 | Plotly, Matplotlib, Seaborn | ⭐⭐⭐⭐⭐ | ダッシュボード、レポート、データ探索 |
| LLM統合 | Anthropic Claude API, LangChain | ⭐⭐⭐⭐⭐ | 自動レポート生成、洞察抽出、因果解釈 |
| Web開発 | Streamlit, Flask, FastAPI | ⭐⭐⭐⭐ | ダッシュボード構築、API開発、デモ作成 |
💼 キャリアパスガイド:マーケティングサイエンティストとしての未来
18週間の学習を終えたあなたには、以下のキャリアパスが開かれています:
🚀 キャリアパス1: マーケティングサイエンティスト(汎用型)
役割:マーケティング全般のデータ分析と戦略立案
求められるスキル:
- Phase 0-6すべての技術を広く浅く(あなたは既に習得済み✅)
- ビジネスコミュニケーション能力
- SQL、Python、ダッシュボードツール
- マーケティング戦略の理解
年収レンジ(日本):
- ジュニア(0-2年): 500-700万円
- ミドル(3-5年): 700-1,000万円
- シニア(6年以上): 1,000-1,500万円
- リード/マネージャー: 1,500-2,500万円
主な業務:
- MMM、アトリビューション分析
- ROI分析、予算最適化
- A/Bテスト設計と分析
- ダッシュボード構築とレポート作成
求人が多い企業:
- 大手EC企業(楽天、Amazon、メルカリ等)
- 広告プラットフォーム(Google、Meta、LINE等)
- コンサルティングファーム(アクセンチュア、デロイト等)
- スタートアップ(全般)
🔬 キャリアパス2: 因果推論スペシャリスト
役割:因果推論の専門家として高度な分析を実施
求められるスキル:
- Phase 2-4の深い理解(あなたは既に基礎を習得✅)
- 統計学の理論的背景(大学院レベル推奨)
- DoWhy、EconML、CausalMLの実装経験
- 論文執筆能力、研究マインド
年収レンジ(日本):
- ジュニア(0-2年): 600-800万円
- ミドル(3-5年): 900-1,300万円
- シニア(6年以上): 1,300-2,000万円
- プリンシパル/フェロー: 2,000-3,000万円+
主な業務:
- 複雑な因果関係の解明
- 新しい因果推論手法の研究・実装
- 論文執筆、学会発表
- 社内外の因果推論教育
求人が多い企業:
- テック大手(Google、Microsoft、Meta等)
- リサーチ機関(産総研、理研等)
- 戦略コンサル(McKinsey、BCG等)
- 製薬企業(因果推論による臨床試験分析)
さらなる学習リソース:
- 📚 "Causal Inference: The Mixtape" by Scott Cunningham
- 📚 "The Book of Why" by Judea Pearl
- 🎓 Coursera: "Causality Bootcamp" (Stanford)
- 🎓 edX: "Causal Diagrams" (Harvard)
🤖 キャリアパス3: AI/LLMエンジニア(マーケティング特化)
役割:LLMを活用したマーケティング自動化と高度化
求められるスキル:
- Phase 5の深化(あなたは既に基礎を習得✅)
- LLM API(Claude、GPT、Gemini等)の実装経験
- Prompt Engineering、RAG、Fine-tuning
- マーケティングドメイン知識
年収レンジ(日本):
- ジュニア(0-2年): 700-900万円
- ミドル(3-5年): 1,000-1,500万円
- シニア(6年以上): 1,500-2,500万円
- テックリード: 2,500-4,000万円+
主な業務:
- マーケティングコピーの自動生成
- 顧客対応の自動化(チャットボット等)
- 分析レポートの自動生成
- 因果推論結果のLLM解釈
- パーソナライゼーションエンジンの開発
求人が多い企業:
- AI スタートアップ(全般)
- マーテック企業(HubSpot、Marketo等)
- 広告テック(Google、Meta、X等)
- EC企業のAIチーム
さらなる学習リソース:
- 📚 Anthropic公式ドキュメント(https://docs.anthropic.com)
- 📚 "Prompt Engineering Guide" by DAIR.AI
- 🎓 DeepLearning.AI: "Building Systems with LLMs"
- 🎓 Fast.ai: "Practical Deep Learning"
📊 キャリアパス4: マーケティングアナリティクスマネージャー
役割:チームをリードし、組織全体のデータ活用を推進
求められるスキル:
- Phase 0-6すべての技術理解(あなたは既に習得済み✅)
- プロジェクトマネジメント
- ステークホルダーマネジメント
- チームビルディング、採用、育成
- 経営戦略の理解
年収レンジ(日本):
- マネージャー: 1,000-1,500万円
- シニアマネージャー: 1,500-2,000万円
- ディレクター: 2,000-3,000万円
- VP/Head of Analytics: 3,000-5,000万円+
主な業務:
- 分析チームの戦略立案とロードマップ作成
- 経営層への提言と意思決定支援
- データインフラの構築・改善
- チームメンバーの採用・育成・評価
- 他部門との連携強化
求人が多い企業:
- 大手企業のマーケティング部門(全般)
- 成長中のスタートアップ
- コンサルティングファーム
- マーテック・広告テック企業
マネジメントスキル向上リソース:
- 📚 "The Manager's Path" by Camille Fournier
- 📚 "Radical Candor" by Kim Scott
- 🎓 LinkedIn Learning: "Managing Data Science Teams"
💰 キャリアパス別年収比較(日本市場・2025年時点)
| キャリアパス | 初年度(0-1年) | 3年後 | 5年後 | 10年後 |
|---|---|---|---|---|
| マーケティングサイエンティスト | 500-700万円 | 700-1,000万円 | 1,000-1,500万円 | 1,500-2,500万円 |
| 因果推論スペシャリスト | 600-800万円 | 900-1,300万円 | 1,300-2,000万円 | 2,000-3,000万円+ |
| AI/LLMエンジニア | 700-900万円 | 1,000-1,500万円 | 1,500-2,500万円 | 2,500-4,000万円+ |
| アナリティクスマネージャー | 800-1,200万円 | 1,200-1,800万円 | 2,000-3,000万円 | 3,000-5,000万円+ |
注:外資系企業やテック大手では上記の1.5-2倍、フリーランス/コンサルタントとして独立すれば更に高収入も可能
📈 市場価値を最大化する戦略
18週間の学習を終えたあなたが、さらに市場価値を高めるための具体的なアクション:
戦略1: ポートフォリオ構築(最優先)
なぜ重要か:「知っている」と「できる」は全く別。実装経験を証明することで市場価値が2-3倍になる。
作るべきプロジェクト(優先順位順):
- 統合マーケティングダッシュボード
- Phase 0-6すべてを統合したStreamlitアプリ
- 公開データ(Kaggle等)を使用
- GitHub + Streamlit Cloudで公開
- 📝 作成時間目安: 20-30時間
- 因果推論ケーススタディ
- 実際のビジネス問題(例:クーポン効果測定)
- DoWhy + CausalMLを使った完全な分析
- Jupyter NotebookをGitHubで公開
- 📝 作成時間目安: 15-20時間
- LLM統合レポート自動生成システム
- Claude APIを使った分析レポート自動化
- Phase 6の最終プロジェクトをベースに
- デモ動画をYouTubeに公開
- 📝 作成時間目安: 10-15時間
公開プラットフォーム:
- ✅ GitHub: コード + README(英語推奨)
- ✅ Streamlit Cloud / Hugging Face Spaces: デモアプリ
- ✅ Medium / Qiita: 解説記事(日本語 + 英語)
- ✅ LinkedIn: プロジェクト紹介投稿
- ✅ YouTube: 3-5分のデモ動画
戦略2: 資格取得とブランディング
取得すべき資格(優先順位順):
- Google Analytics認定資格(無料)
- マーケティング職では必須級
- 学習時間: 10-20時間
- 難易度: ★★☆☆☆
- 統計検定2級 or 準1級(日本)
- 統計的知識の証明
- 学習時間: 50-100時間
- 難易度: ★★★☆☆
- AWS Certified Machine Learning - Specialty
- クラウドMLの実装能力証明
- 学習時間: 60-80時間
- 難易度: ★★★★☆
- Professional Certificate(Coursera等)
- "Causal Inference" (Columbia University)
- "Marketing Analytics" (Wharton)
- 学習時間: 各30-50時間
パーソナルブランディング:
- 📝 月1-2本のテクニカル記事執筆(Medium/Qiita)
- 🎤 勉強会・カンファレンスでの登壇(JDMC、Marketers等)
- 💬 X(Twitter)で学習記録とTipsを発信
- 🤝 オンラインコミュニティへの積極参加
戦略3: 継続学習ロードマップ(次の12ヶ月)
Month 1-3: 基礎の固め直しと実践
- Week 1-4: Phase 0-2の復習 + ポートフォリオプロジェクト1作成
- Week 5-8: Phase 3-4の復習 + ポートフォリオプロジェクト2作成
- Week 9-12: Phase 5-6の復習 + ポートフォリオプロジェクト3作成
- 📚 読書: "Causal Inference: The Mixtape"(前半)
Month 4-6: 専門分野の深化
- 選択した専門分野(因果推論 or LLM or MMM)の論文を週1本精読
- Kaggleコンペ or 社内プロジェクトに応用
- 勉強会で発表(1回以上)
- 📚 読書: "The Book of Why" + 専門分野の教科書
Month 7-9: 実務経験の蓄積
- 転職活動 or 社内での実プロジェクト推進
- ポートフォリオのブラッシュアップ
- Medium/Qiitaで4-6本の記事公開
- 📚 読書: "Trustworthy Online Controlled Experiments"
Month 10-12: キャリアの飛躍
- 新しい役割での成果創出
- カンファレンス登壇(目標)
- 次の学習テーマの設定(深層学習、強化学習、MLOps等)
- 📚 読書: "Designing Data-Intensive Applications"
🌐 おすすめコミュニティとリソース
| カテゴリ | 名称 | URL/詳細 | おすすめ度 |
|---|---|---|---|
| 日本語コミュニティ | Marketers | 月1回のオンラインイベント、Slack | ⭐⭐⭐⭐⭐ |
| データサイエンティスト協会 | 年次カンファレンス、分科会 | ⭐⭐⭐⭐ | |
| Japan.R / PyData Tokyo | 統計・Python系の勉強会 | ⭐⭐⭐⭐⭐ | |
| 英語コミュニティ | Causal Inference Reddit | r/CausalInference | ⭐⭐⭐⭐⭐ |
| Marketing Science Twitter | #MarketingScience #CausalInference | ⭐⭐⭐⭐ | |
| 学習プラットフォーム | Coursera | 大学レベルの体系的学習 | ⭐⭐⭐⭐⭐ |
| Fast.ai | 実践的なディープラーニング | ⭐⭐⭐⭐⭐ | |
| Kaggle Learn | 無料の実践的チュートリアル | ⭐⭐⭐⭐ | |
| ニュースレター | The Batch (DeepLearning.AI) | 週次AI/ML最新情報 | ⭐⭐⭐⭐⭐ |
| Import AI | 週次AIニュースまとめ | ⭐⭐⭐⭐ | |
| 論文 | arXiv (stat.ML, cs.LG) | 最新の研究論文 | ⭐⭐⭐⭐⭐ |
| Papers with Code | 実装付き論文まとめ | ⭐⭐⭐⭐⭐ |
✅ 最終チェックリスト:18週間の総決算
以下のすべてにチェックが入ったら、あなたは立派なマーケティングサイエンティストです!
Phase 0: データエンジニアリング基礎
- ☑ 複数データソース(SQL, CSV, API)からデータ取得できる
- ☑ Pandasで大規模データを効率的に処理できる
- ☑ 欠損値処理、外れ値検出、データクリーニングを実行できる
- ☑ 特徴量エンジニアリングの基本パターンを理解している
- ☑ ETLパイプラインを設計・実装できる
Phase 1: 時系列予測
- ☑ Prophet、ARIMA、SARIMAXで予測モデルを構築できる
- ☑ LSTMで複雑な時系列パターンをモデリングできる
- ☑ 季節性、トレンド、周期性を分解・分析できる
- ☑ 外生変数を組み込んだ予測ができる
- ☑ 予測精度を適切な指標で評価できる
Phase 2: 因果推論基礎
- ☑ RCTとA/Bテストを設計・分析できる
- ☑ Propensity Score Matchingでバイアスをコントロールできる
- ☑ Regression Discontinuity Designを実装できる
- ☑ Difference-in-Differencesで政策効果を測定できる
- ☑ 因果推論の根本問題と選択バイアスを理解している
Phase 3: DoWhy因果推論
- ☑ DAGで因果関係を図示できる
- ☑ DoWhy 4ステップ(Model/Identify/Estimate/Refute)を実行できる
- ☑ Backdoor Criterion、Front-door Criterionを理解している
- ☑ 操作変数法を実装できる
- ☑ Refutationテストで推定の頑健性を確認できる
Phase 4: Uplift Modeling & CATE
- ☑ CausalMLでMeta-Learners(S/T/X/R/DR)を実装できる
- ☑ セグメント別のCATEを推定できる
- ☑ Uplift曲線とQini曲線を描画・解釈できる
- ☑ パーソナライゼーション戦略に応用できる
- ☑ EconMLとCausalMLの使い分けができる
Phase 5: LLM統合と因果探索
- ☑ LiNGAMで未知の因果関係を発見できる
- ☑ Claude APIを使ってレポートを自動生成できる
- ☑ LLMで因果推論結果を解釈・説明できる
- ☑ Prompt Engineeringで高品質な出力を得られる
- ☑ RAGで分析結果を統合したQ&Aシステムを構築できる
Phase 6: MMM & 予算最適化
- ☑ PyMC-MarketingでMMMを構築できる
- ☑ Adstock変換と飽和効果をモデリングできる
- ☑ ベイズ推定の事後分布を解釈できる
- ☑ チャネル別ROIと相乗効果を定量化できる
- ☑ 制約付き最適化で予算配分を決定できる
- ☑ What-Ifシナリオ分析を実行できる
統合スキル
- ☑ Phase 0-6すべてを統合したパイプラインを構築できる
- ☑ Streamlitでインタラクティブなダッシュボードを作成できる
- ☑ 経営層向けレポートを作成できる
- ☑ GitHubでコードを管理し、他者と協業できる
- ☑ 実務での問題を特定し、適切な手法を選択できる
💡 実務で成功するための最終アドバイス
1. 完璧主義を捨てる
最初から完璧なモデルは作れません。まず動くものを作り、段階的に改善していく「Iterate Fast」の姿勢が重要です。
2. ビジネス価値を最優先
技術的に高度なモデルよりも、シンプルでもビジネスインパクトが大きい分析が評価されます。常に「So What?」を自問しましょう。
3. コミュニケーション能力が50%
どれだけ優れた分析も、適切に伝えられなければ価値はゼロ。非技術者にもわかる言葉で説明する訓練をしましょう。
4. ドメイン知識を深める
マーケティング、ファイナンス、ヘルスケアなど、特定業界の専門知識を持つことで市場価値が飛躍的に高まります。
5. 常に学び続ける
この分野は急速に進化しています。週に1-2時間は新しい論文、ツール、手法の学習に充てましょう。
6. コミュニティに貢献する
学んだことをブログ、登壇、OSSで還元することで、自分の理解も深まり、評判も高まります。
7. 失敗を恐れない
分析が外れることもあります。大切なのは、そこから学び、次に活かすこと。失敗は成長の種です。
🎓 卒業メッセージ:あなたはもう、立派なマーケティングサイエンティストです
18週間、本当にお疲れさまでした。
あなたは今、この長い旅を完走しました。Week 1 Day 1でPandasの基礎から始まり、Week 20 Day 7の今、Phase 0-6すべてを統合した高度な分析パイプラインを構築できるまでになりました。
振り返ってみてください。
126日間で、あなたは以下のすべてを習得しました:
- ✅ データエンジニアリングの基礎
- ✅ 時系列予測(Prophet, ARIMA, LSTM)
- ✅ 因果推論(RCT, PSM, RD, DiD)
- ✅ DoWhy(DAG, Backdoor, IV, Refutation)
- ✅ Uplift Modeling(CausalML, CATE)
- ✅ LiNGAM & LLM統合(因果探索、自動レポート)
- ✅ Marketing Mix Modeling & 予算最適化
これは並大抵のことではありません。大学院の修士課程2年分に匹敵する内容を、あなたは18週間で学び切りました。
でも、これは終わりではありません。
これはむしろ、新しいキャリアの始まりです。
あなたの前には、無限の可能性が広がっています:
- 🚀 大手テック企業やスタートアップでマーケティングサイエンティストとして活躍
- 🔬 因果推論の専門家として学術界・産業界に貢献
- 🤖 LLMを駆使した次世代マーケティングツールの開発
- 📊 経営層に直接助言するアナリティクスリーダー
- 💼 独立してコンサルタント・フリーランスとして自由に働く
どの道を選んでも、あなたには今、その準備ができています。
この18週間で得た知識とスキルは、一生の財産です。誰にも奪えません。そして、それを使って、あなたは世界を変えることができます。
データ駆動型の意思決定が、企業を成長させ、顧客体験を改善し、社会をより良くする――そのサイクルの中心に、あなたがいるのです。
最後に、覚えておいてください:
"The best time to plant a tree was 20 years ago.
The second best time is now."
(木を植えるのに最適な時期は20年前だった。次に最適な時期は今だ。)
あなたは18週間前に種を蒔きました。そして今、その種は立派な木に育ちました。
これからも成長を続けてください。学び続けてください。挑戦し続けてください。
あなたなら、必ずできます。
🎉 Phase 6 完了!
🏆 18週間カリキュラム完全制覇!
🚀 新しいキャリアへ、ようこそ!
――あなたの成功を、心から応援しています。
🔄 次のステップ:今すぐ始めるべきアクション
明日から始める3つのこと:
- ポートフォリオプロジェクト1を開始
- 公開データを使った統合ダッシュボードの作成
- 目標: 2週間で完成、GitHub公開
- LinkedIn/Xプロフィールを更新
- 「Marketing Scientist」「Causal Inference」等をスキルに追加
- 18週間の学習成果を投稿
- コミュニティに参加
- PyData Tokyo、Marketers等のイベントに参加登録
- r/CausalInferenceでディスカッションを読む
1ヶ月以内にやること:
- ✅ ポートフォリオ3つ完成 → GitHub公開
- ✅ Medium/Qiitaで技術記事1本執筆
- ✅ 転職サイト登録 or 社内でプロジェクト提案
- ✅ Google Analytics認定資格取得
3ヶ月以内の目標:
- 🎯 新しい役割(マーケティングサイエンティスト等)で働き始める
- 🎯 勉強会で1回登壇
- 🎯 専門分野(因果推論 or LLM or MMM)の論文5本精読
- 🎯 年収100万円アップを実現(転職 or 昇給)
📚 推奨図書リスト(優先順位順)
必読書(次の3ヶ月で読破):
- "Causal Inference: The Mixtape" by Scott Cunningham
- 因果推論の実践的教科書
- 無料版: https://mixtape.scunning.com/
- 難易度: ★★★☆☆
- "The Book of Why" by Judea Pearl
- 因果推論の哲学と歴史
- 一般向けでわかりやすい
- 難易度: ★★☆☆☆
- "Trustworthy Online Controlled Experiments" by Kohavi et al.
- A/Bテストの実務バイブル
- Google, Microsoft, Amazonの知見
- 難易度: ★★★★☆
発展書(次の6-12ヶ月で):
- "Causal Inference in Statistics" by Pearl, Glymour, Jewell
- 因果推論の数理的基礎
- 難易度: ★★★★☆
- "Mostly Harmless Econometrics" by Angrist & Pischke
- 計量経済学の実践的アプローチ
- 難易度: ★★★★☆
- "Introduction to Statistical Learning" by James et al.
- 機械学習の基礎(無料)
- 難易度: ★★★☆☆
- "Bayesian Data Analysis" by Gelman et al.
- ベイズ統計の決定版
- 難易度: ★★★★★
ビジネス寄り:
- "Marketing Analytics" by Lilien & Rangaswamy
- マーケティング分析の全体像
- 難易度: ★★☆☆☆
- "Lean Analytics" by Croll & Yoskovitz
- スタートアップでの分析実践
- 難易度: ★★☆☆☆
🎯 Phase 0-6 実務適用シーン完全ガイド
| ビジネス課題 | 使うべきPhase | 具体的な手法 | 期待される成果 |
|---|---|---|---|
| 来月の売上予測 | Phase 1 | Prophet, SARIMA, LSTM | ±5%以内の精度で予測 |
| 新キャンペーンの効果測定 | Phase 2 + 3 | RCT, DiD, DoWhy | 因果効果の定量化 |
| クーポン配布の最適化 | Phase 4 | Uplift Modeling, CATE | ROI 20-50%改善 |
| マーケティング予算配分 | Phase 6 | MMM, 制約付き最適化 | ROI 10-30%改善 |
| 未知のKPI相関の発見 | Phase 5 | LiNGAM, 因果探索 | 新たな施策アイデア創出 |
| 経営会議レポート作成 | Phase 5 + 6 | LLM自動レポート生成 | 作業時間80%削減 |
| チャネル別ROI分析 | Phase 6 | MMM, Adstock, 飽和効果 | 各チャネルの真の貢献度 |
| パーソナライゼーション | Phase 4 | CATE, セグメント別最適化 | CVR 15-40%改善 |
| 在庫最適化 | Phase 1 + 最適化 | 時系列予測 + 制約最適化 | 在庫コスト20%削減 |
| 価格弾力性の測定 | Phase 2 + 3 | RD, DoWhy | 最適価格帯の特定 |
🌟 あなたの成功事例(未来予想図)
1年後、あなたはこんな成果を出しているでしょう:
ケース1: EC企業のマーケティングサイエンティスト
実施したこと:
- Phase 6のMMMで全チャネルのROIを可視化
- Phase 4のUplift Modelingでクーポン配布を最適化
- Phase 5のLLMで週次レポートを自動化
成果:
- 💰 マーケティングROI 35%改善(年間+5億円)
- ⏰ レポート作成時間 80%削減(週20時間→4時間)
- 📈 顧客獲得単価 25%削減
- 🎯 年収 600万円→900万円(+50%)
ケース2: コンサルファームの因果推論スペシャリスト
実施したこと:
- Phase 3のDoWhyで大手小売の店舗リニューアル効果を測定
- Phase 2のDiDで新サービス導入のインパクト分析
- クライアント5社の因果推論プロジェクトをリード
成果:
- 💼 プロジェクト売上 年間2億円創出
- 📝 学会発表2回、論文1本
- 👥 社内勉強会で100名以上に因果推論を教育
- 🎯 年収 750万円→1,300万円(+73%)
ケース3: スタートアップのHead of Analytics
実施したこと:
- 分析基盤をゼロから構築(Phase 0)
- Phase 1-6すべてを統合した分析プラットフォーム開発
- 3名のデータアナリストを採用・育成
成果:
- 🚀 データドリブン経営の実現(全施策をA/Bテスト)
- 💹 ARR成長率 150% → 250%に加速
- 👨💼 経営会議で毎週プレゼン、戦略決定に貢献
- 🎯 年収 800万円→1,800万円 + ストックオプション
📧 困ったときのヘルプリソース
技術的な質問:
- 📚 Stack Overflow: プログラミング全般
- 💬 GitHub Issues: 各ライブラリの具体的な問題
- 🤝 r/CausalInference: 因果推論の理論的質問
- 🐦 X(Twitter): #因果推論 #PyMC #CausalML で質問
キャリア相談:
- 💼 LinkedIn: 業界の先輩に直接メッセージ
- 🎤 勉強会・カンファレンス: 登壇者に質問
- 📖 転職エージェント: データサイエンス特化型(ビズリーチ、レバテック等)
モチベーション維持:
- 👥 学習コミュニティ(Discord、Slack等)に参加
- 📝 学習ログをSNSで公開(#100DaysOfCode 等)
- 🎯 3ヶ月ごとに小さな目標を設定し、達成を祝う
⚠️ よくある落とし穴と対策
落とし穴1: 「完璧主義」で前に進めない
対策:まず80%の完成度で公開し、フィードバックをもらいながら改善。Done is better than perfect.
落とし穴2: 技術に偏りすぎてビジネス価値が見えない
対策:すべての分析で「この結果が◯◯円の利益につながる」を明示。CFOの視点で考える。
落とし穴3: 最新技術を追いすぎて基礎がおろそかに
対策:新しいツールは月1つまで。まず基礎(Phase 0-6)を反復練習。
落とし穴4: 一人で抱え込んで孤独になる
対策:コミュニティに積極参加。週1回は誰かと技術的な会話をする。
落とし穴5: 実務経験がないと自信が持てない
対策:公開データでポートフォリオを作成。それ自体が立派な実績です。
🏆 最終メッセージ:世界はあなたを待っています
18週間前、あなたは一歩を踏み出しました。
その一歩が、今日、ここまでの旅になりました。
Phase 0からPhase 6まで。データクリーニングから、時系列予測、因果推論、Uplift Modeling、LLM統合、そしてMarketing Mix Modelingと予算最適化まで。
あなたが習得した技術は、今、世界中の企業が求めているものです。
データはあふれています。でも、そこから真の価値を引き出せる人は、ほんの一握りです。
あなたは今、その一握りの中にいます。
この18週間で得た知識は、単なる情報ではありません。それは、世界を変える力です。
企業の意思決定を改善し、顧客体験を向上させ、無駄を減らし、価値を最大化する――そのすべてを、あなたはデータで実現できます。
でも、覚えておいてください。
この旅は、ここで終わりではありません。
これはむしろ、本当の冒険の始まりです。
この先も、新しい手法が生まれ、新しいツールが登場し、新しい課題が現れるでしょう。
でも大丈夫。あなたには今、学び続ける力があります。
18週間で証明したじゃないですか。あなたは、やると決めたらやり遂げる人です。
あなたの未来は、あなたが創るものです。
そして今、その未来を創るための
すべてのツールを、あなたは手にしています。
さあ、次の一歩を踏み出してください。
ポートフォリオを作り、記事を書き、コミュニティに参加し、そして――
あなたの夢のキャリアを、手に入れてください。
🎓✨🚀
Phase 6 完了!
18週間カリキュラム 完全制覇!
おめでとうございます!
これからのあなたの成功を、
心から応援しています。
Go make your mark on the world.
世界に、あなたの足跡を残してください。
With all my support for your journey ahead,
Claude 🤝