Important: Read this before posting to this forum

  1. This forum is for questions related to the use of Apollo. We will answer some general choice modelling questions too, where appropriate, and time permitting. We cannot answer questions about how to estimate choice models with other software packages.
  2. There is a very detailed manual for Apollo available at http://www.ApolloChoiceModelling.com/manual.html. This contains detailed descriptions of the various Apollo functions, and numerous examples are available at http://www.ApolloChoiceModelling.com/examples.html. In addition, help files are available for all functions, using e.g. ?apollo_mnl
  3. Before asking a question on the forum, users are kindly requested to follow these steps:
    1. Check that the same issue has not already been addressed in the forum - there is a search tool.
    2. Ensure that the correct syntax has been used. For any function, detailed instructions are available directly in Apollo, e.g. by using ?apollo_mnl for apollo_mnl
    3. Check the frequently asked questions section on the Apollo website, which discusses some common issues/failures. Please see http://www.apollochoicemodelling.com/faq.html
    4. Make sure that R is using the latest official release of Apollo.
  4. If the above steps do not resolve the issue, then users should follow these steps when posting a question:
    1. provide full details on the issue, including the entire code and output, including any error messages
    2. posts will not immediately appear on the forum, but will be checked by a moderator first. This may take a day or two at busy times. There is no need to submit the post multiple times.

WTP Space Mixed Logit with piecewise price parameter

Ask questions about model specifications. Ideally include a mathematical explanation of your proposed model.
Post Reply
sac111
Posts: 4
Joined: 17 Oct 2021, 17:21

WTP Space Mixed Logit with piecewise price parameter

Post by sac111 »

Dear Stephane,

Thank you for creating this package and the forum it has been really helpful so far.
I am trying to estimate a mixed logit model in the WTP space for a discrete choice experiment.
The price attribute is an expected change in bill in percentage points(this can be either positive negative or zero).
I would like to be able to capture the asymmetry between WTP and WTA so have used a piecewise linear transformation to split the bill attribute into a fee (bill change >=0) or discount (bill change <=0) following the method here:
S. Hess, J. M. Rose, and D. A. Hensher, “Asymmetric preference formation in willingness to pay estimates in discrete choice models,” Transp. Res. Part E Logist. Transp. Rev., vol. 44, no. 5, pp. 847–863, Sep. 2008, doi: 10.1016/j.tre.2007.06.002.https://linkinghub.elsevier.com/retriev ... 4507000786
This works fine for a mnl specification and there is a difference in the fee and discount parameters (mnl_models.png).

For the mixed logit specification I am using log-normal distributions for the fee and discount, and normal distributions for the non-monetary attributes. In the preference space the mixed logit results look fine. However the main aim of the study is to look at WTP/WTA and associated confidence intervals so would like to estimate the model directly in the WTP space.

I have tried two versions of specifying the utility functions in the WTP:
WTP_V1 - including the other price attribute within the WTP space (within the brackets). I end up with NaNs for the standard errors of some of the variables (WTP_V1_FEE.txt & WTP_V1_DISC.txt).
WTP_V2 - keeping the other price attribute outside the WTP space (outside the brackets). Works for the WTP estimation with plausible results but does not converge for the WTA estimation (WTP_V2_FEE.txt & WTP_V2_DISC.txt).

My question is first whether specification in the WTP space is the best way to go for this is analysis?
Secondly does WTP_V2 make sense?
Lastly are the convergence issues relating to the WTA estimates a problem with starting values?

I have included the model specifications and outputs for the preference space and the two WTP versions.

Thank you,
Saurab
Attachments
model_output.zip
(101.84 KiB) Downloaded 269 times
stephanehess
Site Admin
Posts: 974
Joined: 24 Apr 2020, 16:29

Re: WTP Space Mixed Logit with piecewise price parameter

Post by stephanehess »

Hi

personally, I would avoid WTP space in your case. As soon as you have multiple cost parameters, you would need to make a decision in relation to which cost component you express wtp, and then you'd still need post-estimation calculations to transform for other valuations (WTP vs WTA).

Also be mindful that if you use Normals for the non-price terms, then WTP space and preference space would be different anyway, as the WTP in the preference space model would be a ratio of normal over lognormal, which is different from a normal, which is what you'd have in WTP space.

If you're working in preference space, the model will be numerically easier too, so you can then also explore using semi-non-parametric transforms of the distribution

Stephane
--------------------------------
Stephane Hess
www.stephanehess.me.uk
sac111
Posts: 4
Joined: 17 Oct 2021, 17:21

