Lecture 14:スイッチングコスト、ロイヤルティ、買いだめ

計量経済学II

作者

Kei Ikegami

重要この講義で押さえたいこと
  • 「同じブランドを買い続ける」という観測された持続性は、本物の状態依存(ロイヤルティ・スイッチングコスト)と単なる個人異質性(もともと好みが決まっている)のどちらからも生まれ得る。少数の記述統計だけでは、この2つを区別できない。
  • ナイーブなpooled logit(ラグ選択を説明変数に入れるだけ)は、異質性しかない世界でも大きな偽の状態依存を検出してしまう。Wooldridge型のcorrelated random effects(CRE)は、初期値と外生変数履歴に条件づけた残差ランダム効果を尤度の中で積分する。単にそれらを説明変数へ足すだけではない。
  • 一時的な値下げ・セールは、観測された売上をその場で押し上げるが、消費者が在庫を持てる財では、その一部(あるいは大部分)は将来の需要の前借りにすぎない。静学的な需要推定はこの前借りを「新しい需要」と誤読し、長期の反応を大きく過大評価する。
  • 短期価格弾力性と長期価格弾力性は別物であり、両者の乖離は買いだめを示唆しうる。ただし、学習・耐久性・調整費用などでも乖離し得るため、それ単独で買いだめを識別する指標ではない。
  • 状態依存の識別を間違えると、経営判断(顧客獲得への値引き投資、セールの頻度、ロイヤルティ施策の予算配分)を体系的に誤る。

前回とのつながり・今回のゴール

前回(第13回)では、Harold Zurcherという一人のバス整備責任者を題材に、消費者(意思決定者)を forward-looking な動学的最適化主体として扱う枠組みを学んだ。状態 \(x_t\)、行動 \(a_t\)、割引因子 \(\beta\)、価値関数 \(V(x)\)、choice-specific value function \(v(x,a)\)、条件付き選択確率(CCP)——この道具箱を使えば、「今日の選択が明日の状態を変え、それを見越して今日の選択をする」という行動を定量的に扱えるようになった。Rustのバスのエンジン交換モデルという抽象的な題材で道具を鍛えたので、今日はこれを消費者のブランド選択・買い物という、もっと身近な文脈に応用する。前回の最後で予告されていた通り、今日の主役は「一人の整備責任者」から「多数の消費者パネル」へと変わる。Zurcherにとっての走行距離が、今日の消費者にとっての「在庫」に置き換わる。

今日の講義は2つの「謎」から始まる。

謎1:あるブランドを何度も買い続ける「ロイヤルな顧客」が観測されたとする。これは、そのブランドが本当に消費者を引き留める力(本物のロイヤルティ、スイッチングコスト、学習効果)を持っているからなのか。それとも、たまたまそのブランドが好きな人がもともと存在していて、その人が単に「毎回同じものを買っている」だけなのか。

謎2:あるセールで売上が大きく伸びたとする。これは新しい需要を掘り起こしたのか。それとも、来週以降に買うはずだった人が今週前倒しで買っただけ(未来の需要の前借り)なのか。

どちらも根っこは同じ問題である。観測される「持続性」や「山」をどう解釈するかという問題だ。そして、この解釈を間違えると、実務上のインパクトは甚大である。謎1を見誤ると、「うちのブランドはロイヤルティが強い」と過信して、本当は効果の薄い顧客獲得施策(初回無料、大幅な入会割引)に大金を投じてしまう。謎2を見誤ると、「セールは効果的だ」と過信して、利益を毀損するだけの安売り合戦にはまり込む。

今日は前半でロイヤルティと状態依存の識別問題を、後半で在庫・買いだめとセールの問題を扱う。数学的な道具はどちらも第13回の動的計画法・価値関数の枠組みの応用である。第15回では同じ動的計画法を企業側の在庫管理と価格設定に再利用する。ただし、第15回の需要は各期の静学的な二値logitであり、今日のような消費者の戦略的待機や買いだめを企業問題へ組み込むわけではない。それらを同時に扱うモデルは、講義で扱う基礎モデルの自然な拡張である。

