Blog

Verstehen wie unsere Kunden Kauf­entscheidungen treffen: Discrete-Choice-Modelle mit RStan

04.10.2021 14:00
von

In unserem einführenden Artikel haben wir erläutert, wie Discrete-Choice-Modelle Einsichten in das Entscheidungsverhalten von Kund*innen generieren können. Um dies an einem Beispiel zu verdeutlichen, haben wir einige der Analysen aus Elshiewy et al. (2017) reproduziert. Die wesentlichen Resultate der Modellierung mittels multinomialer logistischer Regression (abgekürzt: MNL-Modellierung) haben wir in unserem ersten Artikel dargestellt, ohne bisher näher auf die Umsetzung oder technische sowie methodische Details eingegangen zu sein. In diesem Artikel zeigen wir auf, wie man ein MNL-Modell mit Hilfe von RStan, dem R-Interface der Statistik-Software Stan, schätzen kann. Stan ist eine Programmiersprache für statistische Inferenz, die in C++ geschrieben wurde. Sie wird verwendet, um komplexe (bayesianische) statistische Modelle spezifizieren zu können. Auch wenn es Zusatzpakete wie rstanarm für häufig vorkommende Modelle gibt, liegt ein Vorteil von Stan in der hohen Flexibilität bei der Spezifikation eigener Modelle. Dadurch lässt sich wertvolles Wissen von Marketingexpert*innen integrieren, das in Standard-Softwarepaketen nicht immer berücksichtigt werden kann. Da die durch MNL-Modelle erzielten Ergebnisse für die Praxis oftmals zu unterkomplex sind, stellen wir in einem zweiten Schritt sogenannte Random-Parameter-/ Mixed-MNL-Modelle vor. Diese Modelle gehören zum State-of-the-Art der Marketing-Forschung und erlauben es, Heterogenität zwischen den Konsument*innen zu berücksichtigen. Insbesondere können so unterschiedliche Sensitivitäten gegenüber Werbung oder Markenloyalität gemessen werden, wodurch es Marketing-Manager*innen ermöglicht wird, Konsument*innen individuell anzusprechen, anstatt uniforme Strategien verwenden zu müssen. Wir zeigen die Nützlichkeit der Mixed-MNL-Modellierung mit Stan im Vergleich zur einfachen MNL-Modellierung und präsentieren den Stan-Code der Modelle.

Datengrundlage

Der verwendete Datensatz stammt von Jain et al. (1994) und wird in Elshiewy et al. (2017) umfangreich analysiert. Er enthält Revealed-Preference-Daten für 136 Konsument*innen, deren Entscheidung für eine von vier Keksmarken (Keebler, Nabisco, Sunshine, eigene Marke) zu verschiedenen Zeitpunkten (zwischen 14 und 77) beobachtet wurde. Für jeden Verbraucher n (Individuum), jede Marke j (Alternative) und jede Wahlsituation t enthält der Datensatz drei Kekssorten-spezifische Marketing-Mix-Variablen (Preise der Kekse sowie Informationen zu verwendeten Werbemaßnahmen, d.h. zu geschalteter Werbung innerhalb und außerhalb der Filiale). Diese Informationen zeichnen sich dadurch aus, dass sie sowohl für die von der Käufer*in gewählte Keksmarke, als auch für die verfügbaren Alternativen vorhanden sind, was für Discrete-Choice-Modelle essentiell ist. Neben alternativenspezifischen Informationen können Discrete-Choice-Modelle auch mit Käufer*innen-spezifischen Variablen wie Alter oder Geschlecht umgehen, die hier aber nicht verfügbar sind.

Datenaufbereitung und -analyse

Wir installieren und laden die folgenden R-Pakete:

library(dplyr) 
library(mlogit)
library(rstan)

Zugriff auf die Daten, die im mlogit-Paket enthalten sind, bekommt man über die folgenden Befehle:

