# === CBC_DGP ===
choice_prob <- function(quality, price, alpha) {
v <- quality - price * alpha
vmax <- pmax(0, v[, 1], v[, 2])
ev <- exp(v - vmax)
cbind(outside = exp(-vmax), ev) / (exp(-vmax) + rowSums(ev))
}
simulate_cbc <- function(seed = 202607, N = 300, Ttrain = 8, Thold = 8) {
set.seed(seed)
beta_known <- c(battery12 = 0.5, nc = 0.8, major_brand = 0.3)
mu_true <- log(0.12) - 0.6^2 / 2
tau_true <- 0.6
theta_true <- rnorm(N, mu_true, tau_true)
make_tasks <- function(T) {
id <- rep(seq_len(N), each = T)
nr <- length(id)
price <- matrix(sample(c(8, 12, 16), nr * 2, replace = TRUE), nr, 2)
battery12 <- matrix(rbinom(nr * 2, 1, 0.5), nr, 2)
nc <- matrix(rbinom(nr * 2, 1, 0.5), nr, 2)
major_brand <- matrix(rbinom(nr * 2, 1, 0.5), nr, 2)
quality <- beta_known[1] * battery12 + beta_known[2] * nc + beta_known[3] * major_brand
prob <- choice_prob(quality, price, exp(theta_true[id]))
choice <- vapply(seq_len(nr), function(r) sample(0:2, 1, prob = prob[r, ]), integer(1))
list(id = id, price = price, battery12 = battery12, nc = nc,
major_brand = major_brand, quality = quality, choice = choice, T = T)
}
list(train = make_tasks(Ttrain), test = make_tasks(Thold), N = N,
beta_known = beta_known, theta_true = theta_true, mu_true = mu_true, tau_true = tau_true)
}
dat <- simulate_cbc()
train <- dat$trainLecture 7:商品設計とコンジョイント分析
計量経済学II
- コンジョイント分析では、価格や機能を変えた選択課題を作り、好みを測る。購買履歴だけでは比較しにくい商品設計を考えられる。
- 同じ回答から、属性が選択確率を動かす平均効果(AMCE)と、離散選択モデルの効用係数を推定できる。両者は異なる量である。
- 属性のランダム化は調査内の比較を可能にする。一方、実際の購入への予測には、調査回答と実行動の関係を確かめる必要がある。
- 個人のWTP、個人WTPの平均、平均係数の比、同質logitによる比を区別する。WTPの推定誤差をデルタ法で計算する。
- 商品設計にも個人の異質性が必要である。8問の回答から個人係数をどう学ぶかという問題を、第8・9回のベイズ推定につなげる。
前回とのつながり・今回のゴール
前回は、母集団の好みの分布と一人の購買履歴を組み合わせて、その人の次の選択を予測した。短い履歴だけから個人の好みを推定すると、たまたま続いた選択を強い好みと読み違えやすかった。集団の情報を使うことに意味があったのは、このためである。
今回は、好みを測るためのデータを自分で作る。例えばイヤホンの新製品を企画するとき、バッテリーを長持ちさせるのか、ノイズキャンセリングを付けるのか、価格を下げるのかを決めたい。既存商品の販売記録にも情報はあるが、欲しい比較が十分に含まれているとは限らない。新機能の需要を予測するには、その機能への評価について追加の仮定や情報が必要になる。
そこで、機能と価格を変えた仮想の商品を並べ、回答者に選んでもらう。これを choice-based conjoint(CBC) と呼ぶ。選択肢を作るのは分析者だが、回答の形式は第2回以来の離散選択と同じである。さらに同じ人に複数回答えてもらえば、前回と同様に、その人の選好を履歴から学ぶ問題になる。
まず今回は、調査の設計、平均的な属性効果、WTPの推定を扱う。そのうえで、平均だけでは商品設計に足りない理由を確認する。第8回でベイズ推定の考え方を学び、第9回では今回と同じ300人の回答に階層ベイズの選択モデルを当てはめる。
1. 選択課題を設計する
実際の購入と、調査への回答
実際に購入した記録を revealed preference(顕示選好、RP)、仮想的な選択への回答を stated preference(表明選好、SP)という。どちらも需要についての情報を含むが、観測している行動が異なる。
| 購買履歴(RP) | コンジョイントの回答(SP) | |
|---|---|---|
| 観測するもの | お金を払って選んだ商品 | 提示した仮想商品の選択 |
| 比較の作り方 | 市場で生じた価格・属性の変動を使う | 分析者が価格・属性を割り付ける |
| 役立つ場面 | 実際の市場での需要や購入パターン | 新機能や価格の組合せを比較する |
| 検討が必要な点 | 価格内生性、観測されない選択肢 | 仮想回答と購入の違い、調査対象者の代表性 |
第3回では、企業が商品人気を見越して価格を付けることが問題だった。CBCでは提示価格をランダムに割り付けられるため、そのような価格決定から切り離して比較を作れる。ただし、調査内で価格の効果が識別できることと、実際の売上を正確に予測できることは別である。
ワイヤレスイヤホンの例
今回使う属性は次の4つである。価格の単位は千円とする。
| 属性 | 水準 |
|---|---|
| 価格 | 8、12、16(千円) |
| バッテリー | 6時間、12時間 |
| ノイズキャンセリング | なし、あり |
| ブランド | 新興、大手 |
1問ごとに商品AとBを提示し、「A」「B」「どちらも買わない」から選んでもらう。買わないという選択肢を含めることで、AとBの相対的な好みだけでなく、この組合せなら購入するかも観測できる。今回は300人に8問ずつ回答してもらい、分析にはこの2,400問を使う。別に8問ずつ作るが、それは第9回の予測評価用として保留する。
担当者が「ノイズキャンセリングを付けたいか」とだけ聞けば、多くの人は欲しいと答えるかもしれない。しかし企業が知りたいのは、価格が上がってもその機能を選ぶかである。機能と価格を一緒に変えた選択肢を提示すれば、このトレードオフを観測できる。搭載を決めるには、推定した需要に加えて追加費用や既存商品からの買い替えも考える必要がある。
水準を増やせば比較できる範囲は広がるが、一人が回答できる問数には限りがある。非現実的な組合せを除く場合は、どの組合せがどの確率で出るかを記録する。今回の単純なOLS推定は、全属性を独立に割り付ける設計に対応している。制約付きの設計に変えれば、推定方法もその設計に合わせる必要がある。
2. 同じ回答から何を知りたいのか
選択確率を動かす平均効果:AMCE
計量Iのランダム化実験では、処置を受けた人と受けなかった人の平均を比べた。コンジョイントでも、例えばノイズキャンセリングが付いた商品と付かなかった商品の選択率を比べられる。ただし他の属性や相手の商品は、問ごとに違う。この違いについて平均した効果が AMCE(average marginal component effect、平均限界成分効果) である。
商品Aのノイズキャンセリングを \(z\)、それ以外の属性と商品Bの属性をまとめて \(W\) と書く。Aが選ばれるという潜在アウトカムを \(Y_{iA}(z,W)\) とすると、
\[ \operatorname{AMCE}_{\rm nc} =E_{i,W}[Y_{iA}(1,W)-Y_{iA}(0,W)]. \]
平均は、回答者の分布と、実験で使う属性の割付分布について取る。したがって、価格を主に8千円にする調査と16千円にする調査では、同じ人たちのAMCEでも変わりうる。これは「機能そのものの固定された価値」という量ではない。
独立したランダム割付のもとでは、水準ダミーを使うOLSでこの平均効果を推定できる。効用がlogit型であるという仮定は要らない。一方、前の問の提示が次の回答を変えないことや、他の回答者の割付が本人の回答を変えないことなど、どの回答を比較しているかについての実験仮定は必要である。
効用係数とWTP
離散選択モデルの立場では、属性と価格から効用を作る。
\[ u_{ijt}=x_{ijt}'\beta_i-\alpha_i p_{ijt}+\varepsilon_{ijt}. \]
ここで \(t\) は市場ではなく、同じ回答者の選択課題を表す。商品プロファイルも人ごとに異なるので \(x_{ijt},p_{ijt}\) と書いた。\(\varepsilon_{ijt}\) を標準Type I極値分布とすれば、係数を固定したときの選択確率はlogitになる。
効用係数は属性を評価する強さであり、AMCEのような確率の変化ではない。価格係数との比を使えば、「この機能のために何円まで払うか」というWTPを求められる。その解釈には、価格に対して線形な効用や、異質性の分布など、選択モデルの仮定が加わる。
| 知りたい量 | 主な方法 | 単位・意味 |
|---|---|---|
| 平均的な属性効果 | ランダム化した水準ダミーのOLS | 調査設計で平均した選択確率の変化 |
| 効用係数 | 選択モデルの推定 | 属性を評価する強さ |
| 個人のWTP | 個人の属性係数/価格係数 | 機能を金額に換算した値 |
| 個人の選択確率 | その人の係数の分布で平均 | 特定の商品集合から選ぶ確率 |
3. 選好に異質性のある回答を作る
今回のシミュレーションでは、価格への反応だけが人によって違う。
\[ v_{ijt}=0.5\,\mathrm{battery12}_{ijt} +0.8\,\mathrm{nc}_{ijt}+0.3\,\mathrm{major\_brand}_{ijt} -\alpha_i p_{ijt},\qquad \alpha_i=e^{\theta_i}, \]
\[ \theta_i\sim N(\mu,\tau^2),\qquad \tau=0.6,\quad \mu=\log(0.12)-\frac{0.6^2}{2}. \]
これで \(E[\alpha_i]=0.12\) になる。属性係数を共通にするのは、価格感応度の違いと個人の推定を追いやすくするためである。一般のCBCでは属性係数にも異質性を許せる。
同じ人の \(\theta_i\) を一度引き、その値を学習用8問と評価用8問の両方で使う。前回と同じく、一時的な選択の乱数と持続的な係数を区別している。
次の表が実際に提示される選択課題の例である。分析者には choice と提示した属性が見える。theta_true はシミュレーションの診断専用であり、推定には渡さない。
example <- data.frame(profile = c("A", "B"), price = train$price[1, ],
battery_hours = 6 + 6 * train$battery12[1, ], nc = train$nc[1, ], major_brand = train$major_brand[1, ])
kable(example, caption = "回答者1の最初の問")| profile | price | battery_hours | nc | major_brand |
|---|---|---|---|---|
| A | 16 | 12 | 0 | 0 |
| B | 8 | 12 | 0 | 0 |
share <- as.data.frame(prop.table(table(factor(train$choice, levels = 0:2, labels = c("買わない", "A", "B")))))
names(share) <- c("choice", "share")
ggplot(share, aes(choice, share)) + geom_col(fill = "#4C72B0") +
labs(x = NULL, y = "選択割合")価格感応度の分布も見ておく。係数は必ず正なので、どの人も価格が上がると効用が下がる。その下がり方に違いがある。
alpha_distribution <- data.frame(alpha = exp(dat$theta_true))
ggplot(alpha_distribution, aes(alpha)) + geom_histogram(bins = 30, fill = "#4C72B0") +
geom_vline(xintercept = .12, linetype = 2) + labs(x = "価格係数 alpha", y = "人数")4. AMCEを推定する
1問を、提示したAとBの2行へ変換する。Aを選んだときはA行だけが1、Bを選んだときはB行だけが1である。買わないと答えた問では両方を0とする。買わなかった問も残すことが大切である。商品属性を見た後の購入有無で標本を絞ると、ランダムに割り付けた比較ではなくなってしまう。
to_long <- function(tasks) {
do.call(rbind, lapply(1:2, function(j) data.frame(
id = tasks$id, profile = j, price = tasks$price[, j],
battery12 = tasks$battery12[, j], nc = tasks$nc[, j],
major_brand = tasks$major_brand[, j], chosen = as.integer(tasks$choice == j))))
}
cbc_long <- to_long(train)
cbc_long$price12 <- as.integer(cbc_long$price == 12)
cbc_long$price16 <- as.integer(cbc_long$price == 16)
fit_amce <- feols(chosen ~ price12 + price16 + battery12 + nc + major_brand,
data = cbc_long, cluster = ~id)価格は8千円、バッテリーは6時間、ノイズキャンセリングはなし、ブランドは新興を基準とした。例えば nc の係数は、ノイズキャンセリングを付けたときの選択確率の平均変化である。
シミュレーションなので真の選択確率も計算できる。ランダムに作った多数の個人・商品集合について、Aの属性だけを変えた確率差を平均し、AMCEの真値を数値積分で近似する。回答の0/1乱数をもう一度引く必要はない。
population_amce <- function(M = 100000, seed = 2026071) {
set.seed(seed)
alpha <- exp(rnorm(M, dat$mu_true, dat$tau_true))
price <- matrix(sample(c(8, 12, 16), 2 * M, TRUE), M, 2)
x <- lapply(1:3, function(k) matrix(rbinom(2 * M, 1, .5), M, 2))
probability <- function(p, xx) choice_prob(.5 * xx[[1]] + .8 * xx[[2]] + .3 * xx[[3]], p, alpha)[, 2]
baseprice <- price; baseprice[, 1] <- 8
price_effect <- sapply(c(12, 16), function(level) {
changed <- price; changed[, 1] <- level
mean(probability(changed, x) - probability(baseprice, x))
})
attribute_effect <- sapply(1:3, function(k) {
low <- high <- x; low[[k]][, 1] <- 0; high[[k]][, 1] <- 1
mean(probability(price, high) - probability(price, low))
})
c(price_effect, attribute_effect)
}
truth_amce <- population_amce()
b <- coef(fit_amce)[-1]; se <- sqrt(diag(vcov(fit_amce)))[-1]
amce_result <- data.frame(attribute = names(b), estimate = unname(b), truth = truth_amce,
lower = b - 1.96 * se, upper = b + 1.96 * se)
kable(amce_result, digits = 3, row.names = FALSE)| attribute | estimate | truth | lower | upper |
|---|---|---|---|---|
| price12 | -0.076 | -0.081 | -0.109 | -0.044 |
| price16 | -0.147 | -0.143 | -0.177 | -0.117 |
| battery12 | 0.082 | 0.089 | 0.058 | 0.106 |
| nc | 0.157 | 0.143 | 0.131 | 0.182 |
| major_brand | 0.036 | 0.053 | 0.010 | 0.063 |
ggplot(amce_result, aes(attribute, estimate)) +
geom_hline(yintercept = 0, color = "gray60") + geom_point(size = 3, color = "#4C72B0") +
geom_errorbar(aes(ymin = lower, ymax = upper), width = .1) +
geom_point(aes(y = truth), shape = 4, size = 3, color = "#C44E52") +
labs(x = NULL, y = "AMCE", subtitle = "点と区間:推定値と95%信頼区間、×:数値積分した真値")推定値と真値はおおむね近い。ここで回収しているのは選択確率の平均変化であり、効用式に入れた0.5、0.8、0.3ではない。
なぜ回答者単位の標準誤差か
同じ回答者の8問には同じ価格係数が入る。さらに同じ問のAとBは、片方が選ばれればもう片方は選ばれない。このため4,800行を互いに独立とは扱えない。回答者を一つのまとまりとして扱うクラスター標準誤差を使う。
fit_rows <- feols(chosen ~ price12 + price16 + battery12 + nc + major_brand,
data = cbc_long, vcov = "hetero")
se_compare <- data.frame(attribute = names(coef(fit_amce))[-1],
cluster = sqrt(diag(vcov(fit_amce)))[-1], row_independent = sqrt(diag(vcov(fit_rows)))[-1])
kable(se_compare, digits = 4, row.names = FALSE)| attribute | cluster | row_independent |
|---|---|---|
| price12 | 0.0165 | 0.0160 |
| price16 | 0.0153 | 0.0153 |
| battery12 | 0.0122 | 0.0126 |
| nc | 0.0131 | 0.0125 |
| major_brand | 0.0135 | 0.0126 |
クラスター標準誤差が常に大きくなるという意味ではない。どちらを使うかは、この標本で数値が大きいか小さいかではなく、どの単位の独立性を仮定できるかで決める。
買わないという回答を除いてからAMCEを推定する分析を考えよう。属性が魅力的かどうかは購入有無にも影響するので、残る回答者・問の組合せが属性によって変わる。これは、最初から「AかBを必ず選んでください」と設計した実験とも異なる。調査に含めたoutside optionは、分析でも残す。
5. 効用係数からWTPを求める
同質logitを当てはめると何を推定しているか
全員の係数が同じと仮定してlogitを当てはめてみる。計算は第2回と同じ最尤法である。ただし今回のデータでは価格係数に異質性があるので、これは同質logitによる近似(working model)になる。
\[E_\theta[P_j(\theta)]\ne P_j(E[\theta])\]
という非線形性のため、この推定値を個人係数の平均と解釈してはいけない。異質性を持つ人たちの回答を、一つの係数でどこまで近似できるかを調べる推定である。
fit_logit <- function(tasks) {
XA <- cbind(tasks$battery12[, 1], tasks$nc[, 1], tasks$major_brand[, 1], -tasks$price[, 1])
XB <- cbind(tasks$battery12[, 2], tasks$nc[, 2], tasks$major_brand[, 2], -tasks$price[, 2])
loss <- function(b) {
vA <- drop(XA %*% b); vB <- drop(XB %*% b)
mx <- pmax(0, vA, vB)
logden <- mx + log(exp(-mx) + exp(vA - mx) + exp(vB - mx))
-sum(vA * (tasks$choice == 1) + vB * (tasks$choice == 2) - logden)
}
op <- optim(c(.4, .6, .2, .1), loss, method = "L-BFGS-B",
lower = c(-Inf, -Inf, -Inf, 1e-5), hessian = TRUE)
stopifnot(op$convergence == 0)
b <- op$par
vA <- drop(XA %*% b); vB <- drop(XB %*% b)
mx <- pmax(0, vA, vB)
den <- exp(-mx) + exp(vA - mx) + exp(vB - mx)
score <- XA * ((tasks$choice == 1) - exp(vA - mx) / den) +
XB * ((tasks$choice == 2) - exp(vB - mx) / den)
S <- rowsum(score, tasks$id); G <- nrow(S)
bread <- solve(op$hessian)
V <- G / (G - 1) * bread %*% crossprod(S) %*% bread
names(b) <- c("battery12", "nc", "major_brand", "alpha")
list(coef = b, vcov = V, model_vcov = bread)
}
wtp_delta <- function(fit) {
b <- fit$coef; gradient <- c(0, 1 / b[4], 0, -b[2] / b[4]^2)
value <- unname(b[2] / b[4])
se <- sqrt(drop(t(gradient) %*% fit$vcov %*% gradient))
c(estimate = value, se = se, lower = value - 1.96 * se, upper = value + 1.96 * se)
}
fit_work <- fit_logit(train)
kable(data.frame(parameter = names(fit_work$coef),
dgp_mean = c(dat$beta_known, .12), working_estimate = fit_work$coef,
cluster_se = sqrt(diag(fit_work$vcov))), digits = 3, row.names = FALSE)| parameter | dgp_mean | working_estimate | cluster_se |
|---|---|---|---|
| battery12 | 0.50 | 0.434 | 0.064 |
| nc | 0.80 | 0.826 | 0.068 |
| major_brand | 0.30 | 0.185 | 0.069 |
| alpha | 0.12 | 0.104 | 0.007 |
比較のため真の係数の平均も載せたが、この列はworking modelの推定対象ではない。推論には回答者ごとのスコアをまとめたsandwich分散を使う。Hessianの逆行列だけでは、ここで許している個人内の依存とモデルの誤指定を反映できない。
WTPには複数の集計方法がある
ノイズキャンセリングを付けると個人 \(i\) の効用は \(\beta_{{\rm nc},i}\) 増える。価格を \(\Delta p\) 上げると \(\alpha_i\Delta p\) 減る。両者がちょうど釣り合う金額が、その人のWTPである。
\[\mathrm{WTP}_{{\rm nc},i}=\frac{\beta_{{\rm nc},i}}{\alpha_i}.\]
今回、分子は全員0.8だが、分母には個人差がある。平均を求めるなら、個人ごとの比を計算してから平均する。
\[E\left[\frac{\beta_{{\rm nc},i}}{\alpha_i}\right] \ne\frac{E[\beta_{{\rm nc},i}]}{E[\alpha_i]}.\]
wtp_table <- data.frame(quantity = c("個人WTPの平均(真値)", "個人WTPの中央値(真値)", "平均係数の比(真値)", "同質logitで推定した比"),
thousand_yen = c(.8 * exp(-dat$mu_true + dat$tau_true^2 / 2), .8 * exp(-dat$mu_true), .8 / .12,
fit_work$coef[2] / fit_work$coef[4]))
kable(wtp_table, digits = 3)| quantity | thousand_yen |
|---|---|
| 個人WTPの平均(真値) | 9.556 |
| 個人WTPの中央値(真値) | 7.981 |
| 平均係数の比(真値) | 6.667 |
| 同質logitで推定した比 | 7.909 |
wtp_individual <- data.frame(wtp = .8 / exp(dat$theta_true))
ggplot(wtp_individual, aes(wtp)) + geom_histogram(bins = 30, fill = "#4C72B0") +
geom_vline(xintercept = .8 / .12, linetype = 2) +
labs(x = "ノイズキャンセリングへの個人WTP(千円)", y = "人数", subtitle = "破線は平均係数の比")個人WTPの平均は約9.56千円、平均係数の比は約6.67千円である。この違いは標本誤差ではない。そもそも計算している量が違う。working modelで推定した比にも、そのモデルによる要約という意味しか自動的には付かない。
比の推定誤差:デルタ法
計量Iでは回帰係数の標準誤差から信頼区間を作った。WTPは2つの推定係数の比なので、両方の不確実性と共分散を使う必要がある。関数 \(g(\beta,\alpha)=\beta/\alpha\) を推定値の近くで直線近似するのがデルタ法である。
\[ \nabla g=\begin{pmatrix}1/\alpha\\-\beta/\alpha^2\end{pmatrix},\qquad \operatorname{SE}(\widehat{\mathrm{WTP}}) \simeq\sqrt{\nabla g'\widehat V\nabla g}. \]
\(\widehat V\) は \((\hat\beta,\hat\alpha)\) の分散共分散行列である。共分散を落とすと、同じデータから推定した2つの係数の関係を無視することになる。
\(\hat\vartheta\) が推定する対象を \(\vartheta^*\) とすると、
\[g(\hat\vartheta)\simeq g(\vartheta^*)+\nabla g(\vartheta^*)'(\hat\vartheta-\vartheta^*)\]
である。右辺の定数を除いて分散を取れば、\(\nabla g'V\nabla g\) が出る。同質logitが誤指定されている場合の \(\vartheta^*\) は、そのworking modelの擬似真値である。デルタ法を使っても、個人WTPの平均を推定する式へ変わるわけではない。
working_wtp <- wtp_delta(fit_work)
kable(as.data.frame(t(working_wtp)), digits = 3, caption = "同質logitによるWTP要約とデルタ法区間(千円)")| estimate | se | lower | upper |
|---|---|---|---|
| 7.909 | 0.634 | 6.667 | 9.151 |
正しく指定した場合の確認
推定方法が正しい対象を回収するかも確認しておこう。属性の提示は同じまま、全員の価格係数を0.12にそろえて回答を生成し直す。この場合は同質logitが正しく、ノイズキャンセリングのWTPの真値は \(0.8/0.12\) になる。
set.seed(2026072)
hom <- train
prob_hom <- choice_prob(hom$quality, hom$price, rep(.12, length(hom$id)))
hom$choice <- vapply(seq_len(nrow(prob_hom)), function(i) sample(0:2, 1, prob = prob_hom[i, ]), integer(1))
fit_hom <- fit_logit(hom)
wtp_hom <- wtp_delta(fit_hom)
kable(data.frame(parameter = names(fit_hom$coef), truth = c(dat$beta_known, .12), estimate = fit_hom$coef), digits = 3, row.names = FALSE)| parameter | truth | estimate |
|---|---|---|
| battery12 | 0.50 | 0.450 |
| nc | 0.80 | 0.922 |
| major_brand | 0.30 | 0.398 |
| alpha | 0.12 | 0.127 |
ci_data <- data.frame(estimate = wtp_hom[1], lower = wtp_hom[3], upper = wtp_hom[4])
ggplot(ci_data, aes(x = "同質logit", y = estimate)) + geom_point(size = 3) +
geom_errorbar(aes(ymin = lower, ymax = upper), width = .1) +
geom_hline(yintercept = .8 / .12, linetype = 2) +
labs(x = NULL, y = "WTP(千円)", subtitle = "破線:真値、区間:95%信頼区間")推定対象をそろえると、点推定値と真値を直接比較できる。価格係数が0に近い場合は比が大きく変動し、一次近似による区間も不安定になる。WTPだけを報告せず、価格係数自体がどの程度の精度で推定されているかも確認する。
6. 商品設計にも異質性が必要になる
平均の属性効果だけでは決まらない
AMCEは、実験で提示した他の属性について平均している。しかし実際の製品会議では「価格は12千円、バッテリーは12時間」といった特定の組合せを比べる。同じノイズキャンセリングでも、その組合せにおける需要の増え方が必要になる。
効用が属性の足し算で書かれていても、選択確率は非線形なので、確率スケールの交互作用は生じうる。次の図では、商品Bを価格12千円、全機能なしのまま固定し、商品Aの価格とノイズキャンセリングを変える。母集団の価格感応度分布について平均した、モデル上の選択確率である。
nodes <- qnorm((seq_len(1000) - .5) / 1000)
alpha_nodes <- exp(dat$mu_true + dat$tau_true * nodes)
design <- expand.grid(price = seq(8, 16, .5), nc = c(0, 1))
design$share <- mapply(function(p, nc) {
q <- cbind(rep(.5 + .8 * nc, 1000), rep(0, 1000))
prices <- cbind(rep(p, 1000), rep(12, 1000))
mean(choice_prob(q, prices, alpha_nodes)[, 2])
}, design$price, design$nc)
ggplot(design, aes(price, share, color = factor(nc))) + geom_line(linewidth = 1) +
labs(x = "商品Aの価格(千円)", y = "商品Aの選択確率", color = "ノイキャン")平均的に高く評価される機能でも、誰が買うかが変われば既存商品からの需要の移り方も変わる。第4回で代替関係を考えるために異質性が必要だったのと同じである。機能を一つ追加するか、異なる価格帯の商品を二つ用意するかという設計にも、分布が関係する。
価格に敏感な人と、機能のためなら高い価格を払う人がいるとする。全員に上位版だけを出すと、前者は買わないかもしれない。標準版を残せば購入を保てるが、上位版を買うはずだった人が標準版に移る可能性もある。個人別の選択確率を平均して各商品の需要を求め、製造費用と併せて比べる必要がある。平均WTPが追加費用を超えるという条件だけでは、商品群の最適な構成は決まらない。
8問から一人の係数を推定できるか
この調査は一人8問である。300人分を合わせれば平均効果を推定できても、一人の価格係数をその8問だけで正確に求められるとは限らない。例えば、たまたま安い商品を続けて選んだ人を、非常に価格に敏感だと推定してしまうかもしれない。前回の購買履歴と同じ問題である。
今回のモデルで一人の履歴を説明する尤度は、
\[L_i(\theta_i)=\prod_{t=1}^{8}P_{iy_{it}t}(e^{\theta_i})\]
である。ここに母集団の分布 \(\theta_i\sim N(\mu,\tau^2)\) を組み合わせれば、本人の回答と他の人たちの情報の両方を使える。第8回で「尤度と事前分布を合わせる」とはどういう計算かを学び、第9回でこの式を使う。
第9回では個人係数の推定を追いやすくするため、属性係数 \((0.5,0.8,0.3)\) を既知に固定し、\(\theta_i,\mu,\tau\) を推定する。今回の同質logitは属性係数も推定していたので、その点は異なる。実務で属性係数が既知という意味ではなく、階層モデルを段階的に学ぶための簡略化である。
7. 実証研究での使い方
この研究は、複数の属性を同時に変える調査から、各属性の因果効果をどう取り出すかを考えた。移民の受け入れに関する実験では、教育、職業、言語能力など9属性を変えた申請者の組を、一人に5組提示している。潜在アウトカムを使ってAMCEを定義し、割付設計に対応する回帰で推定する。さらに、回答への効果が問の位置や属性の表示順によって変わっていないかを調べた。例えば通訳を必要とすることの不利な効果は、属性の表示位置による比較で10〜23パーセントポイントの範囲にあり、等しいという仮説は棄却されなかった(\(p=0.14\))。ランダム化したから終わりではなく、想定した比較が回答過程でも保たれているかを点検する例である。論文§2.2、§5.3.4
この研究は、仮想的な回答が実際の行動にどこまで対応するかを、スイスの帰化に関する住民投票と調査実験で比較した。同じ種類の申請者属性について、実際の投票と複数の調査形式から効果を推定している。比較した21個の属性効果では、paired conjointと実行動の推定値の絶対差は平均2パーセントポイントだった。一方、拒否率の水準は調査に基づく予測が約21%、実際の投票が37%であった。属性効果が近いことと、選択率の水準まで一致することは異なる。この区別は、調査のAMCEを評価する場合と、新商品の販売数量を予測する場合で、確認すべきものが違うことを示している。論文Table 1、pp.2398–2399
一人の回答を減らしても、選好の個人差を推定できるだろうか。この研究はrating型のコンジョイントを対象に、個人の部分効用を階層ベイズで推定した。シミュレーションでは、一人16件の評価から2件ずつ減らし、2件まで情報を減らして比較している。個人別OLSが計算できなくなる少数回答でも、集団の分布を使うことで個人差を推定できることを示した。今回のCBCとは回答形式が違うが、一人の情報が少ないときに母集団から情報を借りる考え方は共通である。第9回ではこれを、評価点ではなく選択の尤度に組み合わせる。論文、p.177
まとめ
- CBCは価格と属性を割り付けた選択実験であり、同じ離散選択モデルで分析できる。
- AMCEは調査設計で平均した選択確率の変化である。効用係数やWTPとは単位も推定対象も違う。
- outsideの回答を残し、同じ人の複数回答を回答者単位の標準誤差に反映する。
- 個人WTPの平均と平均係数の比は異なる。デルタ法は、どの比を推定しているかを決めたうえで使う。
- 個人の異質性は、提案先を選ぶときだけでなく、商品群の設計や需要の移り方にも関係する。短い回答から個人係数を学ぶため、第8・9回でベイズ推定と階層モデルを使う。
宿題
- 同じノイズキャンセリングでも、価格の割付分布を変えるとAMCEが変わる理由を説明しよう。
- 平均WTPが8千円だったとして、全員が8千円の追加価格を払うと言えるだろうか。今回の個人WTPの分布で考えよう。
- ある人が8問すべてで買わないと答えたとき、個人の係数をどこまで決められるだろうか。
coding課題は 課題7 を参照。Playgroundでは、価格感応度と回答数を変えて計算できる。
次回予告
次回はベイズ推定の基礎を学ぶ。本人のデータが少ないとき、事前に持っている情報とどう合わせればよいかを、簡単な正規モデルから考える。尤度、事前分布、事後分布を区別し、完全に同じ係数を使う方法と一人ずつ推定する方法の間にあるpartial poolingを計算する。その後の第9回で、今回のCBCデータへ戻る。
参考文献
Hainmueller, J., D. J. Hopkins, and T. Yamamoto (2014). “Causal Inference in Conjoint Analysis: Understanding Multidimensional Choices via Stated Preference Experiments.” Political Analysis, 22(1), 1–30. 論文
Hainmueller, J., D. Hangartner, and T. Yamamoto (2015). “Validating Vignette and Conjoint Survey Experiments Against Real-World Behavior.” PNAS, 112(8), 2395–2400. 論文
Lenk, P. J., W. S. DeSarbo, P. E. Green, and M. R. Young (1996). “Hierarchical Bayes Conjoint Analysis: Recovery of Partworth Heterogeneity from Reduced Experimental Designs.” Marketing Science, 15(2), 173–191. 論文
Green, P. E., and V. Srinivasan (1990). “Conjoint Analysis in Marketing: New Developments with Implications for Research and Practice.” Journal of Marketing, 54(4), 3–19.
Egami, N., and K. Imai (2019). “Causal Interaction in Factorial Experiments: Application to Conjoint Analysis.” JASA, 114(526), 529–540.
Goplerud, M., K. Imai, and N. E. Pashley (2025). “Estimating Heterogeneous Causal Effects of High-Dimensional Treatments: Application to Conjoint Analysis.” The Annals of Applied Statistics, 19(2), 866–888.
Train, K. E. (2009). Discrete Choice Methods with Simulation, 2nd ed. Chapters 3, 6, 11. 著者による公開版
上武康亮・遠山祐太・若森直樹・渡辺安虎(2025)『実証ビジネス・エコノミクス』日本評論社。