Lecture 6:購買履歴と個人の需要

計量経済学II

作者

Kei Ikegami

重要この講義で押さえたいこと
  • 第4回で求めた好みの分布を、個人の履歴を使った予測へつなげる。母集団での割合と、その人の履歴を見た後の割合は異なる。
  • 同じ人の複数回の選択には、同じ選好係数が入る。履歴の尤度は、個人の係数を固定して各回の確率を掛け、その後にタイプについて平均する。
  • 一人ずつ推定すると、短い履歴では極端な係数が出やすい。集団の情報と本人の履歴を組み合わせると、何が改善するかを確かめる。
  • データを作り、母集団のタイプ割合を最尤法で推定し、顧客単位のブートストラップで推論する。予測は推定に使っていない後の選択で評価する。
  • 履歴から好みを学ぶことと、過去の購入が好みを変えることは別である。また、個人の予測ができても、施策の効果が識別できたとは限らない。

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

第4回では、商品属性への評価や価格への反応を人ごとに変え、需要の代替関係を柔軟にした。BLPで好みの分布を推定し、その需要を企業の価格設定モデルと組み合わせて、合併後の価格を計算した。第5回のゲスト講義では、こうした分析が合併審査で使われる場面を見た。

今回は、同じ離散選択モデルを別の問いに使う。ある顧客が過去にブラックコーヒーを何度も買っている。この人に、次もブラックを勧めてよいだろうか。市場全体のブラックのシェアだけで予測するより、この人の履歴を使った方がよさそうだ。では、どう使えばよいのか。

市場全体の需要を求めるときは、消費者の好みの分布について選択確率を平均していた。個人の予測では、その人の履歴を説明しやすい好みに大きな重みを付ける。モデルをまったく取り替える必要はない。何について平均するかを、履歴に応じて変えるのである。購買パネルを使うquantitative marketingでは、このような個人の予測を商品提案や販促の判断へつなげる。

ただし、一人の履歴が数回分しかないと、その人の好みを何でも自由に推定することは難しい。今回は、集団の情報と本人の履歴をどう組み合わせるかを、3タイプだけの小さなモデルで追う。次回は、好みを測るための選択課題を自分で設計するコンジョイント分析へ進む。その後の第8・9回で、ここで扱う情報共有をベイズ推定と階層モデルの言葉で整理する。

1. 同じモデルでも、知りたい量が変わる

第4回の市場シェアは、例えば次の積分だった。

\[ s_j=\int P_j(\theta)\,dF(\theta). \]

\(\theta\) は個人の好み、\(F\) は母集団での好みの分布である。全員に同じ商品を同じ価格で提示するとき、誰がどれを買うかを平均している。

今度は、顧客 \(i\) の履歴を \(H_i\) と書く。この人の次の選択を予測するなら、使いたいのは

\[ P_{ij}^{\mathrm{next}}=\int P_j^{\mathrm{next}}(\theta)\,dF(\theta\mid H_i) \]

である。履歴を見た後に、その人がどんな好みを持ちそうかを考え直してから平均する。ブラックを続けて選んだ人と、甘いコーヒーを続けて選んだ人では、\(F(\theta\mid H_i)\) が違う。

ここでの個人予測は、その人の好みを一つの数に断定することではない。「苦味好きかもしれないし、中間的な人がたまたまブラックを選んだのかもしれない」という不確実さを残したまま、次の選択確率を計算する。

分析の問い 使う情報 平均する分布
この市場で何本売れるか 商品の価格・属性、母集団の好み \(F(\theta)\)
この顧客が次に何を選ぶか 価格・属性に加え、本人の履歴 \(F(\theta\mid H_i)\)
この顧客への施策で利益が増えるか 上記に加え、施策が何を変えるかと費用 施策あり・なしの予測の差

最後の行には、まだ進んでいない。好みを予測するための履歴と、価格や広告を変えた効果を識別するための変動は、役割が違うからである。

ノートビジネスの現場で:購入した商品の記録だけで足りるか

会員ID付きの購買履歴からは、同じ人を時間を通じて追える。しかし「Aを買った」という記録だけでは、BよりAを好きだったのか、Bが品切れだったのか、Aだけが値引きされていたのかを区別できない。選択モデルに必要なのは、選ばれた商品に加えて、その時点で選べた商品の属性と価格である。「買わなかった」機会まで予測したいなら、来店や提示機会の記録も必要になる。購買時点だけを集めたデータでは、原則として購入したことに条件づけたブランド選択を説明することになる。