70%くらい分かったら先に進もう。今日は前半・後半でテーマが大きく変わるので、途中で一度頭を切り替える必要がある。

ノート記法について:状態依存パラメータには \(\lambda\) を使う

第13回では、Euler定数(logsum公式に出てくる定数、約0.5772)を\(\gamma\)と書いた。今日はこれとは別に「過去の選択が今日の効用に与える効果」を表すパラメータが主役になるが、これに\(\gamma\)を使うと前回のEuler定数と記号がかぶってしまう。そこで本講義では、状態依存の強さを表すパラメータには\(\lambda\)を使うことにする。割引因子\(\beta\)、効用パラメータ\(\theta\)という第13回の慣習はそのまま引き継ぐ。

1. state dependence と異質性:識別問題の核心

1.1 何が起きているか、言葉で

消費者 \(i\) が期 \(t\) にブランドAを買うかどうかを考える。効用を次のように書いてみる。

\[ u_{iAt} = \text{(価格などの観測要因)} + \lambda\, y_{i,t-1} + \mu_i + \varepsilon_{iAt}. \]

\(y_{i,t-1}\)は「前期にブランドAを買ったかどうか」というダミー変数、\(\lambda\)がその係数である。\(\lambda > 0\)なら、「前回Aを買った人は、今回もAを買いやすい」——これが状態依存(state dependence)である。前回の選択という「状態」が今回の効用に直接入り込む。これはまさに第13回で扱った動学の構造そのものだ。行動(前回の購買)が状態(\(y_{i,t-1}\))を作り、その状態が今日の効用に影響する。ロイヤルティプログラムの心理的効果、スイッチングコスト(別のブランドに乗り換える手間・学習コスト)、あるいは使ってみて品質が分かったという学習効果——理由は何であれ、「過去の行動それ自体が今の選好を変える」というメカニズムをすべて\(\lambda\)の中に押し込めて考える。

一方、\(\mu_i\)は個人ごとに固定された異質性(heterogeneity)である。もともとブランドAの味・ブランド・パッケージが好きな人は\(\mu_i\)が大きく、嫌いな人は小さい。この\(\mu_i\)は時間を通じて変わらない、その人固有の「地力」のようなものだ。

この2つのメカニズムは、どちらも「同じブランドを繰り返し買う」という観測結果を生む。しかし、政策的な含意はまったく違う。

ノートビジネスの現場で:CRMの「ロイヤル顧客」定義を疑う

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 Rシミュ1(見せ場1):少数の記述統計が似る2つのDGP

言葉で説明してもピンと来ないと思うので、実際にコードで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節で述べた通りだ)。

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)

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が、選んだ記述統計ではよく似る")
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")
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\)は簡単に有意にプラスになる、というのが今回のシミュレーションの教訓だ。

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.6 対策(ii):Wooldridge型correlated random effects

Wooldridge (2005) の発想は、最初の観測 \(y_{i0}\) と厳密外生変数の履歴 \(\mathbf p_i\) に条件づけた未観測効果の分布を指定することだ。ここでは簡単化して

\[ \mu_i = a_0+a_1y_{i0}+a_2\overline{p}_i+r_i, \qquad r_i\mid y_{i0},\mathbf p_i\sim N(0,\sigma_r^2) \]

と置く。後続期の条件付き選択確率は

\[ \Pr(y_{it}=1\mid r_i,\cdot)= \Lambda(c+\beta_p p_{it}+\lambda y_{i,t-1}+a_1y_{i0}+a_2\overline p_i+r_i). \]

重要なのは \(r_i\) が残ることだ。個人 \(i\) の尤度は、各期の確率を先に掛け合わせ、その後で \(r_i\) を積分する:

\[ L_i(\theta)=\int\prod_{t=1}^T P_{it}(r)^{y_{it}}\{1-P_{it}(r)\}^{1-y_{it}} \phi(r;0,\sigma_r^2)\,dr. \]