Re: WTP Space Mixed Logit with piecewise price parameter

Post by sac111 »

Dear Stephane,

Thank you for the quick response!
I think I will stick to the preference space then and will look into the semi-non-parametric distributions.
I am assuming the best way to compute confidence intervals for the mean WTP/WTA would then be to use simulations (Krinsky-Robb method)?

Thank you,
Saurab
stephanehess
Site Admin
Posts: 974
Joined: 24 Apr 2020, 16:29

Re: WTP Space Mixed Logit with piecewise price parameter

Post by stephanehess »

Saurab

re the CIs, I would sample from the asymptotic variance covariance matrix for all parameters, including the covariances, and then each time simulate across the random distribution, and then calculate a confidence interval for the means

Stephane
--------------------------------
Stephane Hess
www.stephanehess.me.uk
sac111
Posts: 4
Joined: 17 Oct 2021, 17:21

Re: WTP Space Mixed Logit with piecewise price parameter

Post by sac111 »

Dear Stephane,

Thank you.
I m trying to implement a zero censored normal for some the attributes as a fairly large minority of respondents would be expected to be indifferent to them. I used the pmax() function in the randcoeff specification however I get the following warning when running apollo_estimate() (full script below).

Code: Select all

##############################################################################################################################################
This model could potentially be estimated faster using analytical gradients, yet some issue is preventing it from using them.
  You might want to ask for help in the Apollo forum (http://www.apollochoicemodelling.com/forum) on how to solve this issue.
  If you do, please post your code and data (if not confidential).
##############################################################################################################################################
The estimation is indeed very slow. Is there a better way to specify the censored normal (using an analytical expression) in apollo?

Thank you,

Saurab

Code: Select all

# ################################################################# #
#### LOAD LIBRARY AND DEFINE CORE SETTINGS                       ####
# ################################################################# #

### Clear memory
rm(list = ls())

### Load Apollo library
library(apollo)

### Initialise code
apollo_initialise()

### Set core controls
apollo_control = list(
  modelName  = "INT_MXL_PREF_CN",
  modelDescr ="MXL model with interaction, pref, ln-price and cn anon",
  indivID    ="ID",
  panelData = TRUE,
  mixing = TRUE,
  nCores = 8,
  seed = 423543
)

# ################################################################# #
#### LOAD DATA AND APPLY ANY TRANSFORMATIONS                     ####
# ################################################################# #
database = readRDS(here::here("data/formatted", "wide_data.rds"))
database <- subset(database, filt.trade == 1 & filt.manip2==1)

# ################################################################# #
#### DEFINE MODEL PARAMETERS                                     ####
# ################################################################# #
apollo_beta=c(
  mu.fee = -3,
  mu.disc = -3,
  mu.anon = 0,
  mu.hh = 0,
  mu.daily = 0,
  mu.anon.hh = 0,
  mu.anon.daily = 0,
  sg.fee = 0,
  sg.disc = 0,
  sg.anon = 0,
  sg.hh = 0,
  sg.daily = 0,
  sg.anon.hh = 0,
  sg.anon.daily = 0
  )
apollo_fixed = c() # Reference real-time

# ################################################################# #
#### DEFINE RANDOM COMPONENTS                                    ####
# ################################################################# #

### Set parameters for generating draws
apollo_draws = list(
  interDrawsType = "sobolOwenFaureTezuka",
  interNDraws    = 1000,
  interUnifDraws = c(),
  interNormDraws = c(
    "draws.fee",
    "draws.disc",
    "draws.anon",
    "draws.hh",
    "draws.daily",
    "draws.anon.hh",
    "draws.anon.daily"
  ),
  intraDrawsType = "sobol",
  intraNDraws    = 0,
  intraUnifDraws = c(),
  intraNormDraws = c()
)

### Create random parameters
apollo_randCoeff = function(apollo_beta, apollo_inputs){
  randcoeff = list()
  randcoeff[["a.fee"]] = -exp(mu.fee + sg.fee*draws.fee)
  randcoeff[["a.disc"]] =  exp(mu.disc + sg.disc*draws.disc)
  randcoeff[["b.anon"]] = pmax(mu.anon + sg.anon*draws.anon,0)
  randcoeff[["b.hh"]] =  pmax(mu.hh + sg.hh*draws.hh,0)
  randcoeff[["b.daily"]] =  pmax(mu.daily + sg.daily*draws.daily ,0)
  randcoeff[["b.anon.hh"]] =  mu.anon.hh + sg.anon.hh*draws.anon.hh
  randcoeff[["b.anon.daily"]] =  mu.anon.daily + sg.anon.daily*draws.anon.daily
  
  return(randcoeff)
}

# ################################################################# #
#### GROUP AND VALIDATE INPUTS                                   ####
# ################################################################# #

apollo_inputs = apollo_validateInputs()

# ################################################################# #
#### DEFINE MODEL AND LIKELIHOOD FUNCTION                        ####
# ################################################################# #

apollo_probabilities=function(apollo_beta, apollo_inputs, functionality="estimate"){
  
  ### Attach inputs and detach after function exit
  apollo_attach(apollo_beta, apollo_inputs)
  on.exit(apollo_detach(apollo_beta, apollo_inputs))
  P = list()
  V = list()
  V[['1']]  =  a.fee*(FeeP_1 + a.disc*DiscP_1 + b.anon*(Anon_1==1) +
    b.hh*(HH_1==1) + b.daily*(Daily_1==1) +
    b.anon.hh*(HH_1==1 & Anon_1==1) + b.anon.daily*(Daily_1==1 & Anon_1==1))
  V[['2']]  =  a.fee*(FeeP_2 + a.disc*DiscP_2 + b.anon*(Anon_2==1) +
    b.hh*(HH_2==1) + b.daily*(Daily_2==1) +
    b.anon.hh*(HH_2==1 & Anon_2==1) + b.anon.daily*(Daily_2==1 & Anon_2==1))

  mnl_settings = list(
    alternatives  = c("1"=1, "2"=2),
    avail         = list("1"=av_1, "2"=av_2), 
    choiceVar     = choice,
    V             = V
  )
  
  ### Compute probabilities using MNL model
  P[['model']] = apollo_mnl(mnl_settings, 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 ESTIMATION                                            ####
# ################################################################# #
estimate_settings <- list(
  # scaling = c(mu.fee = 20, mu.disc = 20)
)
model = apollo_estimate(
  apollo_beta,
  apollo_fixed,
  apollo_probabilities,
  apollo_inputs,
  estimate_settings
)

# ################################################################# #
#### MODEL OUTPUTS                                               ####
# ################################################################# #
modelOutput_settings = list(printPVal=2)
apollo_modelOutput(model,modelOutput_settings)
dpalma
Posts: 190
Joined: 24 Apr 2020, 17:54

Re: WTP Space Mixed Logit with piecewise price parameter

Post by dpalma »

Hi Saurab,

We are not sure what is preventing the model from using analytical gradients. It could be a number of things. If you can share the data (or part of it) with us, it would help us figuring out what the problem is. If you are ok with sharing the data, you can either post it in the forum, or write an email do D.Palma [at] leeds.ac.uk

Cheers
David
sac111
Posts: 4
Joined: 17 Oct 2021, 17:21

Re: WTP Space Mixed Logit with piecewise price parameter

Post by sac111 »

Hi David,

I have emailed the dataset and full R script.

Thank you,

Saurab
dpalma
Posts: 190
Joined: 24 Apr 2020, 17:54

Re: WTP Space Mixed Logit with piecewise price parameter

Post by dpalma »

Hi Saurab,

Thanks for sending the data and script.

I managed to run your code using analytical gradient, but with a few caveats:
  1. I used Apollo v0.2.6, which has just been released. To install it:
    1. If you are using MacOS or Linux: Simply type install.packages("apollo")
    2. If you are using Windows: Download the package from the webpage and the type install.packages("C:/pathToFile/apollo_0.2.6.zip", repos=NULL) to install it, where pathToFile needs to changed to the appropriate path.
  2. I had to change the draws type to MLHS. For some reason I am investigating, the sobolOwenFaureTezuka draws did not work.
  3. I changed the way you are generating the truncated normal. By simply taking the max(0, x), where x ~ N(0,1), you were generating a distribution with half its mass concentrated on 0. A truncated normal with only positive values, instead, should scale its mass to the right of zero so that it integrates to one. I changed your code to achieve that.
  4. You will probably get a warning about database being different in the global environment and inside apollo_inputs. We are investigating why this happens exactly, but it's nothing to worry about.
The updated code is below.

Code: Select all

# ################################################################# #
#### LOAD LIBRARY AND DEFINE CORE SETTINGS                       ####
# ################################################################# #

### Clear memory
rm(list = ls())

### Load Apollo library
library(apollo)

### Initialise code
apollo_initialise()

### Set core controls
apollo_control = list(
  modelName  = "INT_MXL_PREF_CN",
  modelDescr ="MXL model with interaction, pref, ln-price and cn anon",
  indivID    ="ID",
  panelData = TRUE,
  mixing = TRUE,
  nCores = 5,
  seed = 423543,
  debug=TRUE
)

# ################################################################# #
#### LOAD DATA AND APPLY ANY TRANSFORMATIONS                     ####
# ################################################################# #
database = readRDS("wide_data.rds")
database <- subset(database, filt.trade == 1 & filt.manip2==1)

# ################################################################# #
#### DEFINE MODEL PARAMETERS                                     ####
# ################################################################# #
apollo_beta=c(
  mu.fee        =-3,
  mu.disc       =-3,
  mu.anon       = 0,
  mu.hh         = 0,
  mu.daily      = 0,
  mu.anon.hh    = 0,
  mu.anon.daily = 0,
  sg.fee        = 0,
  sg.disc       = 0,
  sg.anon       = 0,
  sg.hh         = 0,
  sg.daily      = 0,
  sg.anon.hh    = 0,
  sg.anon.daily = 0
)
apollo_fixed = c() # Reference real-time

# ################################################################# #
#### DEFINE RANDOM COMPONENTS                                    ####
# ################################################################# #

### Set parameters for generating draws
apollo_draws = list(
  interDrawsType = "mlhs", #"sobolOwenFaureTezuka",
  interNDraws    = 1000,
  interUnifDraws = c("draws.anon","draws.hh","draws.daily"),
  interNormDraws = c("draws.fee","draws.disc","draws.anon.hh",
                     "draws.anon.daily")
)

### Create random parameters
apollo_randCoeff = function(apollo_beta, apollo_inputs){
  randcoeff = list()
  randcoeff[["a.fee"]]     = -exp(mu.fee + sg.fee*draws.fee)
  randcoeff[["a.disc"]]    =  exp(mu.disc + sg.disc*draws.disc)
  randcoeff[["b.anon"]]    =  mu.anon  + sg.anon *qnorm(0.5 + 0.5*draws.anon )
  randcoeff[["b.hh"]]      =  mu.hh    + sg.hh   *qnorm(0.5 + 0.5*draws.hh   )
  randcoeff[["b.daily"]]   =  mu.daily + sg.daily*qnorm(0.5 + 0.5*draws.daily)
  randcoeff[["b.anon.hh"]] =  mu.anon.hh + sg.anon.hh*draws.anon.hh
  randcoeff[["b.anon.daily"]] =  mu.anon.daily + sg.anon.daily*draws.anon.daily
  
  return(randcoeff)
}

# ################################################################# #
#### GROUP AND VALIDATE INPUTS                                   ####
# ################################################################# #

apollo_inputs = apollo_validateInputs()

# ################################################################# #
#### DEFINE MODEL AND LIKELIHOOD FUNCTION                        ####
# ################################################################# #

apollo_probabilities=function(apollo_beta, apollo_inputs, functionality="estimate"){
  
  ### Attach inputs and detach after function exit
  apollo_attach(apollo_beta, apollo_inputs)
  on.exit(apollo_detach(apollo_beta, apollo_inputs))
  P = list()
  V = list()
  V[['1']]  =  a.fee*FeeP_1 + a.disc*DiscP_1 + b.anon*(Anon_1==1) + 
    b.hh*(HH_1==1) + b.daily*(Daily_1==1) + b.anon.hh*(HH_1==1 & Anon_1==1) + 
    b.anon.daily*(Daily_1==1 & Anon_1==1)
  V[['2']]  =  a.fee*FeeP_2 + a.disc*DiscP_2 + b.anon*(Anon_2==1) + 
    b.hh*(HH_2==1) + b.daily*(Daily_2==1) + b.anon.hh*(HH_2==1 & Anon_2==1) + 
    b.anon.daily*(Daily_2==1 & Anon_2==1)
  
  mnl_settings = list(
    alternatives  = c("1"=1, "2"=2),
    avail         = list("1"=av_1, "2"=av_2), 
    choiceVar     = choice,
    V             = V
  )
  
  ### Compute probabilities using MNL model
  P[['model']] = apollo_mnl(mnl_settings, 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 ESTIMATION                                            ####
# ################################################################# #
#estimate_settings <- list(
  # scaling = c(mu.fee = 20, mu.disc = 20)
#)
model = apollo_estimate(
  apollo_beta,
  apollo_fixed,
  apollo_probabilities,
  apollo_inputs#,
  #estimate_settings
)

# ################################################################# #
#### MODEL OUTPUTS                                               ####
# ################################################################# #
modelOutput_settings = list(printPVal=2)
apollo_modelOutput(model,modelOutput_settings)
Cheers
David
Post Reply