2. 一人の好みは、複数の選択に共通する

苦いコーヒーAと甘いコーヒーB、そして「買わない」を考える。Aの甘さを \(x_A=-1\)、Bを \(x_B=1\) と置く。顧客 \(i\) の機会 \(t\) における効用は

\[ u_{ijt}=b_0+\theta_i x_j-\alpha p_{ijt}+\varepsilon_{ijt},\qquad j\in\{A,B\}, \]

outsideの効用は \(\varepsilon_{i0t}\) とする。\(\varepsilon_{ijt}\) は選択肢・機会を通じて独立なGumbel分布に従う。今回は、\(b_0=1.5\) と価格係数 \(\alpha=1\) を既知とし、甘さへの好みだけに注目する。価格の単位は100円である。

好みには \(\theta_i\in\{-1.5,0,1.5\}\) の3タイプがあるとする。負の人は苦味、正の人は甘さを好み、0の人には甘さ自体の評価差がない。タイプの母集団割合を \(\pi=(\pi_1,\pi_2,\pi_3)\) と書く。同じ人の \(\theta_i\) は、観測期間を通じて変わらない。毎回引き直すのは、選択のGumbelショックである。

この設定は、第4回の連続的な好みの分布を、3点だけの分布にしたものだ。候補となる好みの値も既知として、まずその割合を推定する。実際の応用で、この3点が正しいと最初から分かるわけではない。ここでは履歴の使い方を見通しよくするための簡略化である。

履歴の確率をどう計算するか

タイプ \(k\) の人が、そのときの価格のもとで商品 \(j\) を選ぶ確率を \(P_{ijt}(\theta_k)\) とする。第2回のlogit公式で計算できる。タイプを固定すれば、複数の機会の選択は独立なので、観測した履歴 \(H_i\) の確率は

\[ L_i(\theta_k)=\prod_{t=1}^{T_i}P_{i,y_{it},t}(\theta_k) \]

となる。\(y_{it}\) は実際に選んだ商品である。タイプが見えないので、その後にタイプの割合で平均する。

\[ L_i(\pi)=\sum_{k=1}^3\pi_k L_i(\theta_k) =\sum_{k=1}^3\pi_k\prod_{t=1}^{T_i}P_{i,y_{it},t}(\theta_k). \]

この順序に注意しよう。機会ごとに先にタイプを平均してから掛ける \(\prod_t\sum_k\pi_kP_{i,y_{it},t}(\theta_k)\) は、毎回タイプを引き直すモデルの確率になる。今回の「同じ人は同じ好みを持つ」というモデルとは異なる。

例えば、苦味好きの人はAを何度も選びやすい。各回の価格が分かっていれば、その連続した履歴が苦味好きという説明にどれくらい整合的かを計算できる。パネルの情報は、単に全体の購入回数が増えることだけではない。どの選択とどの選択が同じ人のものかが情報になる。

3. 購買パネルを作る

400人について、学習用に16機会、評価用に別の20機会を作る。最初は学習用のうち4機会だけを使い、後で履歴を長くして比較する。価格は各機会で100〜250円の範囲から独立に発生させる。価格が好みや需要ショックに反応しないDGPなので、ここでは価格内生性を入れていない。

theta_grid <- c(-1.5, 0, 1.5)
pi_true <- c(.25, .5, .25)
N <- 400; T_train <- 16; T_test <- 20

choice_prob <- function(theta, pA, pB, beta0 = 1.5, alpha = 1) {
  va <- beta0 - theta - alpha * pA
  vb <- beta0 + theta - alpha * pB
  mx <- pmax(0, va, vb)
  ev <- cbind(exp(-mx), exp(va - mx), exp(vb - mx))
  ev / rowSums(ev)  # 列はoutside、A、B
}
sim_panel <- function(n, nt, types, theta_grid, beta0 = 1.5, alpha = 1) {
  id <- rep(seq_len(n), each = nt)
  pA <- runif(n * nt, 1, 2.5); pB <- runif(n * nt, 1, 2.5)
  prob <- choice_prob(theta_grid[types[id]], pA, pB, beta0, alpha)
  u <- runif(n * nt)
  y <- as.integer(u > prob[, 1]) + as.integer(u > prob[, 1] + prob[, 2])
  data.frame(id, occasion = rep(seq_len(nt), n), pA, pB, y)
}
type_true <- sample(1:3, N, replace = TRUE, prob = pi_true)
train_all <- sim_panel(N, T_train, type_true, theta_grid)
test <- sim_panel(N, T_test, type_true, theta_grid)
train <- subset(train_all, occasion <= 4)
head(train)
   id occasion       pA       pB y
