Page 1 of 1

Mixed logit with latent variable and socio-demographic

Posted: 12 Dec 2024, 13:36
by Xxxxie
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))

Re: Mixed logit with latent variable and socio-demographic

Posted: 13 Dec 2024, 09:34
by stephanehess
Hi

in the online examples, we interact the LVs with the ASCs. It's the same principle as interacting them with betas that multiply attributes.

You can then either do that inside apollo_randCoeff, e.g.

randCoeff[["beta"]] = mu_beta + sigma_beta * draws_beta + tau_beta_LV1 * randcoeff[["LV1"]]

or inside apollo_probabilities. The above is probably easier.

Note that in my example above, I also have independent random heterogeneity to ensure that the model does not misassociate the impact of the LV. Have a look at https://www.sciencedirect.com/science/a ... 452030049X

Also, in your code, you refer to eta1 in the LV when that is not defined anywhere. I assume you mean eta01. But in the coefficients, you should then include the entire LV, not just the random part eta

Stephane