data(Cracker) 
cracker <- Cracker

Inspiziert man die Daten mittels head(cracker), so sieht man, dass die Daten im Wide-Format vorliegen und jede Zeile eine Kaufentscheidung darstellt. Die bereits im Datensatz vorhandene Id ist die personId n, die aufzeigt, welches Individuum die Kaufentscheidung durchgeführt hat. Zu beachten ist außerdem, dass es im Wide-Format für jede alternativenspezifische Variable so viele Spalten wie Alternativen gibt. Alternativenspezifische Variablen tragen in den Spalten das gleiche Präfix (die Bezeichnung der Marketing-Mix-Variable), gefolgt von einem Separator (zum Beispiel einem Punkt) sowie einem Bezeichner für die Alternative.

Die erste Marketing-Mix-Variable, die erhoben wurde, ist der Preis der Kekse. Die Preise der Kekse sind bereits mit Bezug auf ihr Füllgewicht normiert und in $-Cent / oz angegeben. Wir rechnen in $ / oz um und konstruieren zudem wie in Elshiewy et al. (2017) zusätzlich zu den drei vorhandenen Marketing-Mix-Variablen disp (Werbung innerhalb der Filiale), feat (Werbung außerhalb der Filiale) und price eine vierte Variable lastChoice, die eine Art Markenloyalität misst. Hierzu verwenden wir für jeden Kauf die Information, für welche Marke sich beim vorherigen Kauf entschieden wurde. Wir nennen zur besseren Nachvollziehbarkeit die id in personId um und erstellen zusätzlich die personChoiceId, die es uns ermöglicht, jede Kaufentscheidung der Person zuzuordnen, die sie durchgeführt hat. Außerdem löschen wir die erste Kaufentscheidung jeder Person, da für diese keine vorherige Kaufentscheidung bekannt, und somit keine Aussage zur Markenloyalität möglich ist.

cracker <- cracker %>% 
rename(personId = id) %>%
group_by(personId) %>% 
mutate(price.keebler = price.keebler / 100, 
   price.sunshine = price.sunshine / 100, 
   price.nabisco = price.nabisco / 100, 
   price.private = price.private / 100, 
   lastChoice = lag(choice)) %>% 
filter(!is.na(lastChoice)) %>%
mutate(personChoiceId = row_number()) %>%
ungroup() %>%
mutate(choiceId = row_number()) %>%
as.data.frame()

Schätzung eines multinomialen Logit-Modells mit RStan

Im Folgenden schätzen wir zunächst ein einfaches multinomiales Logit-Modell mit Hilfe von RStan. Das MNL-Modell ist ein Standard-Verfahren in der Marketing-Forschung bei der Analyse von Kauf- bzw. Auswahlentscheidungen. Das Modell, das wir schätzen, zeichnet sich durch generische Koeffizienten (d.h. ein Koeffizient pro Variable) für die Marketing-Variablen aus. MNL-Modelle diesen Typs werden auch als Conditional-Logit-Modelle bezeichnet. Generische Koeffizienten sind immer dann sinnvoll, wenn Variablen untersucht werden, die unabhängig von der Alternative den gleichen Nutzen für die Käufer*innen hervorbringen. Zum Beispiel ist eine Preissenkung von einem Euro unabhängig von den Alternativmarken einen Euro wert. Zusätzlich werden in dem Modell für drei der vier Keksmarken Konstanten geschätzt, die in Relation zur vierten Keksmarke (hier die Marke “Keebler”) interpretiert werden.

Ein Stan-Modell wird in einer .stan-Datei spezifiziert, die immer mindestens aus den drei Blöcken data, parameters und model besteht. Dort werden die Daten spezifiziert, die übergeben werden müssen, es wird festgelegt, welche Parameter geschätzt werden sollen, sowie das Modell spezifiziert. Dazu werden Skalare, Integer, Vektoren, Matrizen und Arrays deklariert. Detaillierte Informationen dazu, wie dies funktioniert, finden sich hier. Alternativ kann die Spezifikation direkt in einem Character-Vektor innerhalb eines R-Scripts durchgeführt werden, was wir im Folgenden tun.