\(y_{i0}\)\(\overline p_i\) をpooled logitに足すだけ」では、この積分がなく、残差異質性をゼロと仮定してしまう。以下ではGauss–Hermite求積で積分する。

normal_gh <- function(n = 12) {
  # 標準正規分布に対するGauss--Hermiteの節点と重み(追加package不要)
  J <- matrix(0, n, n)
  off <- sqrt((1:(n - 1)) / 2)
  J[cbind(1:(n - 1), 2:n)] <- off
  J[cbind(2:n, 1:(n - 1))] <- off
  eg <- eigen(J, symmetric = TRUE)
  ord <- order(eg$values)
  list(z = sqrt(2) * eg$values[ord],
       w = eg$vectors[1, ord]^2)
}

build_cre_panel <- function(sim) {
  Tn <- ncol(sim$y)
  list(
    y = sim$y[, -1, drop = FALSE],
    lag = sim$y[, -Tn, drop = FALSE],
    price = sim$price[, -1, drop = FALSE],
    y0 = sim$y[, 1],
    pbar = rowMeans(sim$price[, -1, drop = FALSE])
  )
}

cre_loglik <- function(theta, d, gh) {
  c0 <- theta[1]; bp <- theta[2]; lam <- theta[3]
  a1 <- theta[4]; a2 <- theta[5]; sigma_r <- exp(theta[6])
  base <- c0 + bp * d$price + lam * d$lag + a1 * d$y0 + a2 * d$pbar
  log_terms <- matrix(NA_real_, nrow(d$y), length(gh$z))
  for (q in seq_along(gh$z)) {
    eta <- base + sigma_r * gh$z[q]
    ll_it <- ifelse(d$y == 1,
                    plogis(eta, log.p = TRUE),
                    plogis(eta, lower.tail = FALSE, log.p = TRUE))
    log_terms[, q] <- rowSums(ll_it) + log(gh$w[q])
  }
  m <- apply(log_terms, 1, max)
  sum(m + log(rowSums(exp(log_terms - m))))
}

d_cre_b <- build_cre_panel(dgp_b)
gh <- normal_gh(12)

# 説明変数を足すだけのpooled logitは、最適化の初期値としてのみ使う
df_controls_b <- transform(build_pooled_df(dgp_b),
  y0 = rep(dgp_b$y[, 1], times = Tn - 1),
  price_bar = rep(rowMeans(dgp_b$price[, -1]), times = Tn - 1))
fit_controls_b <- glm(y ~ price + y_lag + y0 + price_bar,
                      family = binomial(), data = df_controls_b)
start <- c(coef(fit_controls_b), log(0.8))

fit_cre_b <- optim(
  par = start,
  fn = function(th) -cre_loglik(th, d_cre_b, gh),
  method = "BFGS",
  control = list(maxit = 300, reltol = 1e-8)
)
stopifnot(fit_cre_b$convergence == 0)

lambda_cre_b <- fit_cre_b$par[3]
sigma_cre_b <- exp(fit_cre_b$par[6])
cre_tbl <- data.frame(
  method = c("naive pooled logit", "説明変数を足すだけ", "CRE(残差REを積分)", "真値"),
  lambda_hat = c(coef(fit_naive_b)["y_lag"],
                 coef(fit_controls_b)["y_lag"], lambda_cre_b, 0)
)
kable(cre_tbl, digits = 3,
      caption = "DGP(b):残差ランダム効果を積分して初めてCREになる")
DGP(b):残差ランダム効果を積分して初めてCREになる
method lambda_hat
naive pooled logit 1.090
説明変数を足すだけ 0.844
CRE(残差REを積分) 0.006
真値 0.000
重要ここが核心:条件変数の追加とCREを混同しない

\(y_{i0}\)\(\overline p_i\) は、未観測効果の条件付き平均を表す。そこからの残差 \(r_i\) を積分することで、同じ人の複数期の選択が共通の未観測効果を持つことを尤度に反映できる。CREの整合性は、動学の次数、価格の厳密外生性、\(\mu_i\mid y_{i0},\mathbf p_i\) の分布指定などに依存する。正規・線形という補助分布を誤指定すればバイアスは残り得るので、求積点数や分布仮定への感度確認も必要である。

