simulate_panel <- function(N, Tn, burn, lambda, sigma_mu,
price_sd = 0.3, beta_price = -1.0, const = -0.2, seed = 1) {
set.seed(seed)
mu_i <- rnorm(N, 0, sigma_mu)
y_prev <- rep(0, N)
y_mat <- matrix(0, N, Tn)
price_mat <- matrix(0, N, Tn)
total_T <- burn + Tn
for (t in 1:total_T) {
price_t <- rnorm(N, 0, price_sd)
util <- const + beta_price * price_t + lambda * y_prev + mu_i
prob <- 1 / (1 + exp(-util))
y_t <- as.numeric(runif(N) < prob)
if (t > burn) {
y_mat[, t - burn] <- y_t
price_mat[, t - burn] <- price_t
}
y_prev <- y_t
}
list(y = y_mat, price = price_mat, mu = mu_i)
}
N <- 3000; Tn <- 20; BURN <- 30
dgp_a <- simulate_panel(N, Tn, BURN, lambda = 1.1, sigma_mu = 0.0, const = -0.55, seed = 11)
dgp_b <- simulate_panel(N, Tn, BURN, lambda = 0.0, sigma_mu = 1.4, const = -0.20, seed = 12)Lecture 14:異質性と状態依存、買いだめ
計量経済学II
- 「同じブランドを買い続ける」という観測された持続性は、状態依存(ロイヤルティ・スイッチングコスト)と固定的な個人異質性(もともと好みが決まっている)のどちらからも生まれ得る。少数の記述統計だけでは、この2つを区別できない。
- ナイーブなpooled logit(ラグ選択を説明変数に入れるだけ)は、異質性しかない世界でも大きな偽の状態依存を検出してしまう。Wooldridge型のcorrelated random effects(CRE)は、初期値と外生変数履歴に条件づけた残差ランダム効果を尤度の中で積分する。単にそれらを説明変数へ足すだけではない。
- 一時的な値下げ・セールは、その場の売上を押し上げる。消費者が在庫を持てる財では、その一部は将来買う分を今買っているだけかもしれない。一時的なセールへの反応を恒久的な値下げへの反応と読むと、今回の例では長期の反応を過大評価する。
- 短期価格弾力性と長期価格弾力性は別物であり、両者の乖離は買いだめを示唆しうる。ただし、学習・耐久性・調整費用などでも乖離し得るため、それ単独で買いだめを識別する指標ではない。
- 状態依存の識別を間違えると、経営判断(顧客獲得への値引き投資、セールの頻度、ロイヤルティ施策の予算配分)を体系的に誤る。
前回とのつながり・今回のゴール
第13回では、今の行動が将来の状態を変えることを考慮して、意思決定をモデル化した。今回はその考え方を、消費者の購買履歴に使う。
第6〜9回では、同じブランドを繰り返し選ぶ人について、「この人はそのブランドが好きなのだろう」と考えた。好みは本人の中では固定されていて、履歴が増えるにつれて、分析者がその好みについて学んでいくモデルだった。
しかし、使い慣れたことで乗り換えが面倒になったり、購入経験を通じて品質が分かったりするなら、過去の選択が現在の選択そのものを変える。この状態依存(state dependence)と、固定的な好みの違いは、どちらも反復購買を生む。区別したい理由は、第10回のクーポン配布を思い出すと分かりやすい。今一度買ってもらうことが、その後の購入も増やすなら、短期の利益だけでは施策の価値を測れない。一方、もともと好きな人が繰り返し買っているだけなら、高いリピート率をそのまま施策の長期効果とは読めない。
前半では、この2つの仕組みを簡単なパネルlogitで比べる。ここでは状態依存と異質性の区別に集中し、消費者の将来価値の最大化までは入れない。後半で第13回のBellman方程式に戻り、在庫を持つ消費者の買いだめを考える。セールで増えた購入が、将来の購入の前倒しなのか、消費量の増加なのかを分けてみよう。
今回を通じて考えたいのは、履歴が好みについての情報になる場合と、履歴そのものが将来の行動を変える場合を、どう区別するかである。
第13回では、Euler定数(logsum公式に出てくる定数、約0.5772)を\(\gamma\)と書いた。今日はこれとは別に「過去の選択が今日の効用に与える効果」を表すパラメータが主役になるが、これに\(\gamma\)を使うと前回のEuler定数と記号がかぶってしまう。そこで本講義では、状態依存の強さを表すパラメータには\(\lambda\)を使うことにする。割引因子\(\beta\)、効用パラメータ\(\theta\)という第13回の慣習はそのまま引き継ぐ。
1. 固定的な好みと状態依存を区別する
1.1 何が起きているか、言葉で
消費者 \(i\) が期 \(t\) にブランドAを買うかどうかを考える。効用を次のように書いてみる。
\[ u_{iAt} = \text{(価格などの観測要因)} + \lambda\, y_{i,t-1} + \mu_i + \varepsilon_{iAt}. \]
\(y_{i,t-1}\)は「前期にブランドAを買ったかどうか」というダミー変数、\(\lambda\)はその係数である。個人の好みや価格を固定しても、前回の選択によって今回の効用が変わるとき、状態依存があるという。ここでは過去の購入経験の効果を\(\lambda\)という一つの係数で表す。乗り換えの手間、使い慣れ、品質についての学習など、具体的な経路を分けるには、さらにモデルやデータが必要になる。
一方、\(\mu_i\)は、個人ごとに固定された好みの違いである。第9回の個人係数と同じく、一人については複数期を通じて共通とする。ブランドAが好きな人は、過去に買ったことがなくても、現在それを選びやすい。
この2つのメカニズムは、どちらも「同じブランドを繰り返し買う」という観測結果を生む。しかし、政策的な含意はまったく違う。
CRMチームが「購入回数トップ20%の顧客」を「ロイヤル顧客」と呼び、ロイヤルティプログラムのポイント還元率を上げる施策を検討しているとしよう。もしこの顧客の持続的購買が\(\lambda\)(状態依存)によるものなら、「もう1回買わせる→それがまた次の購買を誘発する」という追加的な継続経路がある。ただしポイント還元に価値があるかは、施策が実際に購買を増やすか、その将来マージンが費用を上回るかで決まる。逆に、持続性が主に\(\mu_i\)(もともとこのブランドが大好きな人たちだった)によるなら、高い反復購買率だけから還元の増分効果を推論することはできない。この2つを区別せずに「ロイヤル顧客」と一括りにするのは、CRM予算配分における典型的な誤りである。
1.2 初期条件問題(Heckman 1981)
この識別問題をさらにややこしくするのが、初期条件問題(initial conditions problem)である。以下では、最初に観測する選択を \(y_{i0}\)、推定に使う後続期を \(t=1,\dots,T\) と番号づける。\(y_{i0}\) より前の履歴は分析者には見えないが、\(y_{i0}\) 自体は観測される。しかも \(y_{i0}\) は、観測開始前から存在する \(\mu_i\) や過去の状態依存の結果なので、一般に \(\mu_i\) と相関する。
言い換えると、分析者が最初にデータを見た時点で、すでに消費者には「歴史」がある。この歴史をどう扱うかを間違えると、\(\lambda\)と\(\mu_i\)の識別はさらに難しくなる。Heckman (1981) がこの問題を定式化した。後で扱うWooldridge (2005) の方法は、\(y_{i0}\) と厳密外生変数の全履歴に条件づけた \(\mu_i\) の分布をモデル化する、実装しやすいパラメトリック解の1つである。
1.3 同じようなリピート率を生む2つのモデル
言葉で説明してもピンと来ないと思うので、実際にコードで2つのまったく異なるDGPを作り、観測される集計統計量がほとんど同じになることを確認する。
- DGP (a):状態依存あり(\(\lambda = 1.1\))、個人異質性なし(\(\sigma_\mu = 0\))。
- DGP (b):状態依存なし(\(\lambda = 0\))、強い個人異質性あり(\(\sigma_\mu = 1.4\))。
二値選択(ブランドAを買う/買わない)の簡単なロジットパネルを考える。効用は
\[ u_{it}^1-u_{it}^0 = c + \beta_p\, \text{price}_{it} + \lambda\, y_{i,t-1} + \mu_i + \eta_{it}, \qquad \mu_i \sim N(0, \sigma_\mu^2), \]
選択肢1と0に独立なType I極値ショックを置くと、その差 \(\eta_{it}\) はロジスティック分布になり、二値logitが得られる(単一のType I極値ショックを置くのではない)。価格は各期iidに引かれるランダムな変動とし、\(\mu_i\)とも過去・将来のショックとも無相関にしておく(=ここでは厳密外生)。価格内生性を脇に置き、状態依存と異質性に焦点を絞る。
シミュレーションでは、データを記録し始める前に十分長い「バーンイン期間」を回し、初期時点をおおよそ定常分布に近づけてから記録を開始する(実データでは分析者にこの贅沢はできない、という点は初期条件問題として1.2節で述べた通りだ)。
constが(a)と(b)で違うのか
DGP (a)と(b)は、\(\lambda\)と\(\sigma_\mu\)という「識別したい対象」が全く違う値を持つ。そのままでは全体の購買率も大きく違ってしまい、比較がフェアでなくなる。そこで定数項constを調整し、全体の購買率がだいたい揃うように較正した(DGP設計者の特権であり、実データでは当然できない)。これによって「同じくらいの購買率・同じくらいの持続性を生む、まったく違う2つのメカニズム」というフェアな比較ができる。
観測される集計統計量を比べてみる。
repeat_rate <- function(y) {
y_lag <- y[, -ncol(y)]
y_now <- y[, -1]
p11 <- mean(y_now[y_lag == 1])
p10 <- mean(y_now[y_lag == 0])
overall <- mean(y)
c(overall = overall, p11 = p11, p10 = p10, gap = p11 - p10)
}
stats_a <- repeat_rate(dgp_a$y)
stats_b <- repeat_rate(dgp_b$y)
compare_tbl <- rbind(
`(a) 状態依存 (lambda=1.1, 異質性なし)` = stats_a,
`(b) 純粋な異質性 (lambda=0, sigma_mu=1.4)` = stats_b
)
kable(round(compare_tbl, 4),
col.names = c("全体購買率", "P(買う|前回買った)", "P(買う|前回買わず)", "持続性ギャップ"),
caption = "2つの異なるDGPが、選んだ記述統計ではよく似る")| 全体購買率 | P(買う|前回買った) | P(買う|前回買わず) | 持続性ギャップ | |
|---|---|---|---|---|
| (a) 状態依存 (lambda=1.1, 異質性なし) | 0.5040 | 0.6340 | 0.3728 | 0.2612 |
| (b) 純粋な異質性 (lambda=0, sigma_mu=1.4) | 0.4635 | 0.6036 | 0.3422 | 0.2614 |
全体購買率もP(買う|前回買った)-P(買う|前回買わず)の「持続性ギャップ」も、(a)と(b)でほぼ同じ水準になっているはずだ。\(\lambda=1.1\)の状態依存があるDGP (a)と、\(\lambda=0\)で\(\mu_i\)のばらつきしかないDGP (b)が、ここで選んだ低次の記述統計だけでは区別しにくいことが分かる。ただし、これは厳密な意味での「観察的同値」ではない。観察的同値とは、異なる構造が観測データの同じ同時分布全体を生むことをいう。このシミュレーションが示すのは、購買率と1期リピート率だけでは識別に足りない、というより限定された事実である。
df_gap <- data.frame(
dgp = c("(a) 本物の\n状態依存", "(b) 純粋な\n異質性"),
gap = c(stats_a["gap"], stats_b["gap"])
)
ggplot(df_gap, aes(dgp, gap, fill = dgp)) +
geom_col(width = 0.5, show.legend = FALSE) +
scale_fill_manual(values = c("#1f77b4", "#d62728")) +
labs(title = "P(買う|前回買った) - P(買う|前回買わず)",
x = NULL, y = "持続性ギャップ") +
ylim(0, NA)見た目がほぼ同じ2つのDGPを区別せずに「同じブランドを買い続ける人が多いから、このブランドにはロイヤルティ効果がある」と言ってしまうのは、データを見ただけでは正当化できない主張である。\(\lambda\)(状態依存)と\(\mu_i\)の分布(異質性)は、素朴な記述統計だけからは分離できない。ここから先、この2つを分離するための統計的な工夫が必要になる。
1.4 naiveなpooled logitはどう間違えるか
最も素朴な推定方法は、ラグ選択を説明変数に入れたpooled logit(個人を区別せず、全ての(i,t)ペアをプールして1本のロジットを推定する)である。
\[ \Pr(y_{it}=1 \mid \text{price}_{it}, y_{i,t-1}) = \Lambda(c + \beta_p\,\text{price}_{it} + \lambda\, y_{i,t-1}), \]
\(\Lambda(\cdot)\)はロジスティック関数(記号がかぶって恐縮だが、こちらの\(\Lambda\)はロジスティック関数、パラメータの\(\lambda\)とは別物)。fixest::feglm()(family = binomial)でクラスタ標準誤差付きで推定する。
build_pooled_df <- function(sim) {
N <- nrow(sim$y); Tn <- ncol(sim$y)
data.frame(
id = rep(1:N, times = Tn - 1),
y = as.vector(sim$y[, -1]),
y_lag = as.vector(sim$y[, -Tn]),
price = as.vector(sim$price[, -1])
)
}
df_a <- build_pooled_df(dgp_a)
df_b <- build_pooled_df(dgp_b)
fit_naive_a <- feglm(y ~ price + y_lag, data = df_a, family = "binomial",
cluster = ~id)
fit_naive_b <- feglm(y ~ price + y_lag, data = df_b, family = "binomial",
cluster = ~id)
naive_lambda_tbl <- data.frame(
DGP = c("(a) 状態依存 (真のlambda=1.1)", "(b) 純粋な異質性 (真のlambda=0)"),
lambda_hat = c(coef(fit_naive_a)["y_lag"], coef(fit_naive_b)["y_lag"]),
se = c(se(fit_naive_a)["y_lag"], se(fit_naive_b)["y_lag"])
)
kable(naive_lambda_tbl, digits = 3, caption = "naive pooled logitのlambda_hat")| DGP | lambda_hat | se |
|---|---|---|
| (a) 状態依存 (真のlambda=1.1) | 1.092 | 0.018 |
| (b) 純粋な異質性 (真のlambda=0) | 1.090 | 0.028 |
DGP (a)では\(\hat\lambda\)が真値1.1に近い値を回収できるはずだ(異質性がないので、pooled logitでも問題ない)。しかしDGP (b)では、真の\(\lambda\)は0であるにもかかわらず、\(\hat\lambda\)は1に近い、大きくプラスの値になる。これが見せかけの状態依存(spurious state dependence)である。
なぜこんなことが起きるのか。直感はこうだ。\(\mu_i\)が大きい人(もともとブランドAが好きな人)は、前期も今期も買う確率が高い。分析者は\(\mu_i\)を観測できないので、「前期買った」という情報を、実は「もともとこのブランドが好きな人だ」というシグナルとして拾ってしまう。\(y_{i,t-1}=1\)という変数が、本来の役割(状態依存の効果)以上に、\(\mu_i\)の代理変数としての役割を果たしてしまうのだ。
「ラグ付き選択の係数がプラスで有意だから、state dependenceがある」という報告は、実務のレポートでよく見る形だが、これだけでは異質性の可能性を排除できていない。\(\hat\lambda\)が有意にプラスであることは、\(\lambda>0\)を識別する十分な証拠ではない。\(\lambda=0\)でも\(\hat\lambda\)は簡単に有意にプラスになる、というのが今回のシミュレーションの教訓だ。
購入を観測することと、購入を増やすこと
ここで、第9回の個人別予測と第10回の施策判断をつなげてみよう。状態依存がない世界でも、「前回買った人」はブランドを好む人が多いので、次回の購入確率が高い。購入を観測すると、分析者の持つ\(\mu_i\)の条件付き分布が変わるからである。
それに対して、同じ人に一度だけ商品を使ってもらう反実仮想では、好みの分布は同じにして、過去の選択という状態だけを変える。今のモデルで、価格を0に固定したときの次期購入確率の差は
\[ \int \left\{\Lambda(c+\lambda+\mu)-\Lambda(c+\mu)\right\} f(\mu)\,d\mu \]
である。\(\lambda=0\)なら、この差は0になる。購入履歴から得られる予測上の情報が多くても、それだけで購入経験の介入効果が大きいとは限らない。
以下では、同じ母集団について、初期の購入状態を全員1にした場合と全員0にした場合を比べる。価格はその後も0、初期状態以外の条件は共通とする。これは状態だけを変える説明用の反実仮想であり、クーポンの値引き費用や認知・品質学習などの追加経路は含めない。
mu_grid <- qnorm((seq_len(4000) - 0.5) / 4000)
state_effect <- function(lambda, sigma_mu, const, label) {
p0 <- plogis(const + sigma_mu * mu_grid)
p1 <- plogis(const + lambda + sigma_mu * mu_grid)
data.frame(period = 1:8,
effect = vapply(1:8, function(h) mean((p1 - p0)^h), numeric(1)),
model = label)
}
state_cf <- rbind(
state_effect(1.1, 0, -0.55, "状態依存あり"),
state_effect(0, 1.4, -0.20, "固定的な異質性のみ")
)
ggplot(state_cf, aes(period, effect, color = model)) +
geom_line(linewidth = 0.9) + geom_point(size = 2) +
scale_y_continuous(labels = scales::percent) +
scale_x_continuous(breaks = 1:8) +
labs(x = "初期状態を変えた後の期", y = "購入確率の差", color = NULL) +
theme(legend.position = "top")状態依存があるモデルでは、初期の購入経験が次期の購入確率を変え、その違いがさらに次の期へ伝わる。図ではその差が徐々に小さくなる。第11回で考えた長期の顧客価値に施策を結びつけるには、このように、何が継続的な効果を生むかまで定める必要がある。
1.5 対策(i):固定効果ロジットのどの推定量かを区別する
\(\mu_i\)を個人ダミーとして同時に最尤推定する unconditional FE logit は、\(T\)が固定された短パネルでincidental parameters biasを持つ。バイアスの符号や大きさはDGP依存であり、「必ず逆方向」とは限らない。静学二値logitには個人別の総成功回数に条件づけて固定効果を消す conditional FE logit があるが、ラグ従属変数を含む動学モデルでは初期値と履歴が絡み、静学版の条件付き尤度をそのまま流用できない。
線形モデルとの比較にも注意しよう。静学線形モデルならwithin変換で加法的固定効果を除ける。しかし、ラグ従属変数を含む動学線形パネルではwithin推定量にも Nickell bias がある。したがって「線形FEなら短い\(T\)でも常に安全、非線形FEだけ危険」という区分ではない。
| モデル・推定法 | 短い \(T\) での論点 |
|---|---|
| 静学線形FE | 加法的固定効果をwithin変換で除去できる |
| 動学線形FE | ラグ従属変数と変換後誤差が相関し、Nickell bias |
| 静学二値conditional logit | 固定効果を条件づけで除けるが、時間不変変数は識別できない |
| 動学logitの個人ダミーMLE | incidental parametersと初期条件の双方が問題 |
1.7 推定法を選ぶときのチェック順
- ラグ係数を状態依存と呼ぶ前に、時間不変異質性を疑う。
- \(y_{i0}\) が「最初に観測された値」なのか、「観測前で見えない値」なのかを記法上も区別する。
- CREなら、条件付き平均だけでなく残差ランダム効果を個人尤度で積分する。
- 固定効果法なら、unconditional MLE・静学conditional logit・動学パネル向け推定法を区別する。
- 補助分布、求積点数、初期条件、外生性への感度を報告する。
問い:スーパーマーケットの実際のスキャナーデータ(POSデータの原型)から、ブランド選択モデルをどう推定するか。そこに「ロイヤルティ」をどう組み込むか。
データ:コーヒーカテゴリの家庭用スキャンパネルデータ。価格・特売(feature)・陳列(display)といった観測可能な変数に加え、購買履歴の情報を持つ。
識別戦略・モデル:第2回で紹介した通り、彼らはロジットモデルに、過去の購買履歴の加重平均のような「ロイヤルティ変数」を説明変数として組み込んだ。これは、今日の\(y_{i,t-1}\)(ラグ選択ダミー)よりも滑らかな形(指数平滑化)だが、発想は同じで、「過去の購買が今日の効用に直接入る」という状態依存の一種である。
主要な発見:ロイヤルティ変数を入れることで、ブランド選択モデルの当てはまりが大きく改善した。この結果は、消費者の購買履歴が将来の選択に強い予測力を持つことを実証的に示し、以後数十年にわたる顧客パネル分析・state dependence研究の出発点になった。
なぜこの回と繋がるか:ロイヤルティ変数の説明力は、状態依存(\(\lambda\))から来ているのか、それとも個人異質性の代理として効いているのか。それを区別するには、今日のCREのように残差異質性を明示したモデルが必要になる。この問いに正面から取り組んだのが、次に紹介するDubé, Hitsch and Rossi (2010) である。
同じブランドを買い続ける理由を、固定的な好みだけで説明できるかを調べた研究である。対象はマーガリンと冷蔵オレンジジュースの購買パネルで、世帯ごとの選択、価格、販促を観測している。著者たちは、選好の分布を正規分布の混合で柔軟にし、誤差の系列相関も検討した。異質性の分布を1成分から5成分の混合へ柔軟にしても、購買経験に伴う状態依存が残るという結果を得ている。
さらに、慣性の理由としてロイヤルティ、探索、学習を比較し、このデータはロイヤルティによる説明と整合的だと報告している。これは単に「異質性を入れるとラグ係数が小さくなる」という研究ではない。異質性や他の仕組みを許したうえで、どの経路が残るかを調べている。仕組みが違えば、企業が今の価格を変えて将来需要を動かせるかどうかも変わる。そのため、今回の識別問題が次回の動学的な価格設定に直接つながる。原論文
2. 状態変数のカタログ:学習・参照価格・スイッチングコスト・在庫
ここまでは「前回買ったかどうか」というシンプルな状態を扱ってきたが、第13回の道具箱の言葉で言えば、これは「状態変数を何に取るか」という選択の1つに過ぎない。実際の消費者行動モデルでは、いろいろな変数が「状態」として使われる。
| 状態変数の種類 | 何を捉えるか | 代表的な研究 |
|---|---|---|
| 前回の選択(本日の主役) | ロイヤルティ・スイッチングコスト・単純な慣性 | Dubé, Hitsch and Rossi (2010) |
| ブランド品質についての信念(ベイズ事後分布) | 「使ってみないと品質が分からない」ときの学習 | Erdem and Keane (1996) |
| 参照価格(過去に見た価格の記憶) | 「これくらいが妥当」という基準からのズレへの反応 | 行動経済学的な価格反応モデル |
| 在庫水準(今日の後半の主役) | 買いだめ・在庫を持てる財の購買タイミング | Erdem, Imai and Keane (2003)、Hendel and Nevo (2006) |
| 累積使用経験・スイッチングコスト | 乗り換えの手間・学習コストそのもの | 携帯電話・保険・銀行口座の乗り換え研究 |
Erdem and Keane (1996) は、消費者が新しいブランドの品質を完全には知らず、使ってみて得た経験(シグナル)からベイズ更新で品質についての信念を更新していく、というモデルを提示した。この場合の「状態」は、消費者の頭の中にある品質の事後分布(平均と分散)そのものだ。プロモーションで一度試させることには、「その場の値引き効果」だけでなく、「品質についての情報を得させる効果」という別のルートがある、という洞察がここから生まれる。第13回でZurcherの「走行距離」という物理的な状態を扱ったのに対し、ここでの状態は消費者の頭の中の信念という、より抽象的な対象になる。それでもBellman方程式・価値関数という道具立ては変わらない。
ここでのベイズ更新は、第8・9回の推定とは主体が違う。第9回では、分析者が顧客の好みについて学んでいた。学習モデルでは、消費者自身が商品の品質について学ぶ。そのため、購入して経験を得ることが本人の信念を変え、次の選択にも影響する。同じ「事後分布」という言葉でも、誰が何について知ろうとしているのかを区別しておこう。
参照価格モデルは、「消費者は絶対的な価格水準よりも、自分が過去に見た価格からの乖離(値上げされた、値下げされた)に反応する」という考え方だ。過去の価格の(指数平滑化などによる)記憶が状態変数になる。
携帯キャリアやサブスクリプションサービスの「解約率が低い」という現象を見て、「うちのサービスはロイヤルティが高い」と結論づけるのは早計だ。乗り換え時の面倒さ(電話番号ポータビリティの手続き、新しいアプリへの学習コスト、メールアドレス変更の連絡)という本物のスイッチングコストによる部分と、単に「もともと乗り換えに興味のない層が多かった」という異質性による部分を分けて考える必要がある。前者は顧客の乗り換え反応を弱めるメカニズムだが、それだけで追加的な囲い込み施策の採算や望ましさは決まらない。後者も持続的な安定を生み得るが、観測された解約率だけから競合参入への反応はわからない。第13回で扱った「解約金のあるサブスク解約」の話——契約残期間という状態を見て駆け込み解約するかどうかを決める、という動学の問題——とも直結する論点だ。
3. 在庫を持つ消費者とセール
ここから後半。ロイヤルティの話から一転して、消費者が在庫を持てる財(洗剤、缶詰、トイレットペーパーのような日用消耗品)を買うときの動学に焦点を移す。
3.1 在庫モデルの直感
消費者には毎期、最大1単位を消費する機会が来ると仮定し、在庫の上限(パントリーの大きさ)があるとする。価格が通常価格の場合、消費者は「必要な分だけ、その都度買う」のが最適になりやすい。しかしセール(一時的な値下げ)が来ると、消費者は「今のうちに多めに買っておこう」というインセンティブを持つ。これが買いだめ(stockpiling)である。
この意思決定は、まさに第13回の枠組みそのものだ。状態 \(x\) = 在庫水準、行動 \(a\) = 今期の購入量、価値関数 \(V(x)\) を、購入コスト・保有コスト・消費からの効用のトレードオフを織り込んだBellman方程式で解く。第13回のZurcherにとって走行距離\(x\)が増えるほど維持費がかさんだのと対称的に、今日の消費者にとっては在庫\(x\)が減るほど欠品リスクが高まる。今日のモデルは第13回のRustモデル(エンジン交換の2択)よりもシンプルな部分がある——ここでは価格と消費機会を確率的にし、行動固有の効用ショックは置かないので、価値関数の計算はより素直な最大化になる。
3.2 在庫モデルを解いて購買データを作る
状態は在庫水準 \(x \in \{0, 1, \dots, X_{\max}\}\)。毎期、消費者は価格 \(p_t\)(通常価格 \(p_R\) または確率 \(\pi_{\text{sale}}\) でセール価格 \(p_S < p_R\))を観測し、購入量 \(a \in \{0,\dots,A_{\max}\}\) を選ぶ。購入後、確率的な「消費機会」(在庫があれば1単位消費、確率\(q(x+a)\))が発生する。フロー効用は
\[ \text{flow}(x, a, p) = u_c \cdot \mathbb{1}[\text{消費}] - p \cdot a - c_h \cdot x_{\text{end}} - c_{\text{out}} \cdot \mathbb{1}[\text{消費機会あり} \land \text{在庫ゼロ}], \]
\(u_c\)は消費から得る効用、\(c_h\)は保有コスト(保管・鮮度劣化)、\(c_{\text{out}}\)は欠品時の機会損失。価値関数は
\[ V(x) = \mathbb{E}_p\Big[\max_{a}\ \mathbb{E}_{\text{消費}}\big[\text{flow}(x,a,p) + \beta\, V(x_{\text{end}})\big]\Big]. \]
これを第13回と同じ価値関数反復(value function iteration)で解く。
まず1期だけを枝分かれで読むと、コードの添字を追いやすい。
| 順番 | 既知になるもの | 計算するもの |
|---|---|---|
| 1 | 期首在庫 \(x\) と今期価格 \(p\) | 購入量 \(a\) を選ぶ |
| 2 | 購入後在庫 \(x+a\) | 消費機会が確率 \(q(x+a)\) で到来 |
| 3a | 消費機会あり | 在庫があれば1単位消費、なければ欠品損失 \(c_{out}\) |
| 3b | 消費機会なし | 消費便益も欠品損失もなく、在庫を翌期へ持ち越す |
| 4 | 期末在庫 \(x_{end}\) | フロー効用 \(+\beta V(x_{end})\) |
この表から、欠品ペナルティが「消費機会なし」の枝に入ってはいけないことも分かる。
Xmax <- 5; Amax <- 3
beta_dp <- 0.96
u_consume <- 3.0
c_hold <- 0.12
stockout_cost <- 2.0
p_regular <- 1.00
p_sale <- 0.75 # セールは25%引き
sale_prob <- 1/8 # セールが来る確率(毎期iid)
q_base <- 0.6; q_slope <- 0.03 # 消費機会の確率(在庫が多いほど「使う機会」がわずかに増える)
q_of_x <- function(x) pmin(q_base + q_slope * x, 0.95)
states <- 0:Xmax
actions <- 0:Amax
expected_flow_continuation <- function(x, a, p, V) {
post <- x + a
q <- q_of_x(post)
# 消費機会がある場合(確率q)とない場合(確率1-q)を積分
val_consume <- {
consumption <- as.numeric(post >= 1)
end_inv <- post - consumption
cost <- p * a + c_hold * end_inv
# 欠品損失は「消費機会が来たのに在庫がない」この枝でだけ発生する
benefit <- if (post >= 1) u_consume else -stockout_cost
benefit - cost + beta_dp * V[end_inv + 1]
}
val_noconsume <- {
end_inv <- post
cost <- p * a + c_hold * end_inv
-cost + beta_dp * V[end_inv + 1]
}
q * val_consume + (1 - q) * val_noconsume
}
solve_inventory_dp <- function(sale_prob_arg = sale_prob, tol = 1e-10, maxit = 3000) {
V <- rep(0, Xmax + 1)
prices <- c(p_regular, p_sale)
probs_price <- c(1 - sale_prob_arg, sale_prob_arg)
policy <- matrix(0L, 2, Xmax + 1)
for (it in 1:maxit) {
pol_new <- matrix(0L, 2, Xmax + 1)
for (pi_idx in 1:2) {
p <- prices[pi_idx]
for (xi in seq_along(states)) {
x <- states[xi]
best_val <- -Inf; best_a <- 0
for (a in actions) {
if (x + a > Xmax) next
val <- expected_flow_continuation(x, a, p, V)
if (val > best_val) { best_val <- val; best_a <- a }
}
pol_new[pi_idx, xi] <- best_a
}
}
V_new <- numeric(Xmax + 1)
for (xi in seq_along(states)) {
x <- states[xi]
v_x <- 0
for (pi_idx in 1:2) {
a <- pol_new[pi_idx, xi]
v_x <- v_x + probs_price[pi_idx] * expected_flow_continuation(x, a, prices[pi_idx], V)
}
V_new[xi] <- v_x
}
err <- max(abs(V_new - V))
V <- V_new; policy <- pol_new
if (err < tol) break
}
list(V = V, policy = policy, iters = it)
}
dp_sol <- solve_inventory_dp()
cat("収束反復回数:", dp_sol$iters, "\n")収束反復回数: 571
cat("V(x), x=0..", Xmax, ":\n", sep = "")V(x), x=0..5:
print(round(dp_sol$V, 3))[1] 30.761 31.730 32.547 33.245 33.843 34.351
cat("\n通常価格での最適購入量 a*(x):\n")
通常価格での最適購入量 a*(x):
print(dp_sol$policy[1, ])[1] 1 0 0 0 0 0
cat("セール価格での最適購入量 a*(x):\n")セール価格での最適購入量 a*(x):
print(dp_sol$policy[2, ])[1] 2 1 0 0 0 0
在庫がゼロのとき、通常価格では「1単位だけ買う」(Just-in-time)だが、セール価格では「2単位買う」という、まさに買いだめの政策関数が出てくるはずだ。在庫を多く持っている状態では、セールでもこれ以上は買わない(保有コストと欠品リスクのトレードオフから、際限なく買いだめするのは最適ではない)。
df_policy <- data.frame(
inventory = rep(states, 2),
purchase = c(dp_sol$policy[1, ], dp_sol$policy[2, ]),
price_type = rep(c("通常価格", "セール価格"), each = length(states))
)
ggplot(df_policy, aes(inventory, purchase, color = price_type, shape = price_type)) +
geom_line(linewidth = 0.9) +
geom_point(size = 2.5) +
scale_color_manual(values = c("通常価格" = "grey50", "セール価格" = "#d62728")) +
labs(title = "在庫DPの最適政策関数 a*(x)",
x = "現在の在庫水準 x", y = "最適購入量 a*(x)", color = NULL, shape = NULL) +
theme(legend.position = "top")この在庫モデルは、第13回のRustモデル(バスのエンジン交換)と数学的には同じBellman方程式の構造をしている。対応関係は次の通り:状態 \(x\)(エンジンの累積走行距離 → 今日は在庫水準)、行動 \(a\)(交換する/しない → 今日は購入量、離散だが多値)、割引因子 \(\beta\)、価値関数 \(V(x)\)。違いは、Rustモデルでは行動固有のGumbelショックがあり、選択確率がロジット形(Emax = logsum)で求まったのに対し、今日のモデルでは価格と消費機会に不確実性があり、行動固有の効用ショックを置かないので、政策関数は決定論的(ある状態では確実にある購入量を選ぶ)になっている、という点だ。同じ道具箱の中の、少し違う使い方だと思ってほしい。
3.3 多数の消費者を集計する:スパイクとpost-promotion dip
1人の消費者の政策関数が分かったので、これを多数の消費者に適用してシミュレーションし、週次の売上系列を作る。孤立したセールイベント(10週目だけセール)を挟んで、その前後の売上を見る。
simulate_calendar <- function(N_hh, Tn, sale_bool, policy, seed = 1,
burn_in = 120, burn_sale_prob = sale_prob) {
set.seed(seed)
x <- rep(0L, N_hh)
for (t in 1:burn_in) {
# 店舗の週次価格は全世帯に共通。世帯ごとに別価格を引かない。
is_sale <- runif(1) < burn_sale_prob
a <- if (is_sale) policy[2, x + 1] else policy[1, x + 1]
post <- x + a
q <- q_of_x(post)
occ <- runif(N_hh) < q
consumption <- as.integer(occ & post >= 1)
x <- post - consumption
}
units_sold <- numeric(Tn)
for (t in 1:Tn) {
on_sale <- sale_bool[t]
a <- if (on_sale) policy[2, x + 1] else policy[1, x + 1]
units_sold[t] <- sum(a)
post <- x + a
q <- q_of_x(post)
occ <- runif(N_hh) < q
consumption <- as.integer(occ & post >= 1)
x <- post - consumption
}
units_sold
}
N_hh <- 10000
sale_bool_event <- rep(FALSE, 25); sale_bool_event[10] <- TRUE
units_event <- simulate_calendar(N_hh, 25, sale_bool_event, dp_sol$policy, seed = 42)
baseline <- mean(units_event[6:9])
df_event <- data.frame(week = 6:19, units = units_event[6:19]) %>%
mutate(pct_vs_baseline = (units / baseline - 1) * 100,
is_sale_week = week == 10)
kable(df_event %>% select(-is_sale_week), digits = 1,
caption = "孤立したセールイベント(10週目)前後の週次販売数量")| week | units | pct_vs_baseline |
|---|---|---|
| 6 | 6276 | -0.8 |
| 7 | 6257 | -1.1 |
| 8 | 6396 | 1.1 |
| 9 | 6370 | 0.7 |
| 10 | 16366 | 158.8 |
| 11 | 0 | -100.0 |
| 12 | 4141 | -34.5 |
| 13 | 5557 | -12.1 |
| 14 | 6108 | -3.4 |
| 15 | 6221 | -1.6 |
| 16 | 6310 | -0.2 |
| 17 | 6334 | 0.1 |
| 18 | 6251 | -1.2 |
| 19 | 6290 | -0.5 |
ggplot(df_event, aes(week, units)) +
geom_col(aes(fill = is_sale_week)) +
geom_hline(yintercept = baseline, linetype = "dashed", color = "grey40") +
scale_fill_manual(values = c("TRUE" = "#d62728", "FALSE" = "#1f77b4"), guide = "none") +
labs(title = "在庫モデルから生成された週次販売数量",
subtitle = "赤=セール週。セール後、数週間にわたり販売数量が沈む(post-promotion dip)",
x = "週", y = "販売数量(単位)")セール週に販売数量が跳ね上がり(スパイク)、その直後の数週間、販売数量がベースラインを大きく下回る(post-promotion dip)様子が見えるはずだ。買いだめした消費者が、しばらく在庫を消費するだけで、新たに買いに来ないからである。dipは数週間かけて徐々にベースラインへ戻っていく。これは、小売のPOSデータを扱ったことがある人なら誰でも見たことのある形だ。
「先月の大型セールの直後、今月の売上が急に落ち込んでいる。セールの効果が『マイナス』だったのではないか」という指摘が経営会議で飛ぶことがある。post-promotion dipは買いだめによる需要の前倒しと整合的だが、それ自体はセール成功の証拠ではない。セールの成否を判断するには、スパイクだけでなくdipまで含む十分な観測窓で、無施策の反実仮想に対する増分の累積販売数量・売上・粗利を比較する必要がある。
3.4 短期弾力性 vs 長期弾力性
ここが今日の後半の核心だ。価格変化は25%と大きいので、以下ではlog-log回帰係数と同じ尺度にするため、離散変化の弾力性を \(\log(Q_1/Q_0)/\log(P_1/P_0)\) で定義する。短期価格弾力性は「今週だけ価格を変えたときの今週の数量対数変化」を価格対数変化で割る。長期価格弾力性は「恒久価格レジーム間の定常販売数量の対数変化」を同じ価格対数変化で割る。買いだめが強い財では、この2つは大きく乖離しうる。
# 短期弾力性:1週間だけの不意打ち値下げが、その週の販売数量に与える影響
units_sale10 <- simulate_calendar(N_hh, 25, sale_bool_event, dp_sol$policy, seed = 42)
sale_bool_none <- rep(FALSE, 25)
units_nosale <- simulate_calendar(N_hh, 25, sale_bool_none, dp_sol$policy, seed = 42)
log_price_change <- log(p_sale / p_regular)
sr_log_q_change <- log(units_sale10[10] / units_nosale[10])
sr_elasticity <- sr_log_q_change / log_price_change
# 長期弾力性:価格レジームごとにDPを解き直して定常状態を比較
stationary_units <- function(always_sale, seed) {
Tprobe <- 60
sale_bool <- rep(always_sale, Tprobe)
cf_prob <- if (always_sale) 1 else 0
cf_dp <- solve_inventory_dp(sale_prob_arg = cf_prob)
u <- simulate_calendar(N_hh, Tprobe, sale_bool, cf_dp$policy,
seed = seed, burn_in = 150,
burn_sale_prob = cf_prob)
mean(tail(u, 15))
}
units_permanent_sale <- stationary_units(TRUE, seed = 99)
units_permanent_regular <- stationary_units(FALSE, seed = 99)
lr_log_q_change <- log(units_permanent_sale / units_permanent_regular)
lr_elasticity <- lr_log_q_change / log_price_changeここで長期反実仮想の政策関数を解き直すのは本質的である。「毎期たまたまセールが続く」と考える消費者と、「今後も恒久的に安い」と知る消費者では期待継続価値が違う。次の表は、実行したコードから短期・長期の値を直接作る。
| 指標 | 推定値 |
|---|---|
| 短期弾力性(1週間だけの不意打ち値下げ) | -3.282 |
| 長期弾力性(レジーム別にDPを解き直した定常状態) | 0.000 |
df_elast_plot <- data.frame(
measure = factor(c("短期弾力性\n(真の反応)", "静学回帰\n(見かけの弾力性)", "長期弾力性\n(真の反応)"),
levels = c("短期弾力性\n(真の反応)", "静学回帰\n(見かけの弾力性)", "長期弾力性\n(真の反応)")),
value = c(sr_elasticity, NA_real_, lr_elasticity)
)なぜこんなに違うのか。短期的には「セールに反応して買いだめする人の量」がそのまま数量の増加として観測される。しかし長期的には、恒久的に価格が安くても、1人が1期に消費できる量には上限がある(在庫を積み増しても、消費ペース自体はそれほど加速しない)。だから恒久的な値下げは、長期的な総消費量をそれほど押し上げない。買いだめが強い財ほど、この短期・長期の乖離は大きくなる。
3.5 静学的な需要推定はどう間違えるか
実務でよくあるのは、週次の価格と販売数量のデータに、単純な対数-対数回帰をかけて「価格弾力性」を推定するという分析だ。この静学推定は、セール週の「スパイク」を、恒久的な価格変化の効果であるかのように扱ってしまう。
# 何度もセールが起きる長期カレンダーをシミュレーションし、静学的なlog-log回帰にかける
Tlong <- 500
sale_bool_long <- runif(Tlong) < sale_prob
units_long <- simulate_calendar(N_hh, Tlong, sale_bool_long, dp_sol$policy, seed = 123, burn_in = 120)
price_long <- ifelse(sale_bool_long, p_sale, p_regular)
df_static <- data.frame(
log_units = log(pmax(units_long[-(1:10)], 0.5)),
log_price = log(price_long[-(1:10)])
)
fit_static <- lm(log_units ~ log_price, data = df_static)
static_elasticity <- coef(fit_static)["log_price"]
df_elast_plot$value[2] <- static_elasticity
ggplot(df_elast_plot, aes(measure, value, fill = measure)) +
geom_col(width = 0.55, show.legend = FALSE) +
geom_hline(yintercept = 0, color = "grey40") +
scale_fill_manual(values = c("#1f77b4", "#ff7f0e", "#2ca02c")) +
labs(title = "一時的セールの静学回帰と、恒久価格レジームは別の対象",
x = NULL, y = "価格弾力性")この静学回帰の係数は、iidに起きる一時的セールと当週販売量の関係であり、恒久的価格変更への弾力性ではない。このDGPでは買いだめのため短期反応が長期反応より大きくなるが、その倍率はパラメータと反実仮想の定義に依存する。固定した「30〜40倍」を一般的事実として外挿してはいけない。
在庫を持てる財では、セール時の売上増に、将来買う分を先に買う行動が含まれる。今回の静学回帰は、その週の価格と販売量の関係を捉えている。その係数を恒久的な値下げへの反応と解釈すると、この例では長期の反応を過大評価する。価格をどれだけの期間変えるのか、消費者はその変更を予見するのかを決め、対応する反実仮想を計算する必要がある。
問い:洗濯用洗剤のように長期保存できる日用品では、消費者の在庫保有行動を無視した需要推定は、価格弾力性やセールの効果をどれだけ見誤るか。
データ:米国の洗濯用洗剤カテゴリの世帯パネルデータ(スキャンパネル)。世帯ごとの購買履歴、価格、プロモーションのタイミングを長期間追跡。
識別戦略・モデル:消費者が在庫を持ち、価格変動(セール)を見越して購入タイミングを最適化する動学的な需要モデルを構築・推定した。これは今日のRシミュ2の在庫モデルの、本格的な構造推定版に当たる。在庫を無視した静学モデルと、在庫を織り込んだ動学モデルの推定結果を比較した。
主要な発見:静学的な需要推定は、長期的な需要反応(恒久的な価格変化への反応)をかなり過大評価する、という結論が得られた。在庫保有行動を無視すると、セールによる需要創出効果を過大に見積もり、価格弾力性の推定にも系統的なバイアスが生じる。
なぜこの回と繋がるか:今日のRシミュ2の在庫モデル・短期長期弾力性の比較は、この論文の考え方をレクチャースケールで再現したものである。実務でセールの効果検証を任された分析者が、静学的な回帰だけで「セールは効果があった」と結論づけることの危うさを、この論文は定量的に裏付けている。
問い:消費者は将来の価格がどうなるか完全には分からない中で、ブランドと数量をどう選んでいるのか。価格への「期待」(今後セールが来そうか)を考慮すると、需要のダイナミクスはどう変わるか。
データ:ケチャップカテゴリの家庭用スキャンパネルデータ。ブランド選択と購入数量の両方を同時に記録した詳細な購買履歴。
識別戦略・モデル:消費者が価格プロセス(今後の価格分布についての信念)をベイズ的に持ちながら、ブランド選択・購入数量・在庫保有を同時に最適化する、動学的な構造モデルを推定した。今日学んだ在庫モデルに、「将来価格の不確実性」という要素を明示的に組み込んだ、より精緻なバージョンである。
主要な発見:価格の不確実性を織り込んだモデルは、単純な在庫モデルよりも、観測される購買パターン(いつ買うか、どれだけ買うか)をよく説明できた。消費者は「今後セールが来そうにない」と予想するときほど、現在の価格で多めに購入する、という価格期待の効果が定量的に確認された。
なぜこの回と繋がるか:今日のモデルでは「セールが来る確率」を外生的な既知のパラメータとして扱ったが、現実の消費者はこの確率自体を経験から学習し、期待を形成している。この論文は、その期待形成まで含めた、より現実に近いモデルの姿を示している。
問い:カタログ通販において、現在の値引きの深さは将来購買にどう影響するか。その効果は新規顧客と既存顧客で同じか。
データ・アプローチ:カタログ通販事業者と協力し、値引き幅を実験的に変化させたフィールド実験を実施。短期的な注文行動だけでなく、その後の長期的な購買パターンまで追跡した一連の研究。
識別戦略・モデル:ランダム化比較試験(第10回で扱った施策の実験評価)を用いて、値引き幅の違いが顧客のその後の行動(リピート購買、値引きなしでの購入意欲)に与える因果効果を測定した。
主要な発見:深い値引きは、初回購入者では将来購買を増やした一方、既存顧客では将来購買を減らした。論文はforward buying、selection、customer learning、deal sensitivityなど複数の長期経路を示す。したがって「深い値引きは全顧客のブランド評価を一様に毀損する」という結果ではない。短期指標は既存顧客への総効果を過大評価する一方、初回購入者への総効果を過小評価しうる。
なぜこの回と繋がるか:短期の販売増だけで政策を評価できない点は同じだが、長期効果は顧客状態によって異なる。新規・既存をまとめた平均だけでなく、事前に定めたセグメント別効果と追跡期間全体の購買を見る必要がある。
4. 価格差別の入り口:状態を価格に使う
4.1 Pigouの「次数」とVarianによる現代的整理
1次・2次・3次という呼称はPigouに由来し、Varian (1989) は価格差別を現代の産業組織論として整理した。1次価格差別は各取引の留保価格まで個別に課金する完全価格差別、2次価格差別は数量・品質などのメニューから消費者が自己選択する方式、3次価格差別は観測可能なグループごとに異なる価格を設定する方式である。
4.2 履歴に基づく価格差別:最も単純な動学的価格戦略
今日の文脈で重要なのが、購買履歴という「状態」に基づいて価格を変えるbehavior-based pricingだ。履歴で粗いグループを作れば3次価格差別に近く、個人別の予測WTPへ細かく合わせればpersonalized pricing(1次価格差別への近似)に近い。新規顧客向け初月無料や休眠顧客向けクーポンを、すべて機械的に「3次価格差別」と呼ぶのは避けよう。
本物のスイッチングコストが存在するなら(今日の前半で\(\lambda>0\)を確認できたなら)、「まず補助金を払って獲得し、後で(スイッチングコストに守られて)回収する」という経路が生まれる。ただし、正の\(\lambda\)は初回値引きを正当化するための十分条件ではない。採算は、値引きが生む因果的な新規獲得増、値引き・獲得費用、将来の限界利益、もともとの継続率と顧客異質性、離脱、競合反応を合わせた増分NPVで判断する。逆に、\(\lambda\)が小さくても学習や広告など別の継続経路があれば便益は残り得る。したがって\(\lambda\)の識別は重要な入力の1つだが、価格戦略の合理性を単独では決めない。
2000年頃、あるオンライン小売企業が、閲覧履歴や過去の購買パターンに基づいて同一商品に異なる価格を提示していたことが発覚し、消費者から強い反発を受けたという逸話が広く語られている(詳細な経緯には諸説あり、ここでは「起こりうるリスク」の例として紹介する)。履歴に基づく価格差別は、企業にとっては合理的な収益最大化の手段でも、消費者から見れば「同じ商品なのに人によって値段が違う」という不公平感・不信感につながりやすい。動学的価格戦略を設計する際は、統計的・経済学的な最適性だけでなく、この種の炎上リスクや公平性の観点も同時に考慮する必要がある。
小売業界には、EDLP(Every Day Low Price:毎日安売り)とHi-Lo(通常は高め、時々大幅値下げ)という2つの価格戦略の大論争がある。今日学んだ買いだめの視点はこの論争に直接関わる。Hi-Lo戦略はセールのたびに買いだめを誘発し、post-promotion dipを生む。もし顧客が価格の変動パターンを学習してしまえば、「安くなるまで待つ」という行動が定着し、通常価格での購買が先細りになる恐れがある。一方EDLPは、こうした買いだめのタイミングゲームを排除できるが、セールによる一時的な話題性・トラフィック増加の効果は失う。どちらが有利かは、カテゴリの買いだめのしやすさ(保存性、パントリーの大きさ)に依存する、というのが今日の道具立てから見える整理である。
5. 反実仮想の例:動学モデルの上で政策を比較する
今日学んだツールの実践的な価値は、推定したモデルの上で、実行していない政策を計算できることにある。重い構造推定はせず、真のモデル(パラメータが分かっている前提)の上で、政策比較の考え方だけ簡単に見ておく。
セール頻度を月1回から月2回にしたら? 買いだめが強い財では、セール頻度を上げても、消費者の在庫上限に制約されて総消費量はほとんど増えない。むしろ、通常価格で売れていたはずの需要まで安値で捌いてしまうため、収益・利益は悪化しやすい。
# セール確率が変われば期待継続価値が変わるので、各レジームでDPを解き直す
simulate_random_calendar <- function(N_hh, Tn, sale_prob_arg, policy,
seed = 1, unit_cost = 0.35, burn_in = 200) {
set.seed(seed)
x <- rep(0L, N_hh)
for (t in 1:burn_in) {
on_sale <- runif(1) < sale_prob_arg
a <- if (on_sale) policy[2, x + 1] else policy[1, x + 1]
post <- x + a
occ <- runif(N_hh) < q_of_x(post)
x <- post - as.integer(occ & post >= 1)
}
units <- revenue <- profit <- numeric(Tn)
for (t in 1:Tn) {
on_sale <- runif(1) < sale_prob_arg
price_t <- if (on_sale) p_sale else p_regular
a <- if (on_sale) policy[2, x + 1] else policy[1, x + 1]
units[t] <- sum(a)
revenue[t] <- price_t * units[t]
profit[t] <- (price_t - unit_cost) * units[t]
post <- x + a
occ <- runif(N_hh) < q_of_x(post)
x <- post - as.integer(occ & post >= 1)
}
c(units = mean(units), revenue = mean(revenue), profit = mean(profit))
}
sale_prob_double <- 1/4
dp_base <- solve_inventory_dp(sale_prob_arg = sale_prob)
dp_double <- solve_inventory_dp(sale_prob_arg = sale_prob_double)
metric_base <- simulate_random_calendar(5000, 1000, sale_prob, dp_base$policy, seed = 1414)
metric_double <- simulate_random_calendar(5000, 1000, sale_prob_double, dp_double$policy, seed = 1414)
df_double_freq <- data.frame(
metric = factor(c("総販売数量", "収益", "利益"),
levels = c("総販売数量", "収益", "利益")),
pct_change = 100 * (metric_double / metric_base - 1)
)
kable(df_double_freq, digits = 2,
col.names = c("指標", "セール確率1/8→1/4の変化率(%)"))| 指標 | セール確率1/8→1/4の変化率(%) | |
|---|---|---|
| units | 総販売数量 | 0.61 |
| revenue | 収益 | -5.17 |
| profit | 利益 | -8.71 |
この数値はモデルの較正に依存する。重要なのは符号を先に決めつけることではなく、新しいセール確率のもとで消費者の政策関数を解き直し、販売数量だけでなく粗利益まで比べることだ。
ggplot(df_double_freq, aes(metric, pct_change, fill = pct_change > 0)) +
geom_col(width = 0.5, show.legend = FALSE) +
geom_hline(yintercept = 0, color = "grey40") +
scale_fill_manual(values = c("TRUE" = "#2ca02c", "FALSE" = "#d62728")) +
labs(title = "セール頻度を月1回から月2回に倍増した場合の変化(%)",
x = NULL, y = "変化率(%)")初回割引を廃止したら? これは1.6節の\(\lambda\)の推定値だけでなく、値引きによる因果的な獲得増、値引き・獲得費用、将来マージン、ベースライン継続率と顧客異質性、離脱、競合反応にも依存する。本物の正の状態依存があれば初回購入が後続購買へ波及する経路が生まれるが、\(\lambda=0\)ならこの経路はない。ただし、学習・広告など別の因果経路はあり得るので、「\(\lambda=0\)なら現実の波及効果は必ずゼロ」とまでは言えない。
「初回割引で獲得した顧客が、その後も居着いてくれるか」を考えるうえで、\(\lambda\)(状態依存)は重要なパラメータである。\(\lambda\)を過大評価すると、後続購買の増分を水増ししてLTVを楽観視しやすい。しかし投資判断は\(\lambda\)だけでは決まらない。値引きによる因果的な獲得増、値引き・獲得費用、将来マージン、ベースラインの継続率と顧客異質性、離脱、競合反応を同じ反実仮想の増分NPVに入れる必要がある。今日の識別問題は、その計算に必要な重要な入力を正しく測る問題である。
まとめ
- 「同じブランドを買い続ける」という持続性は、本物の状態依存(\(\lambda\))と個人異質性(\(\mu_i\))のどちらからも生まれる。購買率と1期リピート率が似ているだけでは区別できないが、それだけで厳密な観察的同値を示したことにはならない。
- naiveなpooled logitは、異質性しかない世界でも大きな見せかけの状態依存を検出する。動学logitの個人ダミーMLEにはincidental parametersと初期条件の問題がある。Wooldridge型CREでは、初期選択・外生変数履歴で条件付き平均をモデル化したうえで、残差ランダム効果を個人尤度の中で積分する。
- 精緻な構造推定を行うと、state dependenceは「存在するが、素朴な推定より小さい」という整理になる(Dubé, Hitsch and Rossi 2010)。ロックイン効果の過大評価は、顧客獲得投資の過大な正当化につながる。
- 学習(Erdem-Keane)、参照価格、スイッチングコスト、在庫は、すべて「状態変数を何に取るか」という同じ枠組みのバリエーションである。
- 在庫を持てる財では、セールは将来需要の前借りという側面を持つ(post-promotion dip)。一時的セールの静学回帰を、恒久価格レジームの効果として読むと対象を取り違える。倍率は較正依存である。
- 短期弾力性と長期弾力性の乖離は買いだめを示唆するが、それだけで買いだめを識別はできない。
- 履歴ベース価格はbehavior-based pricingであり、設計によって3次価格差別にもpersonalized pricingにも近づく。「先に補助、後で回収」の採算は、状態依存だけでなく将来マージン・離脱・競争にも依存する。
宿題
- ブラウザ実験室(playground14.html)で、今回のシミュレーションの数値を自分でいじって遊んでみよう。インストール不要、ブラウザだけでRが動く(初回ロードのみ30秒〜1分かかる)。
- ChatGPT(または他の対話型AI)に「state dependenceと個人の異質性はどう見分ければいいか」と聞き、pooled controlsと、残差ランダム効果を積分するCREを区別しているか確認しよう。
- 本文のCREコードで、DGP (b)の\(\sigma_\mu\)を0.8や1.8、求積点数を8や16に変え、\(\hat\lambda\)と\(\hat\sigma_r\)の感度を確認せよ。
- 在庫モデルのコードで、
c_hold(保有コスト)を大きくすると、買いだめの強さ(セール時の購入量)はどう変わるか。政策関数dp_sol$policy[2,]を書き換えて確認せよ。 - 身の回りで「初回割引・お試しキャンペーン」を提供しているサービスを1つ挙げ、状態依存に加えて、その値引きの増分NPVを評価するにはどんな獲得効果・費用・将来マージン・離脱・競争の情報が必要かを考えてみよ。
- coding課題は
assignment14.qmdを参照。ブランド選択パネルのDGPを作り、naive推定・CRE・placebo実験・在庫DGPの反実仮想を確認する。
次回予告
第15回では企業の価格設定を考える。消費者が将来の値下げを予想して待つなら、企業もその反応を見込んで価格を決める必要がある。まず2期間の例で、消費者の待機を無視した値付けがどう変わるかを確かめる。
その後、企業自身が持つ在庫と販売期限を状態とするモデルを解く。今売る利益と、在庫を残して後で売る利益を比較する仕組みは、第13回のBellman方程式と共通している。個人の好みを推定し、施策を選び、その先の行動まで計算してきた流れを、企業の動学的な意思決定につなげよう。
参考文献
- Varian, H. R. (1989). “Price Discrimination.” in Handbook of Industrial Organization, Vol. 1, 597–654.
- Erdem, T., and Keane, M. P. (1996). “Decision-Making Under Uncertainty: Capturing Dynamic Brand Choice Processes in Turbulent Consumer Goods Markets.” Marketing Science, 15(1), 1–20.
- Erdem, T., Imai, S., and Keane, M. P. (2003). “Brand and Quantity Choice Dynamics Under Price Uncertainty.” Quantitative Marketing and Economics, 1(1), 5–64.
- Hendel, I., and Nevo, A. (2006). “Measuring the Implications of Sales and Consumer Inventory Behavior.” Econometrica, 74(6), 1637–1673.
- Dubé, J.-P., Hitsch, G. J., and Rossi, P. E. (2010). “State Dependence and Alternative Explanations for Consumer Inertia.” RAND Journal of Economics, 41(3), 417–445.
- Ching, A. T., Erdem, T., and Keane, M. P. (2013). “Learning Models: An Assessment of Progress, Challenges, and New Developments.” Marketing Science, 32(6), 913–938.
- Guadagni, P. M., and Little, J. D. C. (1983). “A Logit Model of Brand Choice Calibrated on Scanner Data.” Marketing Science, 2(3), 203–238.
- Anderson, E. T., and Simester, D. I. (2004). “Long-Run Effects of Promotion Depth on New Versus Established Customers: Three Field Studies.” Marketing Science, 23(1), 4–20. https://doi.org/10.1287/mksc.1030.0040
- Heckman, J. J. (1981). “The Incidental Parameters Problem and the Problem of Initial Conditions in Estimating a Discrete Time-Discrete Data Stochastic Process.” in Structural Analysis of Discrete Data with Econometric Applications.
- Wooldridge, J. M. (2005). “Simple Solutions to the Initial Conditions Problem in Dynamic, Nonlinear Panel Data Models with Unobserved Heterogeneity.” Journal of Applied Econometrics, 20(1), 39–54.
- Neyman, J., and Scott, E. L. (1948). “Consistent Estimates Based on Partially Consistent Observations.” Econometrica, 16(1), 1–32.
- Rust, J. (1987). “Optimal Replacement of GMC Bus Engines: An Empirical Model of Harold Zurcher.” Econometrica, 55(5), 999–1033.
- 上武康亮・遠山祐太・若森直樹・渡辺安虎『実証ビジネス・エコノミクス』日本評論社。