Ein MNL-Modell wie oben, d.h. mit Intercepts (hier bezeichnet als Vektor alpha) und generischen Parametern für alternativenspezifische Variablen (beta) lässt sich wie folgt spezifizieren:

m1 <- "
data {
    int<lower=2> nAlternatives; // number of alternatives/outcomes
    int<lower=1> nChoices; // number of decisions
    int<lower=1> nVariables; // number of covariates
    int<lower=0,upper=nAlternatives> Y[nChoices];
    matrix[nAlternatives,nVariables] X[nChoices];
}

parameters {
    vector[nAlternatives-1] alpha_raw; 
    vector[nVariables] beta;
}

transformed parameters {
  vector[nAlternatives] alpha; 
  alpha = append_row(0, alpha_raw); 
}

model {
    for (i in 1:nChoices)
        Y[i] ~ categorical_logit(alpha + X[i]*beta);
}"

Der Funktion stan() aus dem rstan-Paket muss dann eine Liste von Objekten nAlternatives, nChoices, nVariables, Y und X übergeben werden, woraufhin die Parameter auf Basis des spezifizierten Modells geschätzt werden. In unserem Fall ist nAlternatives = 4, nChoices = 3156 und nVariables = 4. In Y steht für jede Kaufsituationen die Marke (repräsentiert durch eine Zahl zwischen 1 und 4), für die sich entschieden wurde. X ist eine Liste der Länge nChoices, die in jedem Eintrag eine Matrix mit vier Zeilen (Alternativen) und vier Spalten (Marketing-Mix-Variablen) enthält, und in der die Werte der Marketing-Variablen für jede Alternative stehen. Man beachte, dass es sich bei der Variable lastChoice, die in einer Spalte der Matrix steht, um eine Dummy-Variable handelt.

Um die Daten in das richtige Format zu bringen, greifen wir auf die Funktion mlogit.data() zurück. Diese überführt die Daten in Long-Format. Außerdem spezifizieren wir die Id, die Choice-Variable mit den Alternativen, die Spalten, die die alternativenspezifischen Variablen enthalten, sowie Datenformat und Separator der alternativenspezifischen Variablen. Detaillierte Informationen zur Verwendung von mlogit.data() finden sich hier.

cracker_mlogit <- mlogit.data(cracker, id = "choiceId", choice = "choice", varying= c(2:13), shape = "wide", sep = ".") 
cracker_mlogit$lastChoice <- as.integer(cracker_mlogit$lastChoice == cracker_mlogit$alt) 
cracker_long <- cracker_mlogit %>% as.data.frame()
disp <- split(cracker_long$disp, ceiling(seq_along(cracker_long$disp)/4))
feat <- split(cracker_long$feat, ceiling(seq_along(cracker_long$feat)/4))
price <- split(cracker_long$price, ceiling(seq_along(cracker_long$price)/4))
lastChoice <- split(cracker_long$lastChoice, ceiling(seq_along(cracker_long$lastChoice)/4))
X <- mapply(cbind, disp, feat, price, lastChoice, SIMPLIFY = FALSE)
Y <- as.numeric(factor(cracker$choice, levels = c("keebler", "nabisco", "private", "sunshine")))
stanData1 <- list(nChoices = 3156, nAlternatives = 4, nVariables = 4, Y = Y, X = X)

Wir schätzen nun das Modell wie folgt mit Hilfe von RStan.

p1 <- stan(model_code = m1, data= stanData1, init_r = 0.1,
           iter= 400, warmup = 200, chains = 4, 
           control = list(max_treedepth = 10, adapt_delta = 0.8))

Die Koeffizienten und weitere wichtige Informationen sind dann in p1 gespeichert.