1   1        1 1.421101 1.705005 2
2   1        2 1.776243 2.225994 0
3   1        3 1.928818 2.450031 2
4   1        4 1.025585 1.609221 2
17  2        1 1.253396 1.038730 0
18  2        2 1.748683 1.944229 0

評価用データも、学習用と同じ顧客のタイプを使って生成している。ただし選択のショックと価格は新しく引いている。分析者が見るのはID、機会、価格、選択であり、真のタイプは推定に使わない。

choice_summary <- train_all |>
  mutate(type = factor(type_true[id], labels = c("苦味好き", "中間", "甘さ好き")),
         product = factor(y, 0:2, c("買わない", "A:苦い", "B:甘い"))) |>
  count(type, product) |>
  group_by(type) |>
  mutate(share = n / sum(n))
ggplot(choice_summary, aes(product, share, fill = type)) +
  geom_col(position = "dodge") +
  labs(x = NULL, y = "選択割合", fill = "真のタイプ")

真のタイプごとの選択割合。個人のタイプは推定には使わない。

タイプが違えば選択の頻度は違う。ただし、甘さ好きでもいつもBを買うわけではない。価格とその回のショックによって、Aやoutsideも選ぶ。このばらつきがあるため、数回の履歴からタイプを完全には読み取れない。

4. 集団の割合を推定してから、本人の履歴を使う

4.1 母集団のタイプ割合を最尤法で推定する

第2回と同じく、観測データが出やすくなるパラメータを探す。今回は推定するパラメータがタイプ割合 \(\pi\) で、対数尤度は \(\sum_i\log L_i(\pi)\) である。割合は非負で合計1でなければならないので、2つの自由な数 \(a_1,a_2\) を

\[ \pi_k=\frac{e^{a_k}}{1+e^{a_1}+e^{a_2}}\ (k=1,2),\qquad \pi_3=\frac{1}{1+e^{a_1}+e^{a_2}} \]

と変換する。ここでの \(a\) は割合を計算する補助変数で、価格係数ではない。

panel_loglik <- function(dat, grid = theta_grid) {
  sapply(grid, function(th) {
    pr <- choice_prob(th, dat$pA, dat$pB)
    drop(rowsum(log(pr[cbind(seq_len(nrow(dat)), dat$y + 1)]), dat$id))
  })
}
fit_mixture <- function(ell) {
  softmax <- function(a) {
    z <- c(a, 0); z <- exp(z - max(z)); z / sum(z)
  }
  rowmax <- apply(ell, 1, max)
  e <- exp(ell - rowmax)
  loss <- function(a) -sum(rowmax + log(drop(e %*% softmax(a))))
  op <- optim(c(0, 0), loss, method = "BFGS")
  if (op$convergence != 0) stop("タイプ割合の推定が収束しなかった")
  pi <- softmax(op$par)
  ww <- sweep(e, 2, pi, "*")
  ww <- ww / rowSums(ww)
  list(pi = pi, weights = ww, Q = op$value)
}
ell <- panel_loglik(train)
fit <- fit_mixture(ell)
knitr::kable(data.frame(type = c("苦味好き", "中間", "甘さ好き"),
  truth = pi_true, estimate = fit$pi), digits = 3,
  col.names = c("タイプ", "真の割合", "推定した割合"))
タイプ 真の割合 推定した割合
苦味好き 0.25 0.269
中間 0.50 0.479
甘さ好き 0.25 0.252

400人の4回分の履歴から、タイプの割合を真値に近いところへ戻せている。ellの各行は一人、各列は一つのタイプで、その人の履歴の対数確率が入っている。長い履歴の確率を直接掛けると非常に小さな数になるため、コードでは対数と最大値を使って計算を安定させている。

