Mixed logit with latent variable and socio-demographic
Posted: 12 Dec 2024, 13:36
Dear Stephane,
I am building Mixed Logit Model allowed the inclusion of latent variables. I am interested in understanding whether the two external treatments influence consumers' preferences for attribute2 therefore the interaction term attribute2 * treatment is included, and how individual socio-demographic characteristics and attitudes impact the effects of these treatments.
Specifically, I draw on Hao et al.'s paper (See Hao, F., Qiu, R. T. R., Park, J., & Chon, K. (2023). The Myth of Contactless Hospitality Service: Customers’ Willingness to Pay. Journal of Hospitality & Tourism Research, 47(8), 1478-1502. https://doi.org/10.1177/10963480221081781), where the preference parameter is specified as a function of latent values, which differs from the approach in the Apollo manual (where personal characteristics and latent variables are directly incorporated into the utility function).
Below is my code, though I am unsure if it achieves my aim. Additionally, after implementing this, I don't how to estimate willingness to pay, can you advise accordingly? Many thanks.
# Mixed logit with latent variable and socio-demographic ###
apollo_beta = c(
asc_a = 0,
asc_b = 0,
asc_c = 0,
mu_log_b_attribute1 = 0,
sigma_log_b_attribute1= 1,
mu_log_b_attribute2 = 0,
sigma_log_b_attribute2 = 1,
mu_log_b_attribute2_treat1 = 0,
sigma_log_b_attribute2_treat1= 1,
mu_log_b_attribute2_treat2 = 0,
sigma_log_b_attribute2_treat2= 1,
mu_log_b_attribute3 = 0,
sigma_log_b_attribute3 = 1,
mu_log_b_price = 0,
sigma_log_b_price = 1,
lambda_attribute1_LV1=0,
lambda_attribute1_LV2=0,
lambda_attribute2_LV1=0,
lambda_attribute2_LV2=0,
lambda_attribute2_treat1_LV1=0,
lambda_attribute2_treat1_LV2=0,
lambda_attribute2_treat2_LV1=0,
lambda_attribute2_treat2_LV2=0,
lambda_attribute3_LV1=0,
lambda_attribute3_LV2=0,
b_gender_LV1 = 0,
b_age_LV1 = 0,
b_edu_2_LV1 = 0,
b_edu_3_LV1 = 0,
b_edu_4_LV1 = 0,
b_gender_ES = 0,
b_age_ES = 0,
b_edu_2_ES = 0,
b_edu_3_ES = 0,
b_edu_4_ES = 0,
zeta_LV1_1 = 0,
zeta_LV1_2 = 0,
zeta_LV1_3 = 0,
zeta_LV1_4 = 0,
zeta_LV2_1 = 0,
zeta_LV2_2 = 0,
zeta_LV2_3 = 0,
zeta_LV2_4 = 0,
sigma_LV1_1 = 1,
sigma_LV1_2 = 1,
sigma_LV1_3 = 1,
sigma_LV1_4 = 1,
sigma_LV2_1 = 1,
sigma_LV2_2 = 1,
sigma_LV2_3 = 1,
sigma_LV2_4 = 1,
)
### Vector with names (in quotes) of parameters to be kept fixed at their starting value in apollo_beta, use apollo_beta_fixed = c() if none
apollo_fixed = c('asc_a')
apollo_draws = list(
interDrawsType="halton",
interNDraws=100,
interUnifDraws=c(),
interNormDraws=c('eta01','eta02','eta03','eta04',
"eta1", 'eta2'),
intraDrawsType="",
intraNDraws=0,
intraUnifDraws=c(),
intraNormDraws=c()
)
apollo_randCoeff=function(apollo_beta, apollo_inputs){
randcoeff = list()
randcoeff[["LV1"]] =
eta1 +
b_gender_LV1 * gender +
b_age_LV1 * age +
b_edu_2_LV1 * edu_2 +
b_edu_3_LV1 * edu_3 +
b_edu_4_LV1 * edu_4
randcoeff[["LV2"]] =
eta2 +
b_gender_LV2 * gender +
b_age_LV2 * age +
b_edu_2_LV2 * edu_2 +
b_edu_3_LV2 * edu_3 +
b_edu_4_LV2 * edu_4
randcoeff[["attribute1coef"]]=mu_log_b_attribute1 + sigma_log_b_attribute1 * eta01
randcoeff[["attribute2coef"]]=mu_log_b_attribute2 + sigma_log_b_attribute2 * eta02
randcoeff[["attribute2coef_treat1"]]=mu_log_b_attribute2_treat1 + sigma_log_b_attribute2_treat1 * eta02
randcoeff[["attribute2coef_treat2"]]=mu_log_b_attribute2_treat2 + sigma_log_b_attribute2_treat2 * eta02
randcoeff[["attribute3coef"]]=mu_log_b_attribute3 + sigma_log_b_attribute3 * eta03
randcoeff[["pricecoef"]]=-exp(mu_log_b_price + sigma_log_b_price * eta04)
return(randcoeff)
}
# ################################################################# #
#### GROUP AND VALIDATE INPUTS ####
# ################################################################# #
apollo_inputs = apollo_validateInputs()
# ################################################################# #
#### DEFINE MODEL AND LIKELIHOOD FUNCTION ####
# ################################################################# #
apollo_probabilities=function(apollo_beta, apollo_inputs, functionality="estimate"){
apollo_attach(apollo_beta, apollo_inputs)
on.exit(apollo_detach(apollo_beta, apollo_inputs))
P = list()
normalDensity_settings1 = list(
outcomeNormal = LV1_1,
xNormal = zeta_LV1_1 * LV1,
mu = 0,
sigma = sigma_LV1_1,
componentName = "indic_LV1_1")
normalDensity_settings2 = list(
outcomeNormal = LV1_2,
xNormal = zeta_LV1_2 * LV1,
mu = 0,
sigma = sigma_LV1_2,
componentName = "indic_LV1_2")
normalDensity_settings3 = list(
outcomeNormal = LV1_3,
xNormal = zeta_LV1_3 * LV1,
mu = 0,
sigma = sigma_LV1_3,
componentName = "indic_LV1_3")
normalDensity_settings4 = list(
outcomeNormal = LV1_4,
xNormal = zeta_LV1_4 * LV1,
mu = 0,
sigma = sigma_LV1_4,
componentName = "indic_LV1_4")
normalDensity_settings21 = list(
outcomeNormal = LV2_1,
xNormal = zeta_LV2_1 * LV2,
mu = 0,
sigma = sigma_LV2_1,
componentName = "indic_LV2_1")
normalDensity_settings22 = list(
outcomeNormal = LV2_2,
xNormal = zeta_LV2_2 * LV2,
mu = 0,
sigma = sigma_LV2_2,
componentName = "indic_LV2_2")
normalDensity_settings23 = list(
outcomeNormal = LV2_3,
xNormal = zeta_LV2_3 * LV2,
mu = 0,
sigma = sigma_LV2_3,
componentName = "indic_LV2_3")
normalDensity_settings24 = list(
outcomeNormal = LV2_4,
xNormal = zeta_LV2_4 * LV2,
mu = 0,
sigma = sigma_LV2_4,
componentName = "indic_LV2_4")
P[["indic_LV1_1"]] = apollo_normalDensity(normalDensity_settings1, functionality)
P[["indic_LV1_2"]] = apollo_normalDensity(normalDensity_settings2, functionality)
P[["indic_LV1_3"]] = apollo_normalDensity(normalDensity_settings3, functionality)
P[["indic_LV1_4"]] = apollo_normalDensity(normalDensity_settings4, functionality)
P[["indic_LV2_1"]] = apollo_normalDensity(normalDensity_settings21, functionality)
P[["indic_LV2_2"]] = apollo_normalDensity(normalDensity_settings22, functionality)
P[["indic_LV2_3"]] = apollo_normalDensity(normalDensity_settings23, functionality)
P[["indic_LV2_4"]] = apollo_normalDensity(normalDensity_settings24, functionality)
attribute1coefLV = attribute1coef + lambda_attribute1_LV1 * LV1 + lambda_attribute1_LV2 * LV2
attribute2coefLV = attribute2coef + lambda_attribute2_LV1 * LV1 + lambda_attribute2_LV2 * LV2
attribute3coefLV = attribute3coef + lambda_attribute3_LV1 * LV1 + lambda_attribute3_LV2 * LV2
attribute2coef_treat1LV = attribute2coef_treat1 + lambda_attribute2_treat1_LV1 * LV1 + lambda_attribute2_treat1_LV2 * LV2
attribute2coef_treat2LV = attribute2coef_treat2 + lambda_attribute2_treat2_LV1 * LV1 + lambda_attribute2_treat2_LV2 * LV2
V = list()
V[['a']] = asc_a +
attribute2coefLV * attribute2A +
attribute2coef_treat1LV * attribute2A * treatment1+
attribute2coef_treat2LV * attribute2A * treatment2+
attribute1coefLV * attribute1A +
attribute3coefLV * attribute3A +
pricecoef * priceA
V[['b']] = asc_b +
attribute2coefLV * attribute2B+
attribute2coef_treat1LV * attribute2B * treatment1 +
attribute2coef_treat2LV * attribute2B * treatment2 +
attribute1coefLV * attribute1B +
attribute3coefLV * attribute3B +
pricecoef * priceB
V[['c']] = asc_c +
attribute2coefLV * attribute2C+
attribute2coef_treat1LV * attribute2C * treatment1 +
attribute2coef_treat2LV * attribute2C * treatment2 +
attribute1coefLV * attribute1C +
attribute3coefLV * attribute3C +
pricecoef * priceC
mnl_settings = list(
alternatives = c(a = 'a', b = 'b', c = 'c'),
# avail = list(Q1=1, N2=1, Q3=1),
choiceVar = choice,
utilities = V,
componentName = "choice"
)
### Compute probabilities for MNL model component
P[["choice"]] = apollo_mnl(mnl_settings, functionality)
### Likelihood of the whole model
P = apollo_combineModels(P, apollo_inputs, functionality)
### Take product across observation for same individual
P = apollo_panelProd(P, apollo_inputs, functionality)
### Average across inter-individual draws
P = apollo_avgInterDraws(P, apollo_inputs, functionality)
### Prepare and return outputs of function
P = apollo_prepareProb(P, apollo_inputs, functionality)
return(P)
}
model = apollo_estimate(apollo_beta, apollo_fixed, apollo_probabilities, apollo_inputs,
estimate_settings = list(estimationRoutine = "bfgs",
maxIterations = 1000))
I am building Mixed Logit Model allowed the inclusion of latent variables. I am interested in understanding whether the two external treatments influence consumers' preferences for attribute2 therefore the interaction term attribute2 * treatment is included, and how individual socio-demographic characteristics and attitudes impact the effects of these treatments.
Specifically, I draw on Hao et al.'s paper (See Hao, F., Qiu, R. T. R., Park, J., & Chon, K. (2023). The Myth of Contactless Hospitality Service: Customers’ Willingness to Pay. Journal of Hospitality & Tourism Research, 47(8), 1478-1502. https://doi.org/10.1177/10963480221081781), where the preference parameter is specified as a function of latent values, which differs from the approach in the Apollo manual (where personal characteristics and latent variables are directly incorporated into the utility function).
Below is my code, though I am unsure if it achieves my aim. Additionally, after implementing this, I don't how to estimate willingness to pay, can you advise accordingly? Many thanks.
# Mixed logit with latent variable and socio-demographic ###
apollo_beta = c(
asc_a = 0,
asc_b = 0,
asc_c = 0,
mu_log_b_attribute1 = 0,
sigma_log_b_attribute1= 1,
mu_log_b_attribute2 = 0,
sigma_log_b_attribute2 = 1,
mu_log_b_attribute2_treat1 = 0,
sigma_log_b_attribute2_treat1= 1,
mu_log_b_attribute2_treat2 = 0,
sigma_log_b_attribute2_treat2= 1,
mu_log_b_attribute3 = 0,
sigma_log_b_attribute3 = 1,
mu_log_b_price = 0,
sigma_log_b_price = 1,
lambda_attribute1_LV1=0,
lambda_attribute1_LV2=0,
lambda_attribute2_LV1=0,
lambda_attribute2_LV2=0,
lambda_attribute2_treat1_LV1=0,
lambda_attribute2_treat1_LV2=0,
lambda_attribute2_treat2_LV1=0,
lambda_attribute2_treat2_LV2=0,
lambda_attribute3_LV1=0,
lambda_attribute3_LV2=0,
b_gender_LV1 = 0,
b_age_LV1 = 0,
b_edu_2_LV1 = 0,
b_edu_3_LV1 = 0,
b_edu_4_LV1 = 0,
b_gender_ES = 0,
b_age_ES = 0,
b_edu_2_ES = 0,
b_edu_3_ES = 0,
b_edu_4_ES = 0,
zeta_LV1_1 = 0,
zeta_LV1_2 = 0,
zeta_LV1_3 = 0,
zeta_LV1_4 = 0,
zeta_LV2_1 = 0,
zeta_LV2_2 = 0,
zeta_LV2_3 = 0,
zeta_LV2_4 = 0,
sigma_LV1_1 = 1,
sigma_LV1_2 = 1,
sigma_LV1_3 = 1,
sigma_LV1_4 = 1,
sigma_LV2_1 = 1,
sigma_LV2_2 = 1,
sigma_LV2_3 = 1,
sigma_LV2_4 = 1,
)
### Vector with names (in quotes) of parameters to be kept fixed at their starting value in apollo_beta, use apollo_beta_fixed = c() if none
apollo_fixed = c('asc_a')
apollo_draws = list(
interDrawsType="halton",
interNDraws=100,
interUnifDraws=c(),
interNormDraws=c('eta01','eta02','eta03','eta04',
"eta1", 'eta2'),
intraDrawsType="",
intraNDraws=0,
intraUnifDraws=c(),
intraNormDraws=c()
)
apollo_randCoeff=function(apollo_beta, apollo_inputs){
randcoeff = list()
randcoeff[["LV1"]] =
eta1 +
b_gender_LV1 * gender +
b_age_LV1 * age +
b_edu_2_LV1 * edu_2 +
b_edu_3_LV1 * edu_3 +
b_edu_4_LV1 * edu_4
randcoeff[["LV2"]] =
eta2 +
b_gender_LV2 * gender +
b_age_LV2 * age +
b_edu_2_LV2 * edu_2 +
b_edu_3_LV2 * edu_3 +
b_edu_4_LV2 * edu_4
randcoeff[["attribute1coef"]]=mu_log_b_attribute1 + sigma_log_b_attribute1 * eta01
randcoeff[["attribute2coef"]]=mu_log_b_attribute2 + sigma_log_b_attribute2 * eta02
randcoeff[["attribute2coef_treat1"]]=mu_log_b_attribute2_treat1 + sigma_log_b_attribute2_treat1 * eta02
randcoeff[["attribute2coef_treat2"]]=mu_log_b_attribute2_treat2 + sigma_log_b_attribute2_treat2 * eta02
randcoeff[["attribute3coef"]]=mu_log_b_attribute3 + sigma_log_b_attribute3 * eta03
randcoeff[["pricecoef"]]=-exp(mu_log_b_price + sigma_log_b_price * eta04)
return(randcoeff)
}
# ################################################################# #
#### GROUP AND VALIDATE INPUTS ####
# ################################################################# #
apollo_inputs = apollo_validateInputs()
# ################################################################# #
#### DEFINE MODEL AND LIKELIHOOD FUNCTION ####
# ################################################################# #
apollo_probabilities=function(apollo_beta, apollo_inputs, functionality="estimate"){
apollo_attach(apollo_beta, apollo_inputs)
on.exit(apollo_detach(apollo_beta, apollo_inputs))
P = list()
normalDensity_settings1 = list(
outcomeNormal = LV1_1,
xNormal = zeta_LV1_1 * LV1,
mu = 0,
sigma = sigma_LV1_1,
componentName = "indic_LV1_1")
normalDensity_settings2 = list(
outcomeNormal = LV1_2,
xNormal = zeta_LV1_2 * LV1,
mu = 0,
sigma = sigma_LV1_2,
componentName = "indic_LV1_2")
normalDensity_settings3 = list(
outcomeNormal = LV1_3,
xNormal = zeta_LV1_3 * LV1,
mu = 0,
sigma = sigma_LV1_3,
componentName = "indic_LV1_3")
normalDensity_settings4 = list(
outcomeNormal = LV1_4,
xNormal = zeta_LV1_4 * LV1,
mu = 0,
sigma = sigma_LV1_4,
componentName = "indic_LV1_4")
normalDensity_settings21 = list(
outcomeNormal = LV2_1,
xNormal = zeta_LV2_1 * LV2,
mu = 0,
sigma = sigma_LV2_1,
componentName = "indic_LV2_1")
normalDensity_settings22 = list(
outcomeNormal = LV2_2,
xNormal = zeta_LV2_2 * LV2,
mu = 0,
sigma = sigma_LV2_2,
componentName = "indic_LV2_2")
normalDensity_settings23 = list(
outcomeNormal = LV2_3,
xNormal = zeta_LV2_3 * LV2,
mu = 0,
sigma = sigma_LV2_3,
componentName = "indic_LV2_3")
normalDensity_settings24 = list(
outcomeNormal = LV2_4,
xNormal = zeta_LV2_4 * LV2,
mu = 0,
sigma = sigma_LV2_4,
componentName = "indic_LV2_4")
P[["indic_LV1_1"]] = apollo_normalDensity(normalDensity_settings1, functionality)
P[["indic_LV1_2"]] = apollo_normalDensity(normalDensity_settings2, functionality)
P[["indic_LV1_3"]] = apollo_normalDensity(normalDensity_settings3, functionality)
P[["indic_LV1_4"]] = apollo_normalDensity(normalDensity_settings4, functionality)
P[["indic_LV2_1"]] = apollo_normalDensity(normalDensity_settings21, functionality)
P[["indic_LV2_2"]] = apollo_normalDensity(normalDensity_settings22, functionality)
P[["indic_LV2_3"]] = apollo_normalDensity(normalDensity_settings23, functionality)
P[["indic_LV2_4"]] = apollo_normalDensity(normalDensity_settings24, functionality)
attribute1coefLV = attribute1coef + lambda_attribute1_LV1 * LV1 + lambda_attribute1_LV2 * LV2
attribute2coefLV = attribute2coef + lambda_attribute2_LV1 * LV1 + lambda_attribute2_LV2 * LV2
attribute3coefLV = attribute3coef + lambda_attribute3_LV1 * LV1 + lambda_attribute3_LV2 * LV2
attribute2coef_treat1LV = attribute2coef_treat1 + lambda_attribute2_treat1_LV1 * LV1 + lambda_attribute2_treat1_LV2 * LV2
attribute2coef_treat2LV = attribute2coef_treat2 + lambda_attribute2_treat2_LV1 * LV1 + lambda_attribute2_treat2_LV2 * LV2
V = list()
V[['a']] = asc_a +
attribute2coefLV * attribute2A +
attribute2coef_treat1LV * attribute2A * treatment1+
attribute2coef_treat2LV * attribute2A * treatment2+
attribute1coefLV * attribute1A +
attribute3coefLV * attribute3A +
pricecoef * priceA
V[['b']] = asc_b +
attribute2coefLV * attribute2B+
attribute2coef_treat1LV * attribute2B * treatment1 +
attribute2coef_treat2LV * attribute2B * treatment2 +
attribute1coefLV * attribute1B +
attribute3coefLV * attribute3B +
pricecoef * priceB
V[['c']] = asc_c +
attribute2coefLV * attribute2C+
attribute2coef_treat1LV * attribute2C * treatment1 +
attribute2coef_treat2LV * attribute2C * treatment2 +
attribute1coefLV * attribute1C +
attribute3coefLV * attribute3C +
pricecoef * priceC
mnl_settings = list(
alternatives = c(a = 'a', b = 'b', c = 'c'),
# avail = list(Q1=1, N2=1, Q3=1),
choiceVar = choice,
utilities = V,
componentName = "choice"
)
### Compute probabilities for MNL model component
P[["choice"]] = apollo_mnl(mnl_settings, functionality)
### Likelihood of the whole model
P = apollo_combineModels(P, apollo_inputs, functionality)
### Take product across observation for same individual
P = apollo_panelProd(P, apollo_inputs, functionality)
### Average across inter-individual draws
P = apollo_avgInterDraws(P, apollo_inputs, functionality)
### Prepare and return outputs of function
P = apollo_prepareProb(P, apollo_inputs, functionality)
return(P)
}
model = apollo_estimate(apollo_beta, apollo_fixed, apollo_probabilities, apollo_inputs,
estimate_settings = list(estimationRoutine = "bfgs",
maxIterations = 1000))