# Estimating Mixed Logit Models

This vignette demonstrates an example of how to use the logitr() function to estimate mixed logit (MXL) models with preference space and WTP space utility parameterizations.

# The data

This example uses the yogurt data set from Jain et al. (1994). The data set contains 2,412 choice observations from a series of yogurt purchases by a panel of 100 households in Springfield, Missouri, over a roughly two-year period. The data were collected by optical scanners and contain information about the price, brand, and a “feature” variable, which identifies whether a newspaper advertisement was shown to the customer. There are four brands of yogurt: Yoplait, Dannon, Weight Watchers, and Hiland, with market shares of 34%, 40%, 23% and 3%, respectively.

In the utility models described below, the data variables are represented as follows:

Symbol Variable
$$p$$ The price in US dollars.
$$x_{j}^{\mathrm{Feat}}$$ Dummy variable for whether the newspaper advertisement was shown to the customer.
$$x_{j}^{\mathrm{Hiland}}$$ Dummy variable for the “Highland” brand.
$$x_{j}^{\mathrm{Yoplait}}$$ Dummy variable for the “Yoplait” brand.
$$x_{j}^{\mathrm{Dannon}}$$ Dummy variable for the “Dannon” brand.

# Preference space model

This example will estimate the following mixed logit model in the preference space:

$$$u_{j} = \alpha p_{j} + \beta_1 x_{j}^{\mathrm{Feat}} + \beta_2 x_{j}^{\mathrm{Hiland}} + \beta_3 x_{j}^{\mathrm{Yoplait}} + \beta_4 x_{j}^{\mathrm{Dannon}} + \varepsilon_{j} \label{eq:mxlPrefExample}$$$

where the parameters $$\alpha$$, $$\beta_1$$, $$\beta_2$$, $$\beta_3$$, and $$\beta_4$$ have units of utility. In the example below, we will model $$\beta_1$$, $$\beta_2$$, $$\beta_3$$, and $$\beta_4$$ as normally distributed across the population. As a result, the model will estimate two parameters for each of these coefficients: a mean (parname_mu) and a standard deviation (parname_sigma).

To estimate the model, first load the logitr package:

library(logitr)

Estimate the model using the logitr() function:

mxl_pref <- logitr(
data       = yogurt,
choiceName = 'choice',
obsIDName  = 'obsID',
parNames   = c('price', 'feat', 'hiland', 'yoplait', 'dannon'),
randPars   = c(feat = 'n', hiland = 'n', yoplait = 'n', dannon = 'n'),
# You should run a multistart for MXL models since they are non-convex,
# but it can take a long time. Here I just use 5 starts for brevity:
options    = list(numMultiStarts = 5)
)
#> Running Multistart 1 of 5...
#> Running Multistart 2 of 5...
#> Running Multistart 3 of 5...
#> Running Multistart 4 of 5...
#> Running Multistart 5 of 5...
#> Done!

Print a summary of the results:

summary(mxl_pref)
#> =================================================
#> SUMMARY OF ALL MULTISTART RUNS:
#>
#>   run    logLik iterations status
#> 1   1 -2645.786         32      3
#> 2   2 -2645.707         34      3
#> 3   3 -2645.792         23      3
#> 4   4 -2645.785         37      3
#> 5   5 -2645.785         32      3
#> ---
#> Use statusCodes() to view the meaning of the status codes
#>
#> Below is the summary of run 2 of 5 multistart runs
#> (the run with the largest log-likelihood value)
#> =================================================
#> MODEL SUMMARY:
#>
#> Model Space:   Preference
#> Model Run:         2 of 5
#> Iterations:            34
#> Elapsed Time:   0h:0m:17s
#> Weights Used?:      FALSE
#>
#> Model Coefficients:
#>                Estimate StdError    tStat   pVal signif
#> price         -0.393542 0.026743 -14.7158 0.0000    ***
#> feat_mu        0.451993 0.204956   2.2053 0.0275      *
#> hiland_mu     -3.330309 0.176886 -18.8274 0.0000    ***
#> yoplait_mu     1.459630 0.098044  14.8875 0.0000    ***
#> dannon_mu      0.668941 0.165877   4.0327 0.0001    ***
#> feat_sigma     2.518184 0.518427   4.8574 0.0000    ***
#> hiland_sigma   0.030126 0.655151   0.0460 0.9633
#> yoplait_sigma  0.002743 0.295444   0.0093 0.9926
#> dannon_sigma   0.026812 0.887933   0.0302 0.9759
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#>
#> Model Fit Values:
#>
#> Log.Likelihood.         -2645.7072660
#> Null.Log.Likelihood.    -3343.7419990
#> AIC.                     5309.4145000
#> BIC.                     5361.5084000
#> Number.of.Observations.  2412.0000000
#>
#> Summary of 10k Draws for Random Coefficients:
#>                        Min.    1st Qu.     Median       Mean    3rd Qu.
#> feat (normal)    -9.3354677 -1.2482076  0.4500692  0.4484679  2.1477663
#> hiland (normal)  -3.4457012 -3.3506395 -3.3303186 -3.3303369 -3.3100042
#> yoplait (normal)  1.4490729  1.4577780  1.4596275  1.4596256  1.4614776
#> dannon (normal)   0.5666744  0.6508196  0.6688955  0.6688725  0.6869768
#>                        Max.
#> feat (normal)     9.5455575
#> hiland (normal)  -3.2233350
#> yoplait (normal)  1.4692148
#> dannon (normal)   0.7600028

