--- title: "Nested logit and grouped substitution" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Nested logit and grouped substitution} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>") options(digits = 4) ``` Nested logit groups alternatives into *nests* of close substitutes. Within a nest, alternatives share an unobserved component, so substitution is stronger inside a nest than across nests. Each nest has a positive dissimilarity parameter λ. The usual random-utility interpretation of nested logit focuses on `0 < λ <= 1`: λ = 1 gives the MNL limit, and smaller λ means tighter substitution inside the nest. choicer imposes λ > 0 rather than an upper bound. Estimates above one are mathematically computable but imply negative within-nest correlation and should be treated as evidence against the proposed nesting structure, not as the usual "close substitutes" interpretation. Think of nested logit as the **parsimonious middle ground** between the multinomial and mixed logits. It introduces genuine within-nest correlation in unobserved utility at the cost of just one extra parameter per nest, and it stays globally well-behaved and cheap to estimate (a closed-form GEV likelihood, no simulation). The price you pay is a strong prior: *you* must specify the nesting tree in advance, and the model only permits correlation within the nests you draw. When the right grouping is obvious from the application (travel modes, product categories), that is a defensible restriction; when it is not, the result can be sensitive to how you nest. A good nested-logit application therefore treats the tree as an economic assumption, not as a tuning parameter chosen after looking at fit statistics. See [Choosing among choice models](choicer.html#choosing-among-choice-models) for how this tradeoff compares with the alternatives. ```{r setup} library(choicer) set_num_threads(2) ``` ## Simulate a nested process `simulate_nl_data()` builds two nests of inside goods plus an outside option, with known dissimilarity parameters. ```{r sim} sim <- simulate_nl_data(N = 4000, seed = 1) sim ``` ## Fit Supply the nest membership column; choicer estimates the coefficients, the alternative-specific constants and the nest dissimilarity parameters jointly, using an analytical gradient and Hessian. ```{r fit} fit <- run_nestlogit( data = sim$data, id_col = "id", alt_col = "j", choice_col = "choice", covariate_cols = c("X", "W"), nest_col = "nest", use_asc = TRUE, include_outside_option = TRUE, outside_opt_label = 0L ) summary(fit) ``` ## Parameter recovery The `lambda` rows are the nest dissimilarity parameters — the part that is unique to nested logit. ```{r recovery} recovery_table(fit, sim$true_params) ``` ## Nest-consistent elasticities This is the payoff of nesting: cross-elasticities are larger for alternatives in the *same* nest than for alternatives in different nests. choicer's `elasticities()` respects the nest structure automatically. ```{r elast} elasticities(fit, elast_var = "W") diversion_ratios(fit) ``` Those substitution patterns are credible only to the extent that the nesting tree is credible. If plausible alternative trees imply materially different diversion or welfare conclusions, that sensitivity is part of the empirical result rather than a nuisance to hide. ## Share inversion with BLP `blp()` runs the Berry-Levinsohn-Pakes contraction to recover the mean utilities that reproduce a set of target shares — useful for calibration and demand estimation from aggregate data. For strongly-nested models a damping factor below 1 stabilizes the iteration. ```{r blp} target_shares <- predict(fit, type = "shares") head(blp(fit, target_shares, damping = 0.5)) ``` As always, `predict()`, `wtp()` and `consumer_surplus()` are available on the fitted object with the same syntax used throughout choicer.