Estimates a mixed logit model via simulated maximum likelihood.
Usage
run_mxlogit(
data = NULL,
id_col = NULL,
alt_col = NULL,
choice_col = NULL,
covariate_cols = NULL,
random_var_cols = NULL,
input_data = NULL,
eta_draws = NULL,
S = 100L,
rc_dist = NULL,
rc_mean = FALSE,
rc_correlation = FALSE,
use_asc = TRUE,
theta_init = NULL,
lower = NULL,
upper = NULL,
optimizer = NULL,
control = list(),
se_method = c("hessian", "bhhh", "sandwich", "cluster"),
scale_vars = c("bhhh", "none", "sd", "mad", "iqr"),
weights = NULL,
outside_opt_label = NULL,
include_outside_option = FALSE,
draws = c("store", "generate"),
seed = NULL,
scramble = c("permuted", "none", "owen"),
keep_data = TRUE,
nloptr_opts = NULL,
weights_col = NULL,
cluster_col = NULL,
person_col = NULL
)Arguments
- data
Data frame containing choice data (convenience workflow). Mutually exclusive with
input_data.- id_col
Name of the column identifying choice situations.
- alt_col
Name of the column identifying alternatives.
- choice_col
Name of the column indicating chosen alternative (1/0).
- covariate_cols
Vector of column names for fixed covariates. None may take a name the model gives its parameters, or prints for them in
summary():ASC_<label>,L_<i><j>andSigma_<i><j>, and withrc_mean = TRUEMu_<variable>and, for a log-normal coefficient,exp(Mu_<variable>).- random_var_cols
Vector of column names for random coefficients.
- input_data
List output from
prepare_mxl_data(advanced workflow). Mutually exclusive withdata.- eta_draws
Array of shape K_w x S x U with standard normal draws, one K_w x S block per likelihood unit: U is the number of decision makers when
input_datawas prepared withperson_col(length(input_data$Ti)), and the number of choice situations otherwise. Required for the advanced workflow; auto-generated fromSin the convenience workflow. Post-hoc methods (vcov(fit, type = ),wesml_vcov,conditional_tastes, prediction) regenerate the Halton drawsget_halton_normalsbuilds from the recorded draw count, so buildeta_drawswith it for them to reproduce the estimation draws.- S
Integer number of Halton draws per decision maker, or per choice situation without
person_col(convenience workflow only). Default 100.- rc_dist
Integer vector indicating distribution of random coefficients (0 = normal, 1 = log-normal). Default: all normal.
- rc_mean
Logical indicating whether to estimate means for random coefficients.
- rc_correlation
Logical indicating whether random coefficients are correlated (convenience workflow). Ignored when
input_datais used (taken from the prepared data).- use_asc
Logical indicating whether to include alternative-specific constants.
- theta_init
Initial parameter vector in natural-scale units. If
NULL(default), the start is in the columns' units: zero for the \(\beta\), \(\mu\) and ASC blocks and the Cholesky factor's off-diagonal entries; on its diagonal, \(L_{pp} = 1 / h_p\) for a normal random coefficient, where \(h_p\) is the typical step of its column of \(W\): the distance between its values for a column with two (a dummy: 1), the mean distance from a value that fills more than half of the column over the other rows (a mostly-zero column), and the standard deviation otherwise; a column with no variation starts at \(L_{pp} = 1\). At the start, a step of \(h_p\) in the column then moves the utility by a random amount with standard deviation 1, whatever the column's units of measurement. A log-normal coefficient starts at \(L_{pp} = 0.5\), the standard deviation of the logarithm of its random part, which has no units (that random part has median 1 in the column's units, so a log-normal covariate belongs in units where its coefficient is of order one). Where the default start falls outsidelower/upper, it is moved to the nearest bound, with a message. A suppliedtheta_initis used as given.- lower, upper
Optional parameter bounds for the optimizer, in natural-scale units (mapped to the optimizer's coordinates when
scale_vars != "none");lowermust not exceedupper, nor beInf(norupper-Inf). Each accepts three forms:NULL(default) Unbounded (
-Inf/Inf).- Unnamed numeric vector of length
n_params Full-length vector ordered exactly like
theta_init(the nloptr-native form).- Named numeric vector
Names must be a subset of the parameter names (\(\beta\) block: column names of
X; \(\mu\) block:Mu_<col>(ifrc_mean = TRUE); Cholesky block:L_<i><j>for \(i \ge j\); ASC block:ASC_<level>). Unlisted parameters default to \(\pm\infty\). This is the recommended form for typical use, e.g.lower = c(L_11 = -5, L_22 = -5)to clip Cholesky diagonals.
- optimizer
Optimizer to use:
"nloptr"(default),"optim", or a custom function. Seerun_mnlogitfor details.- control
List of optimizer-specific control parameters. With
scale_varsother than"none", settings in parameter units apply to the scaled coordinates.- se_method
Method for computing standard errors. One of
"hessian"(default) for the analytical Hessian of the simulated log-likelihood,"bhhh"for the BHHH/outer-product-of-gradients (OPG) estimator,"sandwich"for the robust (Huber-White) variance \(V = A^{-1} B A^{-1}\) (bread \(A\) = weighted negated Hessian, meat \(B\) = weight-squared OPG), or"cluster"for the cluster-robust sandwich (requirescluster_color a preparedinput_datawith aclusterfield). Use"sandwich"for valid inference under choice-based / WESML weighting, where the inverse-Hessian and ordinary BHHH are invalid; it reduces to the usual robust variance under uniform weights. BHHH needs no second derivatives and so costs less than the Hessian. For both, the work per decision maker involves only the parameters that enter that decision maker's likelihood (the fixed coefficients, the random coefficients' means and Cholesky factor, and the constants of the alternatives they faced), so it does not grow with the number of alternatives in the data; the parameters-by-parameters matrix and its inversion do. BHHH may underestimate standard errors in finite samples or away from the optimum. Any of these variances can also be recomputed post hoc viavcov(fit, type = ). Withoutperson_col, clustering repairs the inference, not the likelihood: the cross-sectional likelihood still treats each choice situation as an independent draw from the mixing distribution, and on panel data is a less efficient composite likelihood for the same taste distribution. Withperson_colthe likelihood unit is the decision maker, so scores are per decision maker and"sandwich"is already robust to dependence across a decision maker's choice situations;"cluster"is then needed only for coarser groups (e.g. households or markets) that nest decision makers.- scale_vars
How the optimizer's coordinates are scaled. The choice does not change the estimator; it changes how fast, and whether, the optimizer reaches the maximum.
"bhhh"(default) scales each parameter by the inverse square root of the diagonal of the BHHH (outer product of gradients) matrix at the start values, rounded to a power of two, from one extra gradient pass, so that the BHHH diagonal, an estimate of the curvature, is near one at the start values in every coordinate: it reaches the constants and the Cholesky factor, whose curvatures can differ by orders of magnitude that no column scale reaches (the constants of large and small alternatives, say). A start near the estimates (theta_init) gives it a better diagonal. Where the diagonal is unusable (no finite, positive entry, as when the likelihood overflows at the start values), the parameters are left unscaled, with a message."none"hands the optimizer the parameters as they are (the default in choicer 0.2.x). With"sd"(sample standard deviation),"mad"(stats::mad, i.e. 1.4826 \(\times\) median absolute deviation; SD-equivalent under normality) or"iqr"(stats::IQR(x) / 1.349; also SD-equivalent under normality), the optimizer works on the parameters the design would have with every column ofXandWdivided by the chosen scale; the constants keep their scale, the data are not divided, and the fit holds no scaled copy of them. Robust scales ("mad"/"iqr") better capture the bulk for heavy-tailed columns where SD is dominated by outliers, butstats::madcan return zero when more than half of a column's entries are identical (e.g., a sparse 0/1 dummy) and will then trigger the same near-constant-column error as"sd". The parameters of log-normal random coefficients (rc_dist == 1) keep their scale under these three, since the shifted log-normal parameterization does not admit a closed-form rescaling under multiplicative column scaling. With any choice but"none"(so by default), the optimizer, a custom one included, works in the scaled coordinates (theta_init,lowerandupperare given in natural units and mapped), so settings in parameter units incontrol(nloptr'sxtol_abs; optim'sparscale, andpgtol, which then applies to the scaled gradient) apply to the scaled coordinates;"none"keeps the parameters' units. Coefficients are reported in natural units and the standard errors computed in them (for any choice but"none", with the information matrix equilibrated before it is inverted), so, where the likelihood is well identified and every choice reaches its maximum, reported quantities do not depend on this choice beyond the optimizer's tolerance.- weights
Optional weight vector (convenience workflow), one weight per choice situation in ascending-id order (see
prepare_mxl_data). Withperson_col, situations are reordered by decision maker, so a positional vector is accepted only when that leaves the id order unchanged; otherwise supplyweights_col. IfNULL, equal weights are used. All weights must be finite and strictly positive. Withperson_colthey are decision-maker weights, constant within each decision maker: the objective is \(\sum_n w_n \log L_n\).- outside_opt_label
Label for the outside option (convenience workflow).
- include_outside_option
Logical whether to include an outside option (convenience workflow).
- draws
Draw storage mode. One of
"store"(default) or"generate"."store"pre-materializes the full \(K_w \times S \times U\) Halton cube (\(8 K_w S U\) bytes), one block per likelihood unit (Udecision makers withperson_col, choice situations otherwise; existing behavior, exact reproducibility). It supports at most \(2^{31} - 1\) points (\(S \times U\)).predict(),logsum(),consumer_surplus(),elasticities(),diversion_ratios(),blp()andgof()(hencesummary()'s fit statistics) integrate over one block per choice situation instead (seeget_halton_normals). With up to 128 random coefficients they form each block on the fly, in parallel, from the pointsget_halton_normals(S, N, K_w)would hold for the \(N\) choice situations, mapped by R'sqnorm(), so they never hold that cube and have no limit on the number of points; with more, they build the cube. Their draws, and so their results, are the cube's, bit for bit where randtoolbox and choicer are compiled with the same floating-point contraction, as on CRAN's platforms (otherwise some of the underlying uniforms differ in their last bit, which moves those draws by at most a few parts in \(10^9\), in the far tails); sums over choice situations are bit for bit at one thread, and at more threads vary in their last bits with the threads' order, as they always did.blp()may keep the draws it forms across its iterations, within itskeep_draws_bytes(4 GiB by default), with either storage mode. In the convenience workflow,run_mxlogit()warns before it builds an estimation cube above 1 GiB: the cube is held while the model is fitted, andvcov(type = ),wesml_vcovandconditional_tastesrebuild it whole."generate"computes each unit's draws on-the-fly in C++ from a stored seed, eliminating the O(U) cube; recommended for memory-constrained or large-N settings. Withscramble = "permuted", each base-\(b\) digit position in each dimension receives a seeded permutation shared across sequence indices. This is not Owen's nested-uniform scramble and does not carry standard randomized-QMC unbiasedness or error-estimation guarantees. Only supported in the convenience workflow.- seed
Integer master seed for the on-the-fly generator. Used only when
draws = "generate". IfNULL(default), a seed is drawn from R's RNG at call time (soset.seed()governs reproducibility). Ignored whendraws = "store".- scramble
Scrambling mode for on-the-fly Halton draws. One of
"permuted"(default) for seeded position-wise digit permutations or"none"for plain Halton (identity permutations). The historical value"owen"is accepted with a deprecation warning as an alias for"permuted"; the implementation is not Owen's nested-uniform scramble."none"uses the points of the randtoolbox sequence, mapped to normals by choicer's own inverse normal CDF, which is within about \(10^{-14}\) ofqnorm(): its draws agree with store mode's to that precision, not bit for bit. Simulation-draw sensitivity should be assessed by increasingSand, for"permuted", varyingseed. Used only whendraws = "generate".- keep_data
Logical. If
TRUE(default), stores prepared data in the returned object for post-estimation functions.- nloptr_opts
Deprecated. Use
optimizerandcontrolinstead.- weights_col
Optional name of a column in
dataholding a per-row weight (constant within each choice situation, finite and strictly positive). Mutually exclusive withweights; the recommended way to pass WESML weights fromsample_by_choice/wesml_weights, since alignment is by id rather than by position. Convenience workflow only. Ifdatacarries choice-based-sampling provenance (a"choice_sampling"attribute, as attached bysample_by_choice/wesml_weights) and neitherweightsnorweights_colis supplied, the recorded weight column is auto-detected and applied (with a message); if that column is absent the call errors rather than silently fitting an unweighted model under a WESML label. Withperson_colthe weight must also be constant within each decision maker, and WESML is not supported: choice-based weights vary with the chosen alternative across a decision maker's situations, so they cannot weight the panel likelihood.- cluster_col
Optional name of a column in
dataholding cluster labels for cluster-robust standard errors (e.g. a person id when the same decision maker contributes several choice situations to a cross-sectional fit). Must be constant within eachid_col, and within each decision maker whenperson_colis used (clusters must nest decision makers). Supplyingcluster_colwithout an explicitse_methodselectsse_method = "cluster".- person_col
Optional name of the column in
dataidentifying decision makers (respondents). When supplied, all choice situations of a decision maker share one draw of the random coefficients (panel likelihood);id_colmust still identify choice situations uniquely across the data set.NULL(default) makes each choice situation its own decision maker (cross-sectional likelihood). Convenience workflow only; in the advanced workflow passperson_coltoprepare_mxl_data.
Value
A choicer_mxl object (inherits from choicer_fit).
Standard S3 methods available: summary(), coef(),
vcov(), logLik(), AIC(), BIC(),
nobs(). A panel fit (person_col) also carries
n_persons, the number of decision makers; nobs() remains
the number of choice situations, which is also the \(n\) in BIC's
\(\log n\) penalty.
Details
Two workflows are supported:
- Convenience
Supply
dataand column names. Data preparation (prepare_mxl_data) and Halton draw generation (get_halton_normals) are handled automatically.- Advanced
Call
prepare_mxl_dataandget_halton_normalsyourself, then pass the results viainput_dataandeta_draws.
Cross-section vs panel. With person_col, each decision
maker \(n\) draws one taste vector \(\beta_n\) from the mixing
distribution \(f(\beta \mid \theta)\) and keeps it in all of their choice
situations \(t = 1, \dots, T_n\). The likelihood integrates the product
of their conditional logit probabilities (Revelt and Train 1998),
$$L_n(\theta) = \int \prod_{t=1}^{T_n} P_{nt}(j_{nt} \mid \beta)
f(\beta \mid \theta) \, d\beta,$$
simulated with S draws per decision maker, so repeated choices
speak to each decision maker's tastes, and
conditional_tastes recovers what they reveal. Without
person_col (the default), each choice situation is an independent
draw from the mixing distribution: \(L_i(\theta) = \int P_i(j_i \mid
\beta) f(\beta \mid \theta) \, d\beta\). That is the right model when tastes
are redrawn in every situation (or each decision maker chooses once). On
panel data with stable tastes it is a composite (pseudo-) likelihood: it
still targets the population mixing distribution, but it ignores the
within-person correlation of choices, so it is less efficient, its
standard errors must be clustered by decision maker (cluster_col),
and its log-likelihood, AIC and BIC are not comparable with those of the
panel fit.
Column names
No column the model uses (the id, alternative, choice, covariate, weight,
cluster, nest or decision-maker column) may have a name starting with
.choicer_, which choicer reserves for its working columns, and the
alternative column may not be named alt_int, N_OBS, N_CHOICES,
TAKE_RATE or MKT_SHARE, the names of the other columns of
alt_mapping. Either is an error that names the column. Columns the model
does not use may have any name.
References
Revelt, D. and Train, K. (1998). Mixed logit with repeated choices: households' choices of appliance efficiency level. Review of Economics and Statistics 80(4), 647-657.
See also
conditional_tastes, run_hmnlogit (the
hierarchical Bayes counterpart)
Examples
# \donttest{
library(data.table)
set.seed(42)
N <- 100; J <- 3
dt <- data.table(id = rep(1:N, each = J), alt = rep(1:J, N))
dt[, `:=`(x1 = rnorm(.N), w1 = rnorm(.N), w2 = rnorm(.N))]
#> id alt x1 w1 w2
#> <int> <int> <num> <num> <num>
#> 1: 1 1 1.37095845 -0.004620768 -0.2484829
#> 2: 1 2 -0.56469817 0.760242168 0.4223204
#> 3: 1 3 0.36312841 0.038990913 0.9876533
#> 4: 2 1 0.63286260 0.735072142 0.8355682
#> 5: 2 2 0.40426832 -0.146472627 -0.6605219
#> ---
#> 296: 99 2 -0.47733551 0.160327395 0.1704735
#> 297: 99 3 -0.16626149 -0.433641942 1.2006682
#> 298: 100 1 0.86256338 1.537412419 -0.1634059
#> 299: 100 2 0.09734049 -2.170246577 1.2824759
#> 300: 100 3 -1.62561674 1.027004619 2.7271964
dt[, choice := 0L]
#> id alt x1 w1 w2 choice
#> <int> <int> <num> <num> <num> <int>
#> 1: 1 1 1.37095845 -0.004620768 -0.2484829 0
#> 2: 1 2 -0.56469817 0.760242168 0.4223204 0
#> 3: 1 3 0.36312841 0.038990913 0.9876533 0
#> 4: 2 1 0.63286260 0.735072142 0.8355682 0
#> 5: 2 2 0.40426832 -0.146472627 -0.6605219 0
#> ---
#> 296: 99 2 -0.47733551 0.160327395 0.1704735 0
#> 297: 99 3 -0.16626149 -0.433641942 1.2006682 0
#> 298: 100 1 0.86256338 1.537412419 -0.1634059 0
#> 299: 100 2 0.09734049 -2.170246577 1.2824759 0
#> 300: 100 3 -1.62561674 1.027004619 2.7271964 0
dt[, choice := sample(c(1L, rep(0L, J - 1))), by = id]
#> id alt x1 w1 w2 choice
#> <int> <int> <num> <num> <num> <int>
#> 1: 1 1 1.37095845 -0.004620768 -0.2484829 0
#> 2: 1 2 -0.56469817 0.760242168 0.4223204 1
#> 3: 1 3 0.36312841 0.038990913 0.9876533 0
#> 4: 2 1 0.63286260 0.735072142 0.8355682 0
#> 5: 2 2 0.40426832 -0.146472627 -0.6605219 0
#> ---
#> 296: 99 2 -0.47733551 0.160327395 0.1704735 1
#> 297: 99 3 -0.16626149 -0.433641942 1.2006682 0
#> 298: 100 1 0.86256338 1.537412419 -0.1634059 0
#> 299: 100 2 0.09734049 -2.170246577 1.2824759 0
#> 300: 100 3 -1.62561674 1.027004619 2.7271964 1
fit <- run_mxlogit(
data = dt, id_col = "id", alt_col = "alt", choice_col = "choice",
covariate_cols = "x1", random_var_cols = c("w1", "w2"), S = 50L
)
#> Optimization run time 0h:0m:0.01s
summary(fit)
#> Mixed Logit (MXL) model
#>
#> Parameter Estimate Std.Error z-value Pr(>|z|)
#> x1 0.005704 0.121525 0.0469 9.63e-01
#> Sigma_11 0.000000 0.000000 0.0000 1.00e+00
#> Sigma_22 0.000000 0.000000 0.0000 1.00e+00
#> ASC_2 0.031083 0.248189 0.1252 9.00e-01
#> ASC_3 0.089236 0.244715 0.3647 7.15e-01
#> ---
#> Signif. codes: '***' 0.001 '**' 0.01 '*' 0.05
#>
#> Random coefficient covariance (Sigma):
#> w1 w2
#> w1 3.768717e-20 0.00000e+00
#> w2 0.000000e+00 1.04647e-17
#>
#> Std. Errors: Analytical Hessian
#> Log-likelihood: -109.79
#> AIC: 229.581 | BIC: 242.607
#> McFadden R2: 0.001 (adj: -0.045) | Hit rate: 0.350
#> N: 100 | Parameters: 5
#> Optimization time: 0.01 s
#> Convergence: 3 ( NLOPT_FTOL_REACHED: Optimization stopped because ftol_rel or ftol_abs (above) was reached. )
# }