The above summary table prints summaries of the estimated coefficients as well as a summary table of the distribution of 10,000 population draws for each normally-distributed model coefficient. The results show that the feat attribute has a significant standard deviation coefficient, suggesting that there is considerable heterogeneity across the population for this attribute. In contrast, the brand coefficients have small and insignificant standard deviation coefficients.

Compute the WTP implied from the preference space model:

wtp_mxl_pref <- wtp(mxl_pref, priceName = "price")
wtp_mxl_pref
#>                Estimate StdError    tStat   pVal signif
#> lambda         0.393542 0.026714  14.7319 0.0000    ***
#> feat_mu        1.148528 0.542814   2.1159 0.0345      *
#> hiland_mu     -8.462407 0.550438 -15.3740 0.0000    ***
#> yoplait_mu     3.708960 0.479558   7.7341 0.0000    ***
#> dannon_mu      1.699799 0.452152   3.7594 0.0002    ***
#> feat_sigma     6.398776 1.452274   4.4060 0.0000    ***
#> hiland_sigma   0.076552 1.676844   0.0457 0.9636
#> yoplait_sigma  0.006971 0.756063   0.0092 0.9926
#> dannon_sigma   0.068130 2.284655   0.0298 0.9762

# WTP space model

This example will estimate the following mixed logit model in the WTP space:

$$$u_{j} = \lambda ( \omega_1 x_{j}^{\mathrm{Feat}} + \omega_2 x_{j}^{\mathrm{Hiland}} + \omega_3 x_{j}^{\mathrm{Yoplait}} + \omega_4 x_{j}^{\mathrm{Dannon}} - p_{j}) + \varepsilon_{j} \label{eq:mnlWtpExample}$$$

where the parameters $$\omega_1$$, $$\omega_2$$, $$\omega_3$$, and $$\omega_4$$ have units of dollars and $$\lambda$$ is the scale parameter. In the example below, we will model $$\omega_1$$, $$\omega_2$$, $$\omega_3$$, and $$\omega_4$$ as normally distributed across the population. As a result, the model will estimate two parameters for each of these coefficients: a mean (parname_mu) and a standard deviation (parname_sigma).

To estimate the model, first load the logitr package:

library(logitr)

Estimate the model using the logitr() function:

# Extract the WTP computed from the preference space model
# to use as the initial starting values
startingValues <- wtp_mxl_pref\$Estimate

mxl_wtp <- logitr(
data       = yogurt,
choiceName = 'choice',
obsIDName  = 'obsID',
parNames   = c('feat', 'hiland', 'yoplait', 'dannon'),
priceName  = 'price',
randPars   = c(feat = 'n', hiland = 'n', yoplait = 'n', dannon = 'n'),
modelSpace = 'wtp',
options    = list(
# You should run a multistart for MXL models since they are non-convex,
# but it can take a long time. Here I just use 5 starts for brevity:
numMultiStarts = 5,
# Use the computed WTP from the preference space model as the starting
# values for the first run:
startVals = startingValues,
# Because the computed WTP from the preference space model has values
# as large as 8, I increase the boundaries of the random starting values:
startParBounds = c(-10, 10)))
#> Running Multistart 1 of 5...
#> NOTE: Using user-provided starting values for this run
#> Running Multistart 2 of 5...
#> Running Multistart 3 of 5...
#> Running Multistart 4 of 5...
#> Running Multistart 5 of 5...
#> Done!