4.2 履歴を見た後のタイプの重み

推定した母集団割合 \(\widehat\pi\) を使い、顧客 \(i\) のタイプについて次の重みを作る。

\[ w_{ik}=\frac{\widehat\pi_k L_i(\theta_k)}{\sum_{\ell=1}^3\widehat\pi_\ell L_i(\theta_\ell)}. \]

分子は「母集団でそのタイプがいる割合」と「そのタイプなら本人の履歴が出る確率」の積である。それを合計1になるように割り直す。履歴がなければ \(L_i(\theta_k)=1\) なので、\(w_{ik}=\widehat\pi_k\) となる。本人の情報が増えるにつれ、本人の履歴を説明しやすいタイプへ重みが移る。

この式は条件付き確率の計算であり、ベイズの公式である。ここではまず計算の意味を押さえ、第8回で事前分布・尤度・事後分布という言葉を使って詳しく学ぶ。なお、今回は母集団割合を点推定してから使っている。個人タイプの不確実さは残すが、割合自体の推定誤差を個人予測へ完全に伝えているわけではない。

show_ids <- c(which(type_true == 1)[1], which(type_true == 2)[1],
              which(type_true == 3)[1])
w_show <- rbind(fit$pi, fit$weights[show_ids, ])
weights_plot <- data.frame(
  customer = rep(c("履歴なし", paste0("顧客", show_ids)), each = 3),
  type = rep(c("苦味好き", "中間", "甘さ好き"), 4),
  weight = as.vector(t(w_show)))
ggplot(weights_plot, aes(customer, weight, fill = type)) +
  geom_col() + labs(x = NULL, y = "タイプの重み", fill = "タイプ")

同じ市場でも、本人の4回の履歴によってタイプの重みが変わる。

この図だけから、どの人のタイプも正確に当てたとはいえない。4回とも同じ商品を選んでも、別のタイプがたまたまそう選んだ可能性は残る。次の選択を予測するときには、その可能性も平均に入れる。

5. 一人ずつ推定すればよいのではないか

集団の情報を使わず、一人の対数尤度だけを最大にすることもできる。これを個人別推定(no pooling)と呼ぼう。今回は \(\theta_i\) を連続的に動かし、\(-5\) から5の範囲で探す。

ところが、4回ともBを買った人では、\(\theta_i\) を大きくするほどデータの確率が上がり続けることがある。これは第2回のlogitと同じ、完全分離に近い問題である。計算上の上限5で止まっても、「この人の好みは5と精密に分かった」という意味ではない。

no_pool <- function(dat) {
  sapply(split(dat, dat$id), function(di) {
    loss <- function(th) {
      pr <- choice_prob(th, di$pA, di$pB)
      -sum(log(pr[cbind(seq_len(nrow(di)), di$y + 1)]))
    }
    optimize(loss, c(-5, 5))$minimum
  })
}
th_np <- no_pool(train)
th_cond <- drop(fit$weights %*% theta_grid)
knitr::kable(data.frame(
  method = c("個人別推定", "履歴を条件にしたタイプ平均"),
  rmse = c(sqrt(mean((th_np - theta_grid[type_true])^2)),
           sqrt(mean((th_cond - theta_grid[type_true])^2)))), digits = 3)
method rmse
個人別推定 1.646
履歴を条件にしたタイプ平均 0.597
cat("個人別推定が探索範囲の端付近にある割合:", round(mean(abs(th_np) > 4.9), 3))
個人別推定が探索範囲の端付近にある割合: 0.178
estimates <- data.frame(truth = rep(theta_grid[type_true], 2),
  estimate = c(th_np, th_cond),
  method = rep(c("個人別推定", "履歴を条件にした平均"), each = N))
ggplot(estimates, aes(truth, estimate)) +
  geom_jitter(width = .12, height = 0, alpha = .2) +
  geom_abline(slope = 1, intercept = 0, color = "#bb4444") +
  facet_wrap(~ method) + labs(x = "真の係数", y = "推定した係数・条件付き平均")

4回の履歴では個人別推定が大きく散らばる。横軸は検証用の真の係数。