ggplot(cre_tbl, aes(method, lambda_hat, fill = method)) +
  geom_col(width = 0.58, show.legend = FALSE) +
  geom_hline(yintercept = 0, linetype = "dashed", color = "grey40") +
  labs(title = "同じ初期値・平均価格を使っても、尤度が違う",
       subtitle = paste0("CREで推定した残差標準偏差 = ", round(sigma_cre_b, 2)),
       x = NULL, y = "lambda_hat") +
  theme(axis.text.x = element_text(angle = 15, hjust = 1))

DGP(b)(真のlambda=0):説明変数追加と、残差REを積分するCREの違い

1.7 推定法を選ぶときのチェック順

  1. ラグ係数を状態依存と呼ぶ前に、時間不変異質性を疑う。
  2. \(y_{i0}\) が「最初に観測された値」なのか、「観測前で見えない値」なのかを記法上も区別する。
  3. CREなら、条件付き平均だけでなく残差ランダム効果を個人尤度で積分する。
  4. 固定効果法なら、unconditional MLE・静学conditional logit・動学パネル向け推定法を区別する。
  5. 補助分布、求積点数、初期条件、外生性への感度を報告する。
ヒント実証研究コーナー:Guadagni and Little (1983) — ロイヤルティ変数の起点

問い:スーパーマーケットの実際のスキャナーデータ(POSデータの原型)から、ブランド選択モデルをどう推定するか。そこに「ロイヤルティ」をどう組み込むか。

データ:コーヒーカテゴリの家庭用スキャンパネルデータ。価格・特売(feature)・陳列(display)といった観測可能な変数に加え、購買履歴の情報を持つ。

識別戦略・モデル:第2回で紹介した通り、彼らはロジットモデルに、過去の購買履歴の加重平均のような「ロイヤルティ変数」を説明変数として組み込んだ。これは、今日の\(y_{i,t-1}\)(ラグ選択ダミー)よりも滑らかな形(指数平滑化)だが、発想は同じで、「過去の購買が今日の効用に直接入る」という状態依存の一種である。

主要な発見:ロイヤルティ変数を入れることで、ブランド選択モデルの当てはまりが大きく改善した。この結果は、消費者の購買履歴が将来の選択に強い予測力を持つことを実証的に示し、以後数十年にわたる顧客パネル分析・state dependence研究の出発点になった。

なぜこの回と繋がるか:ロイヤルティ変数の説明力は、本物の状態依存(\(\lambda\))から来ているのか、それとも個人異質性の代理として効いているのか。それを区別するには、今日のCREのように残差異質性を明示したモデルが必要になる。この問いに正面から取り組んだのが、次に紹介するDubé, Hitsch and Rossi (2010) である。

ヒント実証研究コーナー:Dubé, Hitsch and Rossi (2010) — state dependenceは存在するが、素朴な推定より小さい

問い:スーパーマーケットのスキャンパネルデータで観測される「同じブランドを買い続ける」という強い持続性は、本物の消費者の慣性(inertia)によるものか、それとも異質性や価格プロモーションのタイミングなど、他の要因で説明できるものか。

データ:家庭用品カテゴリ(マーガリン、洗濯用洗剤等)のスキャンパネルデータ。世帯ごとの購買履歴、価格、プロモーション情報を含む長期パネル。

識別戦略・モデル:本講義で見たような、状態依存パラメータと個人異質性を同時に扱う構造付きの需要モデルを推定した。ランダム係数(第4回・第9回で学んだ枠組み)で異質性を柔軟に許容しつつ、初期条件問題にも明示的に対処した。

主要な発見:丁寧に異質性をコントロールしてもなお、統計的に有意な状態依存(慣性)は残るが、その大きさは、異質性を無視した素朴な推定(naive pooled logit的な手法)が示唆する値よりもかなり小さい、という整理になった。つまり「観測される持続性の一部は本物の慣性だが、素朴な推定はその大きさを誇張する」というのが結論である。

