--- title: "Location-varying gating for mixtures of quantile regressions" author: "Kailas Venkitasubramanian, University of North Carolina at Charlotte" output: rmarkdown::html_vignette: toc: true toc_depth: 2 vignette: > %\VignetteIndexEntry{Location-varying gating for mixtures of quantile regressions} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", message = FALSE, warning = FALSE, fig.width = 7, fig.height = 4.2, dpi = 150, fig.align = "center" ) set.seed(2026) ``` A finite mixture of quantile regressions splits the data into latent groups and fits a quantile regression in each (see the **mixqr** package). The mixing probabilities are usually a single set of constants. **mixqrgate** lets them depend on covariates through a multinomial-logit *gate*, and lets the gate change with the quantile level -- so membership itself can shift across the conditional distribution (Furno 2025). The contribution over Furno's reweighting heuristic is **inference**: the gate is the maximiser of the mixture Q-function, so it comes with standard errors. You can ask *whether* membership depends on a covariate, and *whether* it varies across the distribution, rather than eyeballing a curve. ```{r} library(mixqrgate) library(ggplot2) ``` ## A concomitant gate `sim_gate2()` simulates two components whose membership depends on a gating covariate `z`: `Pr(class 2 | z) = plogis(0 + 1.5 z)`. The components are quantile regressions of `y` on `x` with slopes -3 and +3. ```{r fit} d <- sim_gate2(n = 600, gamma = c(0, 1.5)) fit <- mixqrgate(y ~ x, data = d, gating = ~ z, G = 2, tau = 0.5, variance = "louis") summary(fit) ``` The component slopes are recovered (about -3 and +3), and the gate coefficient on `z` (component 2 vs. component 1) is positive and significant: higher `z` raises the odds of the second regime, as simulated. We used `variance = "louis"`, the Louis observed-information standard error that accounts for uncertainty about which observation belongs to which class (in simulations it reaches nominal coverage where the default sandwich SE, conditional on the fitted memberships, reaches only about 0.80). `variance = "stochEM"` is a multiple-imputation alternative. Setting `gating = ~1` recovers a constant gate and the ordinary `mixqr` fit. ## Does the gate vary with the quantile? With `vary_gating = "discrete"` the gate is fit separately at each quantile. The key point is that **each gate carries its own uncertainty** -- so the question "does membership vary across the distribution?" is answered with inference, not by reading a noisy curve. ```{r vary} dh <- sim_gate2(n = 1000, gamma = c(0, 1), sigma = c(1, 3), loc_vary = 2.5, het = TRUE) # location-coupled gate fitv <- mixqrgate(y ~ x, data = dh, gating = ~ z, G = 2, tau = c(0.1, 0.25, 0.5, 0.75, 0.9), vary_gating = "discrete") round(fitv$gate_prob, 3) ``` We draw the class-average gate probability at each $\tau$ with an uncertainty band (simulated from each gate's covariance), so the eye is not fooled by sampling noise. ```{r gateplot, fig.alt = "Class-average gate probability against the quantile level with uncertainty bands."} gate_band <- function(fit, comp = 2, R = 400) { do.call(rbind, lapply(seq_along(fit$tau_grid), function(g) { gam <- as.numeric(fit$gamma[, , g]); V <- fit$gate_vcov[[g]] L <- chol(V + 1e-8 * diag(nrow(V))) draws <- sapply(seq_len(R), function(r) { gd <- matrix(gam + as.numeric(crossprod(L, rnorm(length(gam)))), length(fit$znames)) mean(mixqrgate:::gate_predict(gd, fit$z)[, comp]) }) data.frame(tau = fit$tau_grid[g], prob = mean(draws), lo = quantile(draws, .025), hi = quantile(draws, .975)) })) } gb <- gate_band(fitv) ggplot(gb, aes(tau, prob)) + geom_ribbon(aes(ymin = lo, ymax = hi), fill = "#1b6ca8", alpha = 0.2) + geom_line(linewidth = 1.1, colour = "#1b6ca8") + geom_point(size = 2.4, colour = "#1b6ca8") + ylim(0, 1) + labs(x = expression(tau), y = "Class-average gate probability (component 2)", title = "Is the gate location-varying?", subtitle = "Point estimates per quantile, with simulated uncertainty bands") + theme_minimal(base_size = 12) ``` Read with its uncertainty, the gate drifts only modestly here, and the bands at neighbouring quantiles overlap -- the evidence for a location-varying gate in this sample is weak. That is the right answer to report: the per-quantile gates are fit independently and are genuinely noisy (the "classification ambiguity across $\tau$" of Wu & Yao 2016), and the method does not manufacture a trend. On data with strong location-varying mixing -- Furno's PISA example, where the best-performing class dominates the lower tail and the worst the upper -- the same machinery surfaces it, and the per-$\tau$ gate coefficients with their standard errors (`summary(fitv)`) let you test it formally. Borrowing strength across neighbouring $\tau$ with a smooth gate (a planned `vary_gating = "smooth"` mode) will sharpen this where the discrete fit is noisy. ## Notes * `method = "kde"` uses the Wu & Yao (2016) nonparametric error densities instead of the parametric asymmetric-Laplace path. Gate SEs there are not yet classification-aware; treat them as approximate. * The gating covariates may be the same as, overlap with, or be disjoint from the component-regression covariates. * `predict(fit, newdata, type = "prob", tau = 0.9)` returns the gate probabilities at a chosen quantile for new data; `confint(fit)` gives gate-coefficient intervals. ## References - Furno, M. (2025). Finite Mixture at Quantiles and Expectiles. *Journal of Risk and Financial Management* 18(4), 177. - Wu, Q. & Yao, W. (2016). Mixtures of quantile regressions. *Computational Statistics & Data Analysis* 93, 162–176. - Grün, B. & Leisch, F. (2008). FlexMix version 2. *Journal of Statistical Software* 28(4), 1–35.