Estimates a nested logit model via maximum likelihood.
Usage
run_nestlogit(
data = NULL,
id_col = NULL,
alt_col = NULL,
choice_col = NULL,
covariate_cols = NULL,
nest_col = NULL,
input_data = NULL,
use_asc = TRUE,
theta_init = NULL,
param_names = NULL,
optimizer = NULL,
control = list(),
weights = NULL,
weights_col = NULL,
outside_opt_label = NULL,
include_outside_option = FALSE,
keep_data = TRUE,
se_method = c("hessian", "numeric", "bhhh", "sandwich", "cluster"),
cluster_col = NULL,
nloptr_opts = NULL,
scale_vars = c("none", "sd", "mad", "iqr", "bhhh")
)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 covariates. None may be named like a generated parameter:
Lambda_<k>, orASC_<label>withuse_asc = TRUE.- nest_col
Name of the column mapping each alternative to its nest (convenience workflow).
- input_data
List containing prepared input data for estimation (advanced workflow). Mutually exclusive with
data.- use_asc
Logical indicating whether to include alternative specific constants (ASCs).
- theta_init
Optional initial parameter vector in natural units, ordered as the coefficients, the nest parameters (
Lambda_<k>, one per nest of two or more alternatives) and the constants. IfNULL: 0 for the coefficients and constants, 0.5 for each nest parameter.- param_names
Optional vector of parameter names, which must be unique. If
NULL, default names are generated.- 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.- weights
Optional weight vector (convenience workflow). If
NULL, equal weights are used. All weights must be finite and strictly positive.- weights_col
Optional name of a column in
dataholding per-row weights (convenience workflow only). The column must be constant within eachid_col(one weight per choice situation) and is collapsed accordingly. Mutually exclusive withweights. All weights must be finite and strictly positive. Used for choice-based / WESML weighting; pair withse_method = "sandwich"for valid inference.- outside_opt_label
Label for the outside option (convenience workflow).
- include_outside_option
Logical whether to include an outside option (convenience workflow).
- keep_data
Logical. If
TRUE(default), stores prepared data in the returned object for post-estimation functions.- se_method
Method for computing standard errors:
"hessian"(default, analytical Hessian vianl_loglik_hessian_parallel),"numeric"(finite-difference oracle vianl_loglik_numeric_hessian),"bhhh"(outer product of gradients vianl_bhhh_parallel),"sandwich"(robust Huber–White / WESML variance \(A^{-1} B A^{-1}\)), or"cluster"(cluster-robust sandwich; requirescluster_color a preparedinput_datawith aclusterfield). Use"sandwich"under choice-based / WESML weighting. Any of these can also be recomputed post hoc viavcov(fit, type = ).- 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). Must be constant within eachid_col. Supplyingcluster_colwithout an explicitse_methodselectsse_method = "cluster".- nloptr_opts
Deprecated. Use
optimizerandcontrolinstead.- 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. One of
"none"(default),"sd"(sample standard deviation),"mad"(stats::mad),"iqr"(stats::IQR(x) / 1.349), or"bhhh". With"sd","mad"or"iqr", the optimizer works on the coefficientsXwould have with every column divided by the chosen scale (each coefficient times its column's scale), to improve the conditioning of its problem; the nest parameters and constants keep their scale, and the data are not divided."bhhh"scales every parameter, nest parameters included, 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. Its scales are fixed at the start values, so when the nest parameters end far from their start (0.5) they can mislead the optimizer, which then converges more slowly than without them; hence"none"is the default here. Where the diagonal is unusable (no finite, positive entry), the parameters are left unscaled, with a message. With any choice but"none", the optimizer, a custom one included, works in the scaled coordinates (theta_init, in natural units, and the nest parameters' lower bound of 1e-16 are 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.
Value
A choicer_nl object (inherits from choicer_fit).
Standard S3 methods available: summary(), coef(),
vcov(), logLik(), AIC(), BIC(),
nobs().
Details
Two workflows are supported:
- Convenience
Supply
dataand column names (includingnest_col). Data preparation (prepare_nl_data) is handled automatically.- Advanced
Call
prepare_nl_data(or build the input list manually) and pass it viainput_data.
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.
Examples
# \donttest{
library(data.table)
set.seed(42)
N <- 100; J <- 4
dt <- data.table(id = rep(1:N, each = J), alt = rep(1:J, N))
dt[, `:=`(x1 = rnorm(.N), x2 = rnorm(.N))]
#> id alt x1 x2
#> <int> <int> <num> <num>
#> 1: 1 1 1.3709584 1.33491259
#> 2: 1 2 -0.5646982 -0.86927176
#> 3: 1 3 0.3631284 0.05548695
#> 4: 1 4 0.6328626 0.04906691
#> 5: 2 1 0.4042683 -0.57835573
#> ---
#> 396: 99 4 1.0965134 -1.07287540
#> 397: 100 1 0.4420131 -2.29297143
#> 398: 100 2 0.2410163 -1.20720685
#> 399: 100 3 -0.2556077 0.11410943
#> 400: 100 4 0.9310329 -1.03329708
dt[, nest := ifelse(alt <= 2, "A", "B")]
#> id alt x1 x2 nest
#> <int> <int> <num> <num> <char>
#> 1: 1 1 1.3709584 1.33491259 A
#> 2: 1 2 -0.5646982 -0.86927176 A
#> 3: 1 3 0.3631284 0.05548695 B
#> 4: 1 4 0.6328626 0.04906691 B
#> 5: 2 1 0.4042683 -0.57835573 A
#> ---
#> 396: 99 4 1.0965134 -1.07287540 B
#> 397: 100 1 0.4420131 -2.29297143 A
#> 398: 100 2 0.2410163 -1.20720685 A
#> 399: 100 3 -0.2556077 0.11410943 B
#> 400: 100 4 0.9310329 -1.03329708 B
dt[, choice := 0L]
#> id alt x1 x2 nest choice
#> <int> <int> <num> <num> <char> <int>
#> 1: 1 1 1.3709584 1.33491259 A 0
#> 2: 1 2 -0.5646982 -0.86927176 A 0
#> 3: 1 3 0.3631284 0.05548695 B 0
#> 4: 1 4 0.6328626 0.04906691 B 0
#> 5: 2 1 0.4042683 -0.57835573 A 0
#> ---
#> 396: 99 4 1.0965134 -1.07287540 B 0
#> 397: 100 1 0.4420131 -2.29297143 A 0
#> 398: 100 2 0.2410163 -1.20720685 A 0
#> 399: 100 3 -0.2556077 0.11410943 B 0
#> 400: 100 4 0.9310329 -1.03329708 B 0
dt[, choice := sample(c(1L, rep(0L, J - 1))), by = id]
#> id alt x1 x2 nest choice
#> <int> <int> <num> <num> <char> <int>
#> 1: 1 1 1.3709584 1.33491259 A 1
#> 2: 1 2 -0.5646982 -0.86927176 A 0
#> 3: 1 3 0.3631284 0.05548695 B 0
#> 4: 1 4 0.6328626 0.04906691 B 0
#> 5: 2 1 0.4042683 -0.57835573 A 0
#> ---
#> 396: 99 4 1.0965134 -1.07287540 B 0
#> 397: 100 1 0.4420131 -2.29297143 A 0
#> 398: 100 2 0.2410163 -1.20720685 A 0
#> 399: 100 3 -0.2556077 0.11410943 B 1
#> 400: 100 4 0.9310329 -1.03329708 B 0
fit <- run_nestlogit(
data = dt, id_col = "id", alt_col = "alt", choice_col = "choice",
covariate_cols = c("x1", "x2"), nest_col = "nest"
)
#> Optimization run time 0h:0m:0s
summary(fit)
#> Nested Logit (NL) model
#>
#> Parameter Estimate Std.Error z-value Pr(>|z|)
#> x1 -0.135699 0.188113 -0.7214 4.71e-01
#> x2 0.295655 0.222668 1.3278 1.84e-01
#> Lambda_1 6.296863 23.550214 0.2674 7.89e-01
#> Lambda_2 1.640148 1.813470 0.9044 3.66e-01
#> ASC_2 -2.778324 10.488956 -0.2649 7.91e-01
#> ASC_3 2.033850 11.737696 0.1733 8.62e-01
#> ASC_4 1.520384 11.782281 0.1290 8.97e-01
#> ---
#> Signif. codes: '***' 0.001 '**' 0.01 '*' 0.05
#>
#> Std. Errors: Analytical Hessian
#> Log-likelihood: -134.492
#> AIC: 282.983 | BIC: 301.219
#> McFadden R2: 0.030 (adj: -0.021) | Hit rate: 0.380
#> N: 100 | Parameters: 7
#> Optimization time: 0 s
#> Convergence: 1 ( NLOPT_SUCCESS: Generic success return value. )
# }