--- title: "Quantile-based estimators with Qest" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Quantile-based estimators with Qest} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>", fig.width = 6, fig.height = 4) ``` ```{r setup} library(Qest) library(survival) ``` ## 1. The Q-estimator A parametric model can be described by its quantile function $Q(\tau \mid x, \theta)$. The Q-estimator of Sottile and Frumento (2022) minimizes the integrated loss of quantile regression, $$ L(\theta) = \sum_{i=1}^n \int_0^1 w(\tau)\,\rho_\tau\{y_i - Q(\tau \mid x_i, \theta)\}\,d\tau, \qquad \rho_\tau(u) = u\{\tau - I(u < 0)\}, $$ where $w(\tau) \ge 0$ is a weighting function. The estimator is consistent and asymptotically normal for any correctly specified model, and it is usually more robust than maximum likelihood; truncated weights such as $w(\tau) = I(\delta \le \tau \le 1 - \delta)$ (function `wtrunc()`) increase its robustness further. Censored and truncated data are handled by replacing the check function with its conditional expectation. ## 2. User-defined quantile functions `Q(theta, tau, data)` must return $Q(\tau \mid x_i, \theta)$ for each row of `data`, where `tau` is either a matrix (one row for each observation) or a vector. Here is a normal linear model. ```{r user} Qlinmod <- function(theta, tau, data) { X <- model.matrix(~ x1 + x2, data = data) qnorm(tau, c(X %*% theta[-1]), exp(theta[1])) } set.seed(1) n <- 200 dat <- data.frame(x1 = rnorm(n), x2 = runif(n, 0, 3)) dat$y <- 4 + dat$x1 + 2 * dat$x2 + rnorm(n) m1 <- Qest(y ~ x1 + x2, Q = Qlinmod, start = rep(NA, 4), data = dat) summary(m1) ``` The length of `start` defines the number of parameters; `NA` values are replaced by data-driven starting points (Algorithm 1 of the paper). The derivatives of `Q` with respect to $\theta$ are computed by central differences, and all integrals in $\tau$ on a grid of `ntau` points. The same model is available as a family (`Qnorm`) and, for complete data, as `Qlm()`, both with closed-form estimating equations. The user-defined version agrees with them: ```{r compare} m2 <- Qest(y ~ x1 + x2, Q = Qnorm, data = dat) m3 <- Qlm(y ~ x1 + x2, data = dat) cbind(user = coef(m1), Qnorm = coef(m2), Qlm = c(log(m3$sigma), coef(m3))) ``` ### Censored data With a `Surv()` response, right-censored and left-truncated data are allowed. A Weibull proportional hazards model: ```{r weibull} QWeibPH <- function(theta, tau, data) { shape <- exp(theta[1]) X <- model.matrix(~ x, data = data) qweibull(tau, shape = shape, scale = exp(-c(X %*% theta[-1]) / shape)) } dat <- data.frame(x = runif(n, 0, 3)) t <- QWeibPH(c(log(2), -0.5, 1), runif(n), dat) cens <- runif(n, 0, 2) dat$y <- pmin(t, cens) dat$d <- as.numeric(t <= cens) m4 <- Qest(Surv(y, d) ~ x, Q = QWeibPH, start = rep(NA, 3), data = dat) summary(m4) ``` If the model is correct, $F(y_i \mid x_i)$ is uniformly distributed: `plot()` compares its (Kaplan-Meier) distribution with the uniform one. ```{r plot} plot(m4) ``` ## 3. Families `Qnorm`, `Qgamma`, `Qpois` (a continuous version of the Poisson distribution, to be used with jittered counts $y + U$) and `Qunif` have analytical estimating equations and are much faster. ```{r families} x <- runif(n) yg <- rgamma(n, shape = 2, scale = exp(1 + x)) coef(Qest(yg ~ x, Q = Qgamma)) yp <- rpois(n, exp(1 + x)) + runif(n) coef(Qest(yp ~ x, Q = Qpois)) yu <- runif(n, 0, 1 + 2 * x) coef(Qest(yu ~ x, Q = Qunif(min = FALSE))) ``` `predict()` returns fitted quantiles or the fitted distribution function: ```{r predict} fg <- Qest(yg ~ x, Q = Qgamma) predict(fg, newdata = data.frame(x = c(0, 1)), p = c(0.1, 0.5, 0.9)) ``` ## 4. Robustness We contaminate 5% of a normal sample and compare maximum likelihood (least squares) with Q-estimators with and without truncated weights. ```{r robust} set.seed(2) x <- runif(n) y <- 1 + 2 * x + rnorm(n) out <- sample(n, 10) y[out] <- y[out] + 15 rbind(lm = coef(lm(y ~ x)), Qlm = coef(Qlm(y ~ x)), Qlm.trunc = coef(Qlm(y ~ x, wtau = wtrunc, delta.left = 0.1, delta.right = 0.1))) ``` ## 5. Proportional hazards models `Qcoxph()` fits a proportional hazards model with a piecewise linear baseline cumulative hazard, using closed-form estimating equations. ```{r cox} set.seed(3) dat <- data.frame(x = runif(n, 0, 3)) t <- rweibull(n, shape = 2, scale = (1 / exp(2 + 2 * dat$x))^(1 / 2)) cens <- runif(n, 0, 1) dat$y <- pmin(t, cens); dat$d <- as.numeric(t <= cens) fc <- Qcoxph(Surv(y, d) ~ x, data = dat) summary(fc) cbind(coxph = coef(coxph(Surv(y, d) ~ x, data = dat)), Qcoxph = coef(fc)) sf <- survfit(fc, newdata = data.frame(x = c(0.5, 1.5))) plot(sf, col = 1:2, conf.int = TRUE, xlab = "time", ylab = "survival") ``` ## 6. Numerical details * The grid of quantile orders has `ntau = 199` points (denser in the tails) in $[5 \cdot 10^{-4}, 1 - 5 \cdot 10^{-4}]$; the tails are integrated with rectangles. * The estimating equations are solved by Newton-Raphson with backtracking, with Levenberg-Marquardt steps when needed; the Jacobian is the exact derivative of the discretized equations. Standard errors use the sandwich formula $J^{-1}\Omega J^{-\top}$, where $J$ is not symmetric with censored or truncated data. * `Qest.control()` sets the tolerance, the maximum number of iterations and the step of the numerical derivatives. ## References Sottile, G. and Frumento, P. (2022). Robust estimation and regression with parametric quantile functions. *Computational Statistics and Data Analysis*, 171, 107471. doi:10.1016/j.csda.2022.107471