今回は候補となる3タイプが正しく指定されているので、タイプ間で重みを付ける方法に有利な設定である。その分布の指定が悪ければ、情報共有も誤った方向に働く。個人別推定がいつでも悪いという結論ではなく、短い履歴に合わせすぎると、極端な予測を作りやすいことを確認したい。

ノートビジネスの現場で:新規顧客への商品提案

初回購入の直後には、本人についての情報がほとんどない。母集団や、同じ観測属性を持つ顧客の情報を使えば、それでも予測を作れる。履歴が蓄積したら、本人の選択の重みを増やせばよい。ここで、新規顧客を「情報が少ないから対象外」にする必要も、たった一回の購入から強い嗜好を断定する必要もない。ただし、最初に提示した商品しか選べなかったなら、その提示ルールも考慮して履歴を読む必要がある。

6. 次の選択で予測を評価する

6.1 好みを一つに決めず、確率を平均する

新しい価格での予測は、各タイプのlogit確率を \(w_{ik}\) で平均して求める。

\[ \widehat P_{ij}^{\mathrm{next}}=\sum_k w_{ik}P_j^{\mathrm{next}}(\theta_k). \]

一般には、先ほどの条件付き平均 \(\sum_k w_{ik}\theta_k\) をlogitの式に一度だけ入れた確率とは違う。非線形な関数では、平均する順序が結果を変えるからである。

予測の比較には対数損失(log loss)を使う。実際に選ばれた商品へ付けた確率のマイナスの対数で、小さい方がよい。実際には起きた選択に、ほぼゼロの確率を付けると大きな損失になる。第2回で最大化した対数尤度の符号を逆にして、評価用のデータで測っている。

predict_mix <- function(dat, weights, grid = theta_grid) {
  out <- matrix(0, nrow(dat), 3)
  for (k in seq_along(grid)) {
    out <- out + weights[dat$id, k] * choice_prob(grid[k], dat$pA, dat$pB)
  }
  out
}
score_prob <- function(prob, dat) {
  loss <- -log(pmax(prob[cbind(seq_len(nrow(dat)), dat$y + 1)], 1e-12))
  drop(rowsum(loss, dat$id)) / as.numeric(table(dat$id))
}
pr_pop <- predict_mix(test, matrix(fit$pi, N, 3, byrow = TRUE))
pr_cond <- predict_mix(test, fit$weights)
pr_np <- choice_prob(th_np[test$id], test$pA, test$pB)
pr_oracle <- choice_prob(theta_grid[type_true[test$id]], test$pA, test$pB)
loss <- data.frame(population = score_prob(pr_pop, test),
  individual = score_prob(pr_np, test), conditional = score_prob(pr_cond, test),
  oracle = score_prob(pr_oracle, test))
score_table <- data.frame(method = c("母集団のみ", "個人別推定", "母集団+本人の履歴", "真のタイプを知る"),
                          log_loss = colMeans(loss))
knitr::kable(score_table, digits = 3,
             col.names = c("予測方法", "評価用データの対数損失"))
予測方法 評価用データの対数損失
population 母集団のみ 1.075
individual 個人別推定 1.088
conditional 母集団+本人の履歴 0.918
oracle 真のタイプを知る 0.837
ggplot(score_table, aes(reorder(method, -log_loss), log_loss)) +
  geom_col(fill = "#4477aa") + coord_flip() +
  labs(x = NULL, y = "対数損失")

推定に使っていない20機会で比較する。小さい方がよい。

本人の履歴と母集団の情報を組み合わせた予測が、両者の片方だけを使う予測よりよくなっている。「真のタイプを知る」は分析者には実行できない比較基準である。どこまでよくなり得るかを確かめるために、シミュレーションの真値を使った。

6.2 履歴を長くするとどうなるか

学習に使う機会を1、2、4、8、16回と増やす。そのたびに母集団の割合と個人別係数を推定し直し、同じ評価用データで比較する。評価用データを見てモデルを選び直すことはせず、この比較のために確保しておく。