Für die Evaluation der Güte der Schätzung gibt es eine Reihe von empfohlenen Schritten und Maßzahlen, auf die hier nicht näher eingegangen wird. Es sei aber bemerkt, dass man das spezifizierte Wahrscheinlichkeits-Modell immer an simulierten Daten (und Parametern) testen sollte, bevor man es für die Parameterschätzung von echten Daten verwendet. In diesem Artikel konnten wir das Modell gegen das Modell benchmarken, das in dem Artikel von Elshiewy et al. (2017) diskutiert und mit dem mlogit-Paket geschätzt wurde. Unsere Parameterschätzungen stimmen mit denen in dem Artikel überein.

Die Ergebnisse sind in der folgenden Tabelle dargestellt und wurden bereits im vorherigen Artikel ausführlich interpretiert. Einsichten, die aus dem Modell gewonnen werden können, werden im Fazit nochmal aufgegriffen.

MNL-Modell mit RStan

  Mean estimate Mean std. error
Nabisco (Intercept) 1.14 0.01
Private (Intercept) -0.59 0.01
Sunshine (Intercept) -0.64 0.01
Price -3.60 0.01
Feature 0.73 0.01
Display 0.17 0.00
Lastchoice 2.06 0.00

Schätzung eines Mixed-Logit-Modells mit RStan

Weil das Modell selbst eine Reihe von Schwächen aufweist, ist den Koeffizienten nur bedingt zu trauen. MNL-Modelle sind nicht in der Lage, Heterogenität in den Koeffizienten zu berücksichtigen. Anstatt anzunehmen, dass die Parameter zwischen den Individuen schwanken, unterstellen sie einen gemeinsamen Parameter um das Kaufverhalten zu beschreiben. Modelle, die die Heterogenität in den Koeffizienten berücksichtigen können, werden je nach Disziplin als Random-Coefficients-Logit-, Hierarchical-Bayes- oder Mixed-Logit-Modelle bezeichnet.

Im Folgenden schätzen wir ein Mixed-Logit-Modell, für das wir annehmen, dass die Verteilung der Koeffizienten über die Individuen (im Marketing auch bekannt als “individual part-worths”) multivariat-normalverteilt ist. Für die Darstellung der Koeffizienten-Matrix greifen wir aus Performance-Gründen auf deren Cholesky-Zerlegung zurück.

m2 <- "
data {
  int N; // number of rows
  int nDecisions; // number of person-choice sets
  int nPersons; // number of persons
  int nVariables; // number of covariates that vary by choice
  int nAlternatives; // number of alternatives
  
  vector[N] Y; // binary indicator for choice
  matrix[N, nVariables] X; // choice attributes
  
  int decision_person[nDecisions]; // index for person
  int start[nDecisions]; // the starting observation for each decision
  int end[nDecisions]; // the ending observation for each decision
}
parameters {
  vector[nVariables] beta; // hypermeans of the part-worths
  vector[nVariables] tau; // diagonal of the part-worth covariance matrix
  matrix[nPersons, nVariables] z; // individual random effects (unscaled)
  cholesky_factor_corr[nVariables] L_Omega;
}