Print a summary of the results:

summary(mxl_wtp)
#> =================================================
#> SUMMARY OF ALL MULTISTART RUNS:
#>
#>   run    logLik iterations status
#> 1   1 -2663.066         85      3
#> 2   2 -3343.742          1      1
#> 3   3 -2792.486         74     -1
#> 4   4 -2645.707        223      3
#> 5   5 -2773.808         89      3
#> ---
#> Use statusCodes() to view the meaning of the status codes
#>
#> Below is the summary of run 4 of 5 multistart runs
#> (the run with the largest log-likelihood value)
#> =================================================
#> MODEL SUMMARY:
#>
#> Model Space:   Willingness-to-Pay
#> Model Run:                 4 of 5
#> Iterations:                   223
#> Elapsed Time:           0h:2m:20s
#> Weights Used?:              FALSE
#>
#> Model Coefficients:
#>                Estimate StdError    tStat   pVal signif
#> lambda         0.393461 0.026710  14.7309 0.0000    ***
#> feat_mu        1.150828 0.536881   2.1435 0.0322      *
#> hiland_mu     -8.456990 0.541362 -15.6217 0.0000    ***
#> yoplait_mu     3.710111 0.170756  21.7276 0.0000    ***
#> dannon_mu      1.699060 0.426544   3.9833 0.0001    ***
#> feat_sigma     6.376141 1.328550   4.7993 0.0000    ***
#> hiland_sigma   0.072426 1.635049   0.0443 0.9647
#> yoplait_sigma  0.009802 0.750255   0.0131 0.9896
#> dannon_sigma   0.056342 2.239134   0.0252 0.9799
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#>
#> Model Fit Values:
#>
#> Log.Likelihood.         -2645.7071209
#> Null.Log.Likelihood.    -3343.7419990
#> AIC.                     5309.4142000
#> BIC.                     5361.5081000
#> Number.of.Observations.  2412.0000000
#>
#> Summary of 10k Draws for Random Coefficients:
#>                        Min.   1st Qu.    Median      Mean   3rd Qu.      Max.
#> feat (normal)    -23.631405 -3.154147  1.145956  1.141902  5.444592 24.176088
#> hiland (normal)   -8.734401 -8.505866 -8.457013 -8.457057 -8.408176 -8.199817
#> yoplait (normal)   3.672391  3.703495  3.710103  3.710096  3.716713  3.744358
#> dannon (normal)    1.484156  1.660979  1.698963  1.698915  1.736959  1.890416

If you want to compare the WTP from the two different model spaces, use the wtpCompare() function:

wtp_mxl_comparison <- wtpCompare(mxl_pref, mxl_wtp, priceName = 'price')
wtp_mxl_comparison
#>                       pref           wtp  difference
#> lambda            0.393542  3.934614e-01 -0.00008057
#> feat_mu           1.148528  1.150828e+00  0.00230045
#> hiland_mu        -8.462407 -8.456990e+00  0.00541739
#> yoplait_mu        3.708960  3.710111e+00  0.00115111
#> dannon_mu         1.699799  1.699060e+00 -0.00073916
#> feat_sigma        6.398776  6.376141e+00 -0.02263500
#> hiland_sigma      0.076552  7.242592e-02 -0.00412608
#> yoplait_sigma     0.006971  9.801947e-03  0.00283095
#> dannon_sigma      0.068130  5.634243e-02 -0.01178757
#> logLik        -2645.707266 -2.645707e+03  0.00014510

Note that the WTP will not necessarily be the same between preference space and WTP space MXL models. This is because the distributional assumptions in MXL models imply different distributions on WTP depending on the model space. See Train and Weeks (2005) and Sonnier, Ainslie, and Otter (2007) for details on this topic.

# References

Jain, Dipak C, Naufel J Vilcassim, and Pradeep K Chintagunta. 1994. “A Random-Coefficients Logit Brand-Choice Model Applied to Panel Data.” Journal of Business & Economic Statistics 12 (3): 317–28.
Sonnier, Garrett, Andrew Ainslie, and Thomas Otter. 2007. Heterogeneity distributions of willingness-to-pay in choice models.” Quant. Mark. Econ. 5 (3): 313–31. https://doi.org/10.1007/s11129-007-9024-6.
Train, Kenneth E., and Melvyn Weeks. 2005. Discrete Choice Models in Preference and Willingness-to-Pay Space.” In Appl. Simul. Methods Environ. Resour. Econ., 1–16.