learning_curve <- do.call(rbind, lapply(c(1, 2, 4, 8, 16), function(h) {
  d <- subset(train_all, occasion <= h)
  f <- fit_mixture(panel_loglik(d)); np <- no_pool(d)
  data.frame(history = h,
    method = c("母集団のみ", "個人別推定", "母集団+本人の履歴"),
    loss = c(mean(score_prob(predict_mix(test, matrix(f$pi, N, 3, byrow = TRUE)), test)),
             mean(score_prob(choice_prob(np[test$id], test$pA, test$pB), test)),
             mean(score_prob(predict_mix(test, f$weights), test))))
}))
ggplot(learning_curve, aes(history, loss, color = method)) +
  geom_line() + geom_point() + scale_x_continuous(breaks = c(1, 2, 4, 8, 16)) +
  labs(x = "学習に使う履歴の長さ", y = "対数損失", color = "予測方法")

履歴の長さと予測精度。短い履歴では集団の情報が特に重要になる。

個人別推定も、履歴が増えるにつれて安定する。一方、母集団だけの予測は、何回本人の選択を観測しても本人の情報を使わないので、大きくは改善しない。この違いを分けて見ると、履歴の価値は「データが多いほどよい」という話だけではないと分かる。

7. 推定の不確実性を測る

タイプ割合 \(\widehat\pi\) は有限の顧客標本から推定した値である。別の顧客を観測していれば、割合の推定値も変わる。ここでは、計量Iで学んだ標本抽出の考え方を使い、顧客を復元抽出して推定を繰り返すブートストラップを行う。

一回の購入をばらばらに抽出せず、一人の履歴をまとめて抽出する。同じ好みが複数回の選択に共通しているからである。80回の反復の標準偏差を標準誤差の目安にし、分位点で95%区間を作る。実際に精密な区間を報告する際は、反復数を増やして安定性も確認したい。

set.seed(2026061)
B <- 80
pi_boot <- t(replicate(B,
  fit_mixture(ell[sample.int(N, N, replace = TRUE), , drop = FALSE])$pi))
intervals <- data.frame(type = c("苦味好き", "中間", "甘さ好き"),
  estimate = fit$pi, se = apply(pi_boot, 2, sd), truth = pi_true,
  lower = apply(pi_boot, 2, quantile, .025),
  upper = apply(pi_boot, 2, quantile, .975))
knitr::kable(intervals, digits = 3)
type estimate se truth lower upper
苦味好き 0.269 0.031 0.25 0.224 0.345
中間 0.479 0.037 0.50 0.403 0.544
甘さ好き 0.252 0.029 0.25 0.199 0.317

顧客単位のブートストラップによるタイプ割合の95%区間。赤点が真値。

ggplot(intervals, aes(type, estimate)) +
  geom_point(size = 2) + geom_errorbar(aes(ymin = lower, ymax = upper), width = .15) +
  geom_point(aes(y = truth), color = "#bb4444", size = 2) +
  labs(x = "タイプ", y = "母集団の割合")

顧客単位のブートストラップによるタイプ割合の95%区間。赤点が真値。

この区間は母集団のタイプ割合の推定精度を表す。一人のタイプについての重み \(w_{ik}\) とは別の量である。母集団の割合が精密に分かっても、本人の履歴が短ければ、その人がどのタイプかは曖昧なまま残る。

8. 履歴を何のために使うか

今回のDGPでは、一人の好みは最初から最後まで固定されていた。Aを何度も買ったからAを好きになったわけではない。履歴を見た分析者の理解が変わったのである。実際の市場では、購入経験による学習やスイッチングコストが選択を変える可能性もある。この「固定的な好み」と「過去の行動そのものの影響」の区別は、第14回で扱う。

また、今回は価格を外生的に生成した。実際の企業が顧客の購買履歴を使って値引きを送り分けているなら、その値引きは分析者に見えない好みや購入見込みと相関し得る。個人を細かく予測できるモデルを使っても、この価格・施策の内生性が消えるわけではない。第10回のターゲティングでは、施策を行った場合と行わなかった場合の差を、どんなデータから識別するかを改めて考える。

ヒント実証研究コーナー:Guadagni and Little (1983) — 購買履歴からブランド選択を予測する

コーヒーの値引きや販促で、どのブランドの販売が増えるのだろうか。Guadagni and Little (1983) は、100世帯の32週間の購買記録に多項logitを当て、価格・販促・過去のブランド選択などと購入の関係を推定した。さらに、別の100世帯と、その後20週間のデータで予測を確かめた。ブランドごとのシェアの変動を、個人の選択を集計する方法で捉えられることを示した研究である。履歴と、そのときの価格・販促環境を合わせて使う点が、今回のモデルとつながる。ただし、履歴を説明変数に入れた係数を、そのまま過去の購入の因果効果と読むことはできない。