transformed parameters {
    matrix[nPersons, nVariables] beta_individual = rep_matrix(beta', nPersons) + z*diag_pre_multiply(tau, L_Omega);
}

model {
  vector[N] log_prob;
  
  // priors on the parameters
  to_vector(z) ~ normal(0, 1);
  tau ~ normal(0, 0.5);
  beta ~ normal(0, 0.5);
  L_Omega ~ lkj_corr_cholesky(4);
  
  for(t in 1:nDecisions) {
    vector[nAlternatives] utilities; 
    utilities = X[start[t]:end[t]]*beta_individual[decision_person[t]]';
    
    log_prob[start[t]:end[t]] = log(softmax(utilities));
  }
  
  target += log_prob' * Y;
}
"

Dem Modell muss wieder eine Liste von Objekten N, nAlternatives, nDecisions, nPersons, nVariables, Y und X sowie Indizes (decision_person, start und end) übergeben werden. In Y ist die Kaufentscheidung im Long-Format als Dummy-Variable codiert. Wir fügen für drei der vier Alternativen Konstanten hinzu. Die Daten lassen sich in R wie folgt generieren:

X <- cracker_long[c("lastChoice", "disp", "feat", "price")]
X$Keebler <- rep(c(1,0,0,0), n = 3156)
X$Nabisco <- rep(c(0,1,0,0), n = 3156)
X$Sunshine <- rep(c(0,0,0,1), n = 3156)
Y <- cracker_long[c("choice")] %>% unlist() %>% as.numeric()
indexes <- data.frame(person = cracker_long$personId,
                      decision = cracker_long$choiceId,
                      row = seq(1:nrow(cracker_long))) %>%
  group_by(decision) %>%
  summarize(decision_person = first(person),
            start = first(row),
            end = last(row)) 

stanData2 <- list(N = 12624, nChoices = 3156, nPersons = 136, nVariables = 7, nAlternatives = 4, Y = Y, X = X, decision_person = indexes$decision_person, start = indexes$start, end = indexes$end)

Wir schätzen das Modell dann mittels:

p2 <- stan(model_code = m2, data= stanData2, init_r = 0.1,
           iter= 1000, warmup = 500, chains = 4, 
           control = list(max_treedepth = 10, adapt_delta = 0.8))

Zu beachten ist, dass für diese Modellierung erhöhte Rechnerkapazitäten gebraucht werden. Die Koeffizienten und weitere wichtige Informationen sind in p2 gespeichert.

Anders als beim MNL-Modell liefert ein Mixed-Logit-Modell nicht einen (generischen) Koeffizienten pro Variable, sondern Koeffizienten pro Variable und Person, die dann entweder auf Personenebene, oder aggregiert über alle Personen (als mittlere Sensitivitäten) interpretiert werden können. In der folgenden Tabelle sind die mittleren Koeffizienten (d.h. die Aggregate über alle Personen) ausgegeben.

  Mean estimate Mean std. error SD estimate
Keebler (Intercept) -0.21 0.01 0.24
Nabisco (Intercept) 2.10 0.01 0.23
Sunshine (Intercept) -0.53 0.01 0.22
Price -2.06 0.01 0.36
Feature 0.73 0.01 0.19
Display 0.28 0.00 0.14
Lastchoice 0.75 0.00 0.12

Fazit

Discrete-Choice-Modelle sind von großer Relevanz für die Marketing-Analyse, da sie wertvolle Einsichten in das Kaufverhalten ermöglichen. Zur Schätzung der Discrete-Choice-Modelle in diesem Artikel haben wir RStan, das R-Interface der statistischen Programmiersprache Stan, verwendet. Zunächst haben wir gezeigt, wie man ein MNL-Modell schätzen kann. Ein MNL-Modell erlaubt es, eine Reihe von interessanten Einsichten zu generieren. Beispielsweise indiziert ein negatives Vorzeichen der Variable Preis, dass für Konsument*innen fallende Preise mit höherem Nutzen einhergehen, und die Kaufwahrscheinlichkeit durch fallende Preise erhöht wird. Außerdem zeigen positive Vorzeichen bei den Variablen Feature und Display, dass die Verwendung von Werbe-Strategien positive Effekte hat, da sie die Kaufwahrscheinlichkeit im Mittel ebenfalls erhöhen. Zusätzlich können wir aus den Modellen ableiten, dass die Konsument*innen im Durchschnitt markenloyal sind (positives Vorzeichen bei LastChoice).

Da ein MNL-Modell eine Reihe von Limitationen aufweist, ist es für die Praxis jedoch oftmals zu unterkomplex. Beispielsweise unterstellt das Modell, dass die zur Verfügung stehenden Produkt-Alternativen unabhängig voneinander sind (obwohl manche Produkte häufig ähnlicher zueinander sind, als andere). Außerdem lassen sich mit einem MNL-Modell nur Aussagen über mittlere Sensitivitäten treffen, was dazu führt, dass nur uniforme Werbestrategien begründet werden können. Dabei ist es durchaus wahrscheinlich, dass Werbestrategien zwar im Mittel positive Effekte aufweisen, die Kaufwahrscheinlichkeit für einzelne Kund*innen durch zusätzliche Werbung jedoch negativ beeinträchtigt werden könnte. Daher gehört es mittlerweile zum State-of-the-art, Modelle zu verwenden, die die Heterogenität zwischen den Kund*innen berücksichtigen und kund*innenspezifische Analysen ermöglichen. Hierzu haben wir den Stan-Code eines Mixed-Logit-Modells vorgestellt. Ein Mixed-Logit-Modell schätzt Koeffizienten pro Variable und Konsument*in, die aus einer spezifizierten (multivariaten) Verteilung stammen. Die Koeffizienten des Modells in Tabelle 2 sind Mittelwerte über die Koeffizienten aller Konsument*innen im Datensatz. Dadurch sind sie mit denen des MNL-Modells vergleichbar. In der Analyse fällt zunächst auf, dass es Unterschiede zwischen den Parametern des Mixed-Logit-Modells und denen des MNL-Modells gibt. Insbesondere hat sich die (mittlere) Markenloyalität im Mixed-Logit-Modell verringert. Dieses Phänomen wurde auch in dem Artikel von Elshiewy et al. (2017) vorgefunden (mit leicht abweichenden Werten). Vermutlich sind die Koeffizienten des einfachen MNL-Modells verzerrt und die Markenloyalität wird dadurch im einfachen Modell fälschlich überschätzt. Zusätzlich zu den mittleren Sensitivitäten lassen sich in einem Mixed-Logit-Modell auch die individuellen Sensitivitäten analysieren. Dadurch können Aussagen über einzelne Konsument*innen getroffen werden. Bei der Analyse dieser Koeffizienten stellt man fest, dass die Preissensitivität zwar im Mittel negativ ist, es aber auch Kund*innen mit positiver Preissensitivität gibt. Außerdem gibt es welche, die auf die durchgeführte Werbung nicht ansprechen (negative Koeffizienten bei Feature und Price). Für diese Kund*innen müssen andere Werbestrategien entwickelt werden.

Insgesamt kann festgehalten werden, dass ein Mixed-Logit-Modell tiefere Einsichten liefern kann, als ein MNL-Modell. Insbesondere können individuelle Konsument*innenentscheidungen besser verstanden und so Konsument*innen individuell angesprochen werden. Wir empfehlen daher die Verwendung der State-of-the-art-Modelle, auch wenn diese schwieriger zu schätzen sind. Obwohl es in R bereits Pakete für Mixed-Logit-Modelle gibt, können komplexere Anpassungen auf Basis von Expertenwissen (komplexe Verteilungsannahmen, Parameterschranken) dort nicht immer ohne weiteres vorgenommen werden. Hierin liegt die Stärke von Stan, das flexible Modellspezifikationen ermöglicht. Modellgestützte Aussagen und Prognosen lassen sich dadurch verbessern.

Literatur

  • Jain, D. C., Vilcassim, N. J., and Chintagunta, P. K. (1994). A random-coefficients logit brand-choice model applied to panel data. Journal of Business & Economic Statistics, 12(3), 317–328.
  • Elshiewy, O., Guhl, D. and Boztug, Y. (2017): Multinomial Logit Models in marketing - From Fundamentals to State-of-the-Art. Marketing ZFP - Journal of Research and Marketing, 39(3), 32–55.

Zurück