なぜこの回と繋がるか:まさに今日のシミュレーションで見せた構図——naiveな推定は異質性を\(\lambda\)に取り違えて過大評価する——を、実データで丁寧に確認した研究である。この論文のビジネス含意は重い。ロックイン効果(本物のスイッチングコスト)を過大評価すると、顧客獲得のための値引き投資(初回割引など)を過大に正当化してしまう。「一度獲得すればずっと囲い込める」という前提が過大なら、獲得コストにかけられる予算の上限も過大に見積もってしまう。

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方程式・価値関数という道具立ては変わらない。

参照価格モデルは、「消費者は絶対的な価格水準よりも、自分が過去に見た価格からの乖離(値上げされた、値下げされた)に反応する」という考え方だ。過去の価格の(指数平滑化などによる)記憶が状態変数になる。

ノートビジネスの現場で:サブスクリプションの「乗り換え障壁」をどう測るか

携帯キャリアやサブスクリプションサービスの「解約率が低い」という現象を見て、「うちのサービスはロイヤルティが高い」と結論づけるのは早計だ。乗り換え時の面倒さ(電話番号ポータビリティの手続き、新しいアプリへの学習コスト、メールアドレス変更の連絡)という本物のスイッチングコストによる部分と、単に「もともと乗り換えに興味のない層が多かった」という異質性による部分を分けて考える必要がある。前者は顧客の乗り換え反応を弱めるメカニズムだが、それだけで追加的な囲い込み施策の採算や望ましさは決まらない。後者も持続的な安定を生み得るが、観測された解約率だけから競合参入への反応はわからない。第13回で扱った「解約金のあるサブスク解約」の話——契約残期間という状態を見て駆け込み解約するかどうかを決める、という動学の問題——とも直結する論点だ。

3. 買いだめとセール:後半の核

ここから後半。ロイヤルティの話から一転して、消費者が在庫を持てる財(洗剤、缶詰、トイレットペーパーのような日用消耗品)を買うときの動学に焦点を移す。

3.1 在庫モデルの直感

消費者は毎期1単位を消費すると仮定し(簡単化)、在庫の上限(パントリーの大きさ)があるとする。価格が通常価格の場合、消費者は「必要な分だけ、その都度買う」のが最適になりやすい。しかしセール(一時的な値下げ)が来ると、消費者は「今のうちに多めに買っておこう」というインセンティブを持つ。これが買いだめ(stockpiling)である。

この意思決定は、まさに第13回の枠組みそのものだ。状態 \(x\) = 在庫水準、行動 \(a\) = 今期の購入量、価値関数 \(V(x)\) を、購入コスト・保有コスト・消費からの効用のトレードオフを織り込んだBellman方程式で解く。第13回のZurcherにとって走行距離\(x\)が増えるほど維持費がかさんだのと対称的に、今日の消費者にとっては在庫\(x\)が減るほど欠品リスクが高まる。今日のモデルは第13回のRustモデル(エンジン交換の2択)よりもシンプルな部分がある——ここでの不確実性は「行動固有のショック」ではなく「今週セールが来るかどうか」という外生的な価格プロセスだけであり、価値関数の計算はより素直な最大化になる。

3.2 Rシミュ2(見せ場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\))が発生する。フロー効用は