ヒント実証研究コーナー:Rossi, McCulloch and Allenby (1996) — 履歴にはどれだけ価値があるか

Rossi, McCulloch and Allenby (1996) は、購買履歴から個人の反応を推定し、クーポンを送り分けることの価値を調べた。人口属性、直近の購入、より長い履歴という異なる情報集合を、同じ意思決定問題の中で比べている。使った選択モデルはランダム係数の多項probitであり、今回のlogitと同一ではない。同論文のモデルに基づく計算では、短い履歴を使った個別クーポンの純収益増分が、一律配布の約2.5倍になった。一回の購買機会の情報だけでも、一律配布に比べて約50%大きい純収益増分が得られた。これはどの企業でも同じ改善率になるという結果ではなく、個人について得た情報の価値を金額で比較した例である。

ヒント実証研究コーナー:Lenk et al. (1996) — 一人に何問も聞けないとき

購買履歴が短い問題は、アンケートでも起きる。Lenk et al. (1996) は、仮想商品の評定を使うコンジョイント分析で、短い調査票から個人の好みをどこまで推定できるかを調べた。シミュレーションでは、一人あたりの推定用プロファイルを16から2へ、2ずつ減らす比較を行っている。個人別最小二乗が計算できなくなるほど回答が少なくても、階層ベイズで母集団の情報を使えば個人差を推定できる場合を示した。ここでのデータは数値による評定であり、今回のブランド選択とは異なる。それでも、「本人の情報が少ないときに、集団の情報をどう使うか」という問いは共通する。次回のコンジョイントと、第8・9回の階層ベイズをつなぐ研究である。

まとめ

重要この回のポイント
  • 母集団の好みを平均する需要予測から、本人の履歴を条件にした予測へ進んだ。使う選択モデルは共通でも、平均するタイプの重みが変わる。
  • 同じ人の係数は複数機会に共通する。各タイプのもとで履歴の確率を掛け、その後でタイプについて平均する。
  • 集団の割合と本人の履歴の尤度を掛けて重みを作り、その重みで次の選択確率を平均する。短い履歴ではタイプを断定しないことが予測に役立つ。
  • 個人別推定は、短い履歴では極端になりやすい。どの方法がよいかは、推定に使っていないデータで確かめる。
  • 固定的な好み、購入による状態の変化、施策の因果効果は区別する。情報共有は、識別の問題を自動的に解決するものではない。

宿題

  1. コードの学習期間を4回から2回へ変え、個人別推定が探索範囲の端に来る割合を比べよう。
  2. 本人の履歴が一度もない場合、予測に使うタイプの重みは何になるか、式から確認しよう。
  3. 評価用の20機会まで使って個人係数を推定し直してから、同じ20機会で評価すると何が問題だろうか。
  4. ブラウザ実験室で、履歴が増えると予測がどう変わるかを試そう。coding課題は assignment6.qmd を参照。

次回予告

次回は商品設計とコンジョイント分析を扱う。既存商品の購買履歴は有用だが、未発売の機能への好みはそのままでは観測できない。価格や属性を組み合わせた選択課題を作り、その回答から好みを測る。ただし、一人が答えられる設問数には限りがある。短い履歴の問題が、今度は短い調査票の問題として現れる。

参考文献

  • 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. 論文
  • Rossi, P. E., McCulloch, R. E., and Allenby, G. M. (1996). “The Value of Purchase History Data in Target Marketing.” Marketing Science, 15(4), 321–340. 論文
  • Lenk, P. J., DeSarbo, W. S., Green, P. E., and Young, M. R. (1996). “Hierarchical Bayes Conjoint Analysis: Recovery of Partworth Heterogeneity from Reduced Experimental Designs.” Marketing Science, 15(2), 173–191. 論文
  • Allenby, G. M., and Rossi, P. E. (1999). “Marketing Models of Consumer Heterogeneity.” Journal of Econometrics, 89(1–2), 57–78. 論文
  • Train, K. E. (2009). Discrete Choice Methods with Simulation, 2nd ed., Chapters 6 and 11. 著者公開版