\[ \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")

在庫水準ごとの最適購入量:通常価格ではjust-in-time、セール価格では在庫が少ないほど買いだめする

この在庫モデルは、第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週目)前後の週次販売数量")
孤立したセールイベント(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:買いだめがあると生じうる形

セール週に販売数量が跳ね上がり(スパイク)、その直後の数週間、販売数量がベースラインを大きく下回る(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

ここで長期反実仮想の政策関数を解き直すのは本質的である。「毎期たまたまセールが続く」と考える消費者と、「今後も恒久的に安い」と知る消費者では期待継続価値が違う。次の表は、実行したコードから短期・長期の値を直接作る。

短期・長期弾力性(同じRコードで計算)
指標 推定値
短期弾力性(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倍」を一般的事実として外挿してはいけない。

重要ここが核心:静学推定は買いだめ財の長期反応を大きく過大評価する

在庫を持てる財(ストック可能財)では、観測される「価格を下げたら数量が増えた」という関係のかなりの部分は、将来の需要の前借りであって、新しい需要の創出ではない。静学的な需要推定(価格を動かして数量がどれだけ動いたかを見るだけの回帰)は、この前借りを区別できず、長期的な価格反応を体系的に過大評価する。この乖離の大きさは製品カテゴリによって異なるが、洗剤やケチャップのような長期保存可能な日用品ほど深刻になりやすい。

ヒント実証研究コーナー:Hendel and Nevo (2006) — 洗剤の在庫行動が需要推定を歪める

問い:洗濯用洗剤のように長期保存できる日用品では、消費者の在庫保有行動を無視した需要推定は、価格弾力性やセールの効果をどれだけ見誤るか。

データ:米国の洗濯用洗剤カテゴリの世帯パネルデータ(スキャンパネル)。世帯ごとの購買履歴、価格、プロモーションのタイミングを長期間追跡。

識別戦略・モデル:消費者が在庫を持ち、価格変動(セール)を見越して購入タイミングを最適化する動学的な需要モデルを構築・推定した。これは今日のRシミュ2の在庫モデルの、本格的な構造推定版に当たる。在庫を無視した静学モデルと、在庫を織り込んだ動学モデルの推定結果を比較した。

主要な発見静学的な需要推定は、長期的な需要反応(恒久的な価格変化への反応)をかなり過大評価する、という結論が得られた。在庫保有行動を無視すると、セールによる需要創出効果を過大に見積もり、価格弾力性の推定にも系統的なバイアスが生じる。

なぜこの回と繋がるか:今日のRシミュ2の在庫モデル・短期長期弾力性の比較は、この論文の考え方をレクチャースケールで再現したものである。実務でセールの効果検証を任された分析者が、静学的な回帰だけで「セールは効果があった」と結論づけることの危うさを、この論文は定量的に裏付けている。

ヒント実証研究コーナー:Erdem, Imai and Keane (2003) — 価格の不確実性と買いだめの統合モデル

問い:消費者は将来の価格がどうなるか完全には分からない中で、ブランドと数量をどう選んでいるのか。価格への「期待」(今後セールが来そうか)を考慮すると、需要のダイナミクスはどう変わるか。

データ:ケチャップカテゴリの家庭用スキャンパネルデータ。ブランド選択と購入数量の両方を同時に記録した詳細な購買履歴。

識別戦略・モデル:消費者が価格プロセス(今後の価格分布についての信念)をベイズ的に持ちながら、ブランド選択・購入数量・在庫保有を同時に最適化する、動学的な構造モデルを推定した。今日学んだ在庫モデルに、「将来価格の不確実性」という要素を明示的に組み込んだ、より精緻なバージョンである。

主要な発見:価格の不確実性を織り込んだモデルは、単純な在庫モデルよりも、観測される購買パターン(いつ買うか、どれだけ買うか)をよく説明できた。消費者は「今後セールが来そうにない」と予想するときほど、現在の価格で多めに購入する、という価格期待の効果が定量的に確認された。

なぜこの回と繋がるか:今日のモデルでは「セールが来る確率」を外生的な既知のパラメータとして扱ったが、現実の消費者はこの確率自体を経験から学習し、期待を形成している。この論文は、その期待形成まで含めた、より現実に近いモデルの姿を示している。

ヒント実証研究コーナー:Anderson and Simester (2004) — 効果は新規客と既存客で異なる

問い:カタログ通販において、現在の値引きの深さは将来購買にどう影響するか。その効果は新規顧客と既存顧客で同じか。

データ・アプローチ:カタログ通販事業者と協力し、値引き幅を実験的に変化させたフィールド実験を実施。短期的な注文行動だけでなく、その後の長期的な購買パターンまで追跡した一連の研究。

識別戦略・モデル:ランダム化比較試験(第7回で学んだA/Bテストの枠組み)を用いて、値引き幅の違いが顧客のその後の行動(リピート購買、値引きなしでの購入意欲)に与える因果効果を測定した。

主要な発見:深い値引きは、初回購入者では将来購買を増やした一方、既存顧客では将来購買を減らした。論文は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つだが、価格戦略の合理性を単独では決めない。

ノートビジネスの現場で:Amazonの価格実験騒動という逸話

2000年頃、あるオンライン小売企業が、閲覧履歴や過去の購買パターンに基づいて同一商品に異なる価格を提示していたことが発覚し、消費者から強い反発を受けたという逸話が広く語られている(詳細な経緯には諸説あり、ここでは「起こりうるリスク」の例として紹介する)。履歴に基づく価格差別は、企業にとっては合理的な収益最大化の手段でも、消費者から見れば「同じ商品なのに人によって値段が違う」という不公平感・不信感につながりやすい。動学的価格戦略を設計する際は、統計的・経済学的な最適性だけでなく、この種の炎上リスクや公平性の観点も同時に考慮する必要がある。

ノートビジネスの現場で:EDLPかHi-Loか、買いだめの視点から見る小売の大論争

小売業界には、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 = "変化率(%)")

セール頻度2倍の反実仮想:DPをレジームごとに解き直した結果

初回割引を廃止したら? これは1.6節の\(\lambda\)の推定値だけでなく、値引きによる因果的な獲得増、値引き・獲得費用、将来マージン、ベースライン継続率と顧客異質性、離脱、競合反応にも依存する。本物の正の状態依存があれば初回購入が後続購買へ波及する経路が生まれるが、\(\lambda=0\)ならこの経路はない。ただし、学習・広告など別の因果経路はあり得るので、「\(\lambda=0\)なら現実の波及効果は必ずゼロ」とまでは言えない。

重要ここが核心:初回割引は、状態依存を含む増分NPVで評価する

「初回割引で獲得した顧客が、その後も居着いてくれるか」を考えるうえで、\(\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分かかる)。
  1. ChatGPT(または他の対話型AI)に「state dependenceと個人の異質性はどう見分ければいいか」と聞き、pooled controlsと、残差ランダム効果を積分するCREを区別しているか確認しよう。
  2. 本文のCREコードで、DGP (b)の\(\sigma_\mu\)を0.8や1.8、求積点数を8や16に変え、\(\hat\lambda\)\(\hat\sigma_r\)の感度を確認せよ。
  3. 在庫モデルのコードで、c_hold(保有コスト)を大きくすると、買いだめの強さ(セール時の購入量)はどう変わるか。政策関数dp_sol$policy[2,]を書き換えて確認せよ。
  4. 身の回りで「初回割引・お試しキャンペーン」を提供しているサービスを1つ挙げ、状態依存に加えて、その値引きの増分NPVを評価するにはどんな獲得効果・費用・将来マージン・離脱・競争の情報が必要かを考えてみよ。
  5. coding課題はassignment14.qmdを参照。ブランド選択パネルのDGPを作り、naive推定・CRE・placebo実験・在庫DGPの反実仮想を確認する。

次回予告

第15回は、消費者側の動学(第13回・第14回で学んだ)から視点を転換し、企業側の動学的な価格設定問題(dynamic pricing)に進む。今日の在庫DPで使った「状態・行動・遷移・価値関数」という道具を、次回は残席・残在庫を持つ売り手の問題へ再利用する。次回の基礎モデルでは各期の需要を静学的な二値logitで表し、消費者の戦略的待機や買いだめは組み込まない。企業在庫とforward-lookingな消費者を同時に扱うことは、その先の重要な拡張である。DPがもう一度、今度は売り手の頭の中で再演される。

参考文献

  • 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.
  • 上武康亮・遠山祐太・若森直樹・渡辺安虎『実証ビジネス・エコノミクス』日本評論社。