| Title: | Selective Inference for the Randomized Lasso |
|---|---|
| Description: | Confidence intervals and p-values for parameters chosen by the (randomized) lasso, valid after selection. Implements the polyhedral approach of Lee, Sun, Sun and Taylor (2016) <doi:10.1214/15-AOS1371>, the randomized response of Tian and Taylor (2018) <doi:10.1214/17-AOS1564> and the exact randomized inference of Panigrahi, Fry and Taylor (2024) <doi:10.1093/biomet/asae019>, with the truncated bivariate normal computations of Liu (2023) <doi:10.48550/arXiv.2308.10346>. Includes inference for user-specified estimands, screening of inactive variables and problems fitted by 'glmnet'. Computations use a 'C++' backend with matrix-free linear operators for wide designs. |
| Authors: | Jonathan Taylor [aut, cre, cph], Kevin Fry [aut], Snigdha Panigrahi [aut], Sifan Liu [ctb, cph] (truncated bivariate normal computations, see inst/COPYRIGHTS) |
| Maintainer: | Jonathan Taylor <[email protected]> |
| License: | BSD_3_clause + file LICENSE |
| Version: | 0.1.0 |
| Built: | 2026-10-23 10:00:57 UTC |
| Source: | https://github.com/cran/lassoinf |
Classes exposed from the C++ backend through an Rcpp module;
construct them with new().
new(AffineConstraints, Z, Z_noisy, Sigma, Sigma_noise, scalar_noise = NaN)The randomized selection setup: full-data score Z, the noisy
score used for selection, and their covariances (matrices or
LinearOps; Sigma_noise may be NULL, in which case
scalar_noise scales Sigma). Methods
compute_contrast(v), compute_covariance_contrast(...) and
solve_contrast(...).
new(DiscreteFamily, sufficient_stat, weights, theta)Exponential family with reference measure
sum_j weights[j] delta(sufficient_stat[j]). Methods pdf,
cdf, ccdf, equal_tailed_interval,
get_theta, set_theta and get_partition.
new(TruncatedGaussian, estimate, sigma, smoothing_sigma, lower_bound, upper_bound, noisy_estimate, factor)Selection weight of a Gaussian estimate with randomized truncation;
method weight(x).
new(WeightedGaussianFamily, estimate, sigma, weight_fns, num_sd, num_grid)A Gaussian reweighted by the product of the functions in the list
weight_fns, discretized on num_grid points within
num_sd standard deviations. Methods
pvalue(null_value, alternative, basept) and
interval(basept, level).
grid <- seq(-5, 5, length.out = 201) fam <- new(DiscreteFamily, grid, dnorm(grid), 0) fam$cdf(0, 1, 0)grid <- seq(-5, 5, length.out = 201) fam <- new(DiscreteFamily, grid, dnorm(grid), 0) fam$cdf(0, 1, 0)
contrast_estimand(eta, offset): estimator
sum(eta * Z_full) + offset for the estimand sum(eta * E[Z_full]) + offset.
covariance_estimand(theta_hat, variance, score_cov): estimator given by its
value, Var(theta_hat) and Cov(Z_full, theta_hat); requires Sigma_noise.
contrast_estimand(eta, offset = 0) covariance_estimand(theta_hat, variance, score_cov) estimand_value(estimand, Z)contrast_estimand(eta, offset = 0) covariance_estimand(theta_hat, variance, score_cov) estimand_value(estimand, Z)
eta |
Contrast vector. |
offset |
Constant added to the estimator and the estimand. |
theta_hat |
Observed value of the estimator. |
variance |
Variance of the estimator. |
score_cov |
Covariance of |
estimand |
A contrast estimand. |
Z |
A score vector. |
contrast_estimand and covariance_estimand: estimand
specifications (lists of class ContrastEstimand and CovarianceEstimand),
for contrast_inference, custom_estimand_inference and
estimand_summary. estimand_value: the value of the estimator at Z.
e <- contrast_estimand(c(1, -1, 0), offset = 0.5) estimand_value(e, c(2, 1, 5))e <- contrast_estimand(c(1, -1, 0), offset = 0.5) estimand_value(e, c(2, 1, 5))
Inference for theta = sum(eta * E[Z_full]) + offset, as in
LassoInference$summary().
contrast_inference(selection, estimand, level = NULL, null_value = 0)contrast_inference(selection, estimand, level = NULL, null_value = 0)
selection |
A |
estimand |
A |
level |
Confidence level (default: the selection's level). |
null_value |
Null value for the p-value. |
A list with estimate, lower_conf, upper_conf,
p_value, variance and contrast.
set.seed(1) n <- 100; p <- 10 x <- matrix(rnorm(n * p), n, p) y <- x[, 1] - x[, 2] + rnorm(n) fit <- glmnet::glmnet(x, y, lambda = c(0.3, 0.2), control = list(thresh = 1e-14)) li <- glmnet_inference(fit, x, y, level = 0.9) # index 0 is the intercept # the sum of the coefficients of x1 and x2 in the selected model eta <- li$contrasts[["1"]] + li$contrasts[["2"]] contrast_inference(li, eta)[c("estimate", "lower_conf", "upper_conf", "p_value")]set.seed(1) n <- 100; p <- 10 x <- matrix(rnorm(n * p), n, p) y <- x[, 1] - x[, 2] + rnorm(n) fit <- glmnet::glmnet(x, y, lambda = c(0.3, 0.2), control = list(thresh = 1e-14)) li <- glmnet_inference(fit, x, y, level = 0.9) # index 0 is the intercept # the sum of the coefficients of x1 and x2 in the selected model eta <- li$contrasts[["1"]] + li$contrasts[["2"]] contrast_inference(li, eta)[c("estimate", "lower_conf", "upper_conf", "p_value")]
For (theta_hat, Z_full) jointly Gaussian with known
Var(theta_hat) and Cov(Z_full, theta_hat). Requires Sigma_noise;
with scalar_noise, express the estimand as a contrast.
custom_estimand_inference( selection, theta_hat, variance, score_cov, level = NULL, null_value = 0 )custom_estimand_inference( selection, theta_hat, variance, score_cov, level = NULL, null_value = 0 )
selection |
A |
theta_hat |
Observed value of the estimator. |
variance |
Variance of the estimator. |
score_cov |
Covariance of |
level |
Confidence level (default: the selection's level). |
null_value |
Null value for the p-value. |
A list as from contrast_inference: estimate, lower_conf,
upper_conf, p_value, variance and contrast.
set.seed(1) n <- 100; p <- 10 x <- matrix(rnorm(n * p), n, p) y <- x[, 1] - x[, 2] + rnorm(n) # randomized response y + omega with Var(omega) = kappa, sigma^2 = 1 known kappa <- 0.5 y_noisy <- y + rnorm(n, sd = sqrt(kappa)) Q <- crossprod(x); Z <- drop(crossprod(x, y)) lam <- 2 * sqrt(n) fit <- glmnet::glmnet(x, y_noisy, lambda = lam / n, intercept = FALSE, standardize = FALSE, control = list(thresh = 1e-14)) b <- as.numeric(coef(fit))[-1] G <- drop(crossprod(x, x %*% b - y_noisy)) li <- LassoInference$new(b, G, Q, rep(lam, p), NULL, NULL, Z, Q, kappa * Q, level = 0.9) # the estimator sum(eta * Z), specified by its variance and covariance with Z eta <- li$contrasts[[1]] custom_estimand_inference(li, sum(eta * Z), sum(eta * (Q %*% eta)), drop(Q %*% eta))[c("lower_conf", "upper_conf", "p_value")]set.seed(1) n <- 100; p <- 10 x <- matrix(rnorm(n * p), n, p) y <- x[, 1] - x[, 2] + rnorm(n) # randomized response y + omega with Var(omega) = kappa, sigma^2 = 1 known kappa <- 0.5 y_noisy <- y + rnorm(n, sd = sqrt(kappa)) Q <- crossprod(x); Z <- drop(crossprod(x, y)) lam <- 2 * sqrt(n) fit <- glmnet::glmnet(x, y_noisy, lambda = lam / n, intercept = FALSE, standardize = FALSE, control = list(thresh = 1e-14)) b <- as.numeric(coef(fit))[-1] G <- drop(crossprod(x, x %*% b - y_noisy)) li <- LassoInference$new(b, G, Q, rep(lam, p), NULL, NULL, Z, Q, kappa * Q, level = 0.9) # the estimator sum(eta * Z), specified by its variance and covariance with Z eta <- li$contrasts[[1]] custom_estimand_inference(li, sum(eta * Z), sum(eta * (Q %*% eta)), drop(Q %*% eta))[c("lower_conf", "upper_conf", "p_value")]
Matrix-free operators for Q_hat, Sigma and
Sigma_noise in LassoInference. dense_operator(M) wraps a
matrix; xtvx_operator(X, weights) represents t(X) %*% diag(weights) %*% X
without forming it, which is what wide designs (n << p) need.
dense_operator(M) xtvx_operator(X, weights = rep(1, nrow(X)))dense_operator(M) xtvx_operator(X, weights = rep(1, nrow(X)))
M |
A square numeric matrix. |
X |
A numeric design matrix. |
weights |
Observation weights, length |
A LinearOp with methods multiply, multiply_transpose,
rows, cols, diagonal, to_dense and scaled(c)
(c times the operator, sharing its data).
set.seed(1) x <- matrix(rnorm(50 * 5), 50, 5) v <- rnorm(5) Q <- xtvx_operator(x) all.equal(Q$multiply(v), drop(crossprod(x) %*% v)) all.equal(Q$scaled(2)$to_dense(), 2 * crossprod(x)) dense_operator(crossprod(x))$rows()set.seed(1) x <- matrix(rnorm(50 * 5), 50, 5) v <- rnorm(5) Q <- xtvx_operator(x) all.equal(Q$multiply(v), drop(crossprod(x) %*% v)) all.equal(Q$scaled(2)$to_dense(), 2 * crossprod(x)) dense_operator(crossprod(x))$rows()
Table of selective inference results
estimand_summary(selection, estimands, level = NULL, null_value = 0)estimand_summary(selection, estimands, level = NULL, null_value = 0)
selection |
A |
estimands |
Named list of |
level |
Confidence level (default: the selection's level). |
null_value |
Null value for the p-values. |
A data frame with columns index, estimate, lower_conf,
upper_conf, p_value.
set.seed(1) n <- 100; p <- 10 x <- matrix(rnorm(n * p), n, p) y <- x[, 1] - x[, 2] + rnorm(n) fit <- glmnet::glmnet(x, y, lambda = c(0.3, 0.2), control = list(thresh = 1e-14)) li <- glmnet_inference(fit, x, y, level = 0.9) # index 0 is the intercept estimand_summary(li, list(x1 = li$contrasts[["1"]], sum = li$contrasts[["1"]] + li$contrasts[["2"]]))set.seed(1) n <- 100; p <- 10 x <- matrix(rnorm(n * p), n, p) y <- x[, 1] - x[, 2] + rnorm(n) fit <- glmnet::glmnet(x, y, lambda = c(0.3, 0.2), control = list(thresh = 1e-14)) li <- glmnet_inference(fit, x, y, level = 0.9) # index 0 is the intercept estimand_summary(li, list(x1 = li$contrasts[["1"]], sum = li$contrasts[["1"]] + li$contrasts[["2"]]))
Intervals and p-values for the coefficients selected by a glmnet
fit at lambda = s, accounting for the selection. Without
selection_rows the fit is on all the data and there is no randomization (the
polyhedral approach). With selection_rows, the fit is on those rows only and
inference uses all the data (carving): selecting on a proportion of the data
is randomization with scalar_noise = (1 - pi) / pi.
The selection score is Z_noisy = -G + Q_hat beta_hat from
glmnet_problem_from_fit on the selection rows, and
Sigma = dispersion * Q_hat / sum(weights). Without held-out data
Z_full = Z_noisy. With held-out data, Z_full = -G_full(beta_tilde) +
Q_hat beta_tilde, with G_full the full-data gradient and beta_tilde one
Newton step for the full-data loss on the selected coordinates from beta_hat. Weights are
treated as prior (frequency or precision) weights, as in glm.
glmnet_inference( fit, x, y, s = NULL, selection_rows = NULL, weights = NULL, offset = NULL, dispersion = NULL, level = 0.95, hessian = c("dense", "operator"), information = c("relaxed", "lasso"), envir = parent.frame() )glmnet_inference( fit, x, y, s = NULL, selection_rows = NULL, weights = NULL, offset = NULL, dispersion = NULL, level = 0.95, hessian = c("dense", "operator"), information = c("relaxed", "lasso"), envir = parent.frame() )
fit |
A fitted |
x, y
|
The full data. With |
s |
A value in |
selection_rows |
Rows (indices or logical) the fit used for selection; |
weights, offset
|
Full-data observation weights and offset; default those passed to
|
dispersion |
Dispersion; default 1 for binomial and poisson and, for gaussian, the
residual variance of the least squares fit on all the variables (requires
|
level |
Confidence level. |
hessian, information
|
As in |
envir |
Environment in which to evaluate the arguments of |
A LassoInference object; $summary() gives the selective
intervals and p-values. Indices are 0-based, with 0 the intercept when the fit has one.
Lee, J. D., Sun, D. L., Sun, Y. and Taylor, J. E. (2016). Exact post-selection inference, with application to the lasso. The Annals of Statistics, 44(3), 907–927. doi:10.1214/15-AOS1371
Panigrahi, S., Fry, K. and Taylor, J. (2024). Exact selective inference with randomization. Biometrika, 111(4), 1109–1127. doi:10.1093/biomet/asae019
set.seed(1) n <- 200; p <- 10 x <- matrix(rnorm(n * p), n, p) y <- rbinom(n, 1, plogis(x[, 1] - x[, 2])) fit <- glmnet::glmnet(x, y, family = "binomial", lambda = c(0.2, 0.1, 0.05), control = list(thresh = 1e-14)) glmnet_inference(fit, x, y, level = 0.9)$summary() # carving: select on half of the data, inference with all of it rows <- sample(n, n / 2) fit <- glmnet::glmnet(x[rows, ], y[rows], family = "binomial", lambda = c(0.2, 0.1, 0.05), control = list(thresh = 1e-14)) glmnet_inference(fit, x, y, selection_rows = rows, level = 0.9)$summary()set.seed(1) n <- 200; p <- 10 x <- matrix(rnorm(n * p), n, p) y <- rbinom(n, 1, plogis(x[, 1] - x[, 2])) fit <- glmnet::glmnet(x, y, family = "binomial", lambda = c(0.2, 0.1, 0.05), control = list(thresh = 1e-14)) glmnet_inference(fit, x, y, level = 0.9)$summary() # carving: select on half of the data, inference with all of it rows <- sample(n, n / 2) fit <- glmnet::glmnet(x[rows, ], y[rows], family = "binomial", lambda = c(0.2, 0.1, 0.05), control = list(thresh = 1e-14)) glmnet_inference(fit, x, y, selection_rows = rows, level = 0.9)$summary()
Gradient and Hessian of the smooth part at the solution, penalty
weights and limits, in the original coordinates: the inputs of
LassoInference. glmnet_problem_from_fit reads the settings from a
fitted glmnet object.
glmnet_problem( x, y, coef, a0 = 0, lambda, family = "gaussian", weights = NULL, offset = NULL, alpha = 1, penalty.factor = rep(1, ncol(x)), exclude = NULL, lower.limits = -Inf, upper.limits = Inf, standardize = TRUE, intercept = TRUE, scaling = NULL, y_scale = NULL, hessian = c("dense", "operator"), information = c("relaxed", "lasso") ) glmnet_problem_from_fit( fit, x, y, s = NULL, weights = NULL, offset = NULL, hessian = c("dense", "operator"), information = c("relaxed", "lasso"), envir = parent.frame() )glmnet_problem( x, y, coef, a0 = 0, lambda, family = "gaussian", weights = NULL, offset = NULL, alpha = 1, penalty.factor = rep(1, ncol(x)), exclude = NULL, lower.limits = -Inf, upper.limits = Inf, standardize = TRUE, intercept = TRUE, scaling = NULL, y_scale = NULL, hessian = c("dense", "operator"), information = c("relaxed", "lasso") ) glmnet_problem_from_fit( fit, x, y, s = NULL, weights = NULL, offset = NULL, hessian = c("dense", "operator"), information = c("relaxed", "lasso"), envir = parent.frame() )
x |
Design matrix. |
y |
Response (0/1 or a two-level factor for binomial). |
coef |
Fitted coefficients (original scale). |
a0 |
Fitted intercept. |
lambda |
The lambda of the fit. |
family |
|
weights, offset
|
Observation weights and offset. |
alpha, penalty.factor, exclude, lower.limits, upper.limits, standardize, intercept
|
As passed to |
scaling |
Column scales; default glmnet's convention ( |
y_scale |
The ridge term is divided by |
hessian |
|
information |
Where |
fit |
A fitted |
s |
A value in |
envir |
Environment in which to evaluate the arguments of |
A list with beta_hat, G_hat, Q_hat, D, L,
U and intercept; coordinates are (intercept, coef) when
intercept = TRUE.
set.seed(1) x <- matrix(rnorm(100 * 5), 100, 5) y <- rbinom(100, 1, plogis(x[, 1])) fit <- glmnet::glmnet(x, y, family = "binomial", lambda = c(0.1, 0.05), control = list(thresh = 1e-14)) prob <- glmnet_problem_from_fit(fit, x, y) prob$beta_hat max(kkt_violation(prob$beta_hat, prob$G_hat, prob$D, prob$L, prob$U))set.seed(1) x <- matrix(rnorm(100 * 5), 100, 5) y <- rbinom(100, 1, plogis(x[, 1])) fit <- glmnet::glmnet(x, y, family = "binomial", lambda = c(0.1, 0.05), control = list(thresh = 1e-14)) prob <- glmnet_problem_from_fit(fit, x, y) prob$beta_hat max(kkt_violation(prob$beta_hat, prob$G_hat, prob$D, prob$L, prob$U))
Weighted standard deviations of the columns (with or without an intercept), 1 if not standardizing.
glmnet_scaling(x, weights = rep(1, nrow(x)), standardize = TRUE)glmnet_scaling(x, weights = rep(1, nrow(x)), standardize = TRUE)
x |
Design matrix. |
weights |
Observation weights. |
standardize |
As passed to |
The column scales, a vector of length ncol(x).
x <- matrix(rnorm(40), 10, 4) glmnet_scaling(x)x <- matrix(rnorm(40), 10, 4) glmnet_scaling(x)
Selective inference for inactive variables
inactive_summary( selection, estimand = "coef", basis = "refit", variables = NULL, level = NULL, null_value = 0, Q_diag = NULL )inactive_summary( selection, estimand = "coef", basis = "refit", variables = NULL, level = NULL, null_value = 0, Q_diag = NULL )
selection |
A |
estimand |
|
basis |
|
variables |
Inactive variables (0-based); defaults to |
level |
Confidence level (default: the selection's level). |
null_value |
Null value for the p-values. |
Q_diag |
Optional diagonal of |
A data frame as from estimand_summary, with integer index.
set.seed(1) n <- 100; p <- 10 x <- matrix(rnorm(n * p), n, p) y <- x[, 1] - x[, 2] + rnorm(n) fit <- glmnet::glmnet(x, y, lambda = c(0.3, 0.2), control = list(thresh = 1e-14)) li <- glmnet_inference(fit, x, y, level = 0.9) # index 0 is the intercept head(inactive_summary(li))set.seed(1) n <- 100; p <- 10 x <- matrix(rnorm(n * p), n, p) y <- x[, 1] - x[, 2] + rnorm(n) fit <- glmnet::glmnet(x, y, lambda = c(0.3, 0.2), control = list(thresh = 1e-14)) li <- glmnet_inference(fit, x, y, level = 0.9) # index 0 is the intercept head(inactive_summary(li))
Coordinatewise violation of 0 in G + D d|beta| + N_[L, U](beta);
0 at the exact solution. Coordinates with L = U are fixed and skipped.
kkt_violation(beta, G, D, L, U, tol = 1e-08)kkt_violation(beta, G, D, L, U, tol = 1e-08)
beta, G, D, L, U
|
Solution, gradient of the smooth part, penalty weights and limits
(e.g. from |
tol |
Tolerance for being at 0 or at a limit. |
The violation for each coordinate, nonnegative.
# beta = (1, 0) solves min (beta - c(2, 0.5))^2 / 2 + |beta|_1 beta <- c(1, 0) kkt_violation(beta, G = beta - c(2, 0.5), D = c(1, 1), L = c(-Inf, -Inf), U = c(Inf, Inf))# beta = (1, 0) solves min (beta - c(2, 0.5))^2 / 2 + |beta|_1 beta <- c(1, 0) kkt_violation(beta, G = beta - c(2, 0.5), D = c(1, 1), L = c(-Inf, -Inf), U = c(Inf, Inf))
Computes post-selection inference for the lasso. This class mirrors the Python 'lassoinf.LassoInference' dataclass, providing identical parameters and functional parity.
beta_hatThe lasso solution from the randomized objective.
G_hatThe gradient at beta_hat of the unpenalized loss.
Q_hatThe Hessian / design matrix crossprod: a matrix or a LinearOp.
DThe penalty weights.
LLower bounds.
UUpper bounds.
Z_fullThe unpenalized score on the full data.
SigmaCovariance of Z_full: a matrix or a LinearOp.
Sigma_noiseCovariance of the randomization noise: a matrix, a LinearOp or NULL.
scalar_noiseScalar controlling variance of noise if Sigma_noise is not provided.
AAffine constraints, a matrix-free LinearOp (from C++ backend).
bAffine constraint vector (from C++ backend).
EThe active set (selected indices).
E_cThe inactive set.
s_ESigns of the active set coefficients.
v_EcValues at the bounds for the inactive set.
WInverse of Q_hat restricted to the active set.
scoreInactive score operator U_{-E} (a LinearOp).
Z_noisyThe score used for selection.
levelConfidence level.
siAffineConstraints C++ wrapper instance.
intervalsSelective inference intervals.
splittingData splitting intervals.
naiveNaive intervals.
contrastsThe contrast vectors for inference.
_naiveData frame storing naive results.
_splittingData frame storing data splitting results.
LassoInference$new()Initialize the LassoInference object.
LassoInference$new( beta_hat, G_hat, Q_hat, D, L = NULL, U = NULL, Z_full, Sigma, Sigma_noise = NULL, scalar_noise = NaN, tol = 1e-06, level = 0.95 )
beta_hatThe lasso solution.
G_hatThe gradient at beta_hat.
Q_hatThe Hessian / design matrix crossprod.
DThe penalty weights.
LLower bounds for beta. Default is NULL (equivalent to -Inf).
UUpper bounds for beta. Default is NULL (equivalent to Inf).
Z_fullThe unpenalized score.
SigmaCovariance of Z_full.
Sigma_noiseCovariance of Z_noisy. Default is NULL.
scalar_noiseIf Sigma_noise is NULL, the noise covariance is
scalar_noise * Sigma. scalar_noise = 0 is no randomization (the
polyhedral lemma, with Z_full equal to the selection score
-G_hat + Q_hat beta_hat); as in the Python package, values below 0.001 are
raised to 0.001 for numerical stability. Default is NaN.
tolTolerance for active set and KKT conditions. Default is 1e-6.
levelConfidence level. Default is 0.95.
LassoInference$summary()Summary of post-selection inference results.
LassoInference$summary()
A data frame containing the parameter indices, estimates, selective confidence intervals, and selective p-values.
LassoInference$clone()The objects of this class are cloneable with this method.
LassoInference$clone(deep = FALSE)
deepWhether to make a deep clone.
Tian, X. and Taylor, J. (2018). Selective inference with a randomized response. The Annals of Statistics, 46(2), 679–710. doi:10.1214/17-AOS1564
Panigrahi, S., Fry, K. and Taylor, J. (2024). Exact selective inference with randomization. Biometrika, 111(4), 1109–1127. doi:10.1093/biomet/asae019
set.seed(1) n <- 100; p <- 10 x <- matrix(rnorm(n * p), n, p) y <- x[, 1] - x[, 2] + rnorm(n) # randomized gaussian LASSO 1/2 ||y + omega - x b||^2 + lam ||b||_1, sigma^2 = 1, # Var(omega) = kappa kappa <- 0.5 y_noisy <- y + rnorm(n, sd = sqrt(kappa)) lam <- 2 * sqrt(n) fit <- glmnet::glmnet(x, y_noisy, lambda = lam / n, intercept = FALSE, standardize = FALSE, control = list(thresh = 1e-14)) b <- as.numeric(coef(fit))[-1] Q <- crossprod(x) G <- drop(crossprod(x, x %*% b - y_noisy)) li <- LassoInference$new(b, G, Q, D = rep(lam, p), Z_full = drop(crossprod(x, y)), Sigma = Q, scalar_noise = kappa, level = 0.9) li$summary() # for a glmnet fit, glmnet_inference() builds these argumentsset.seed(1) n <- 100; p <- 10 x <- matrix(rnorm(n * p), n, p) y <- x[, 1] - x[, 2] + rnorm(n) # randomized gaussian LASSO 1/2 ||y + omega - x b||^2 + lam ||b||_1, sigma^2 = 1, # Var(omega) = kappa kappa <- 0.5 y_noisy <- y + rnorm(n, sd = sqrt(kappa)) lam <- 2 * sqrt(n) fit <- glmnet::glmnet(x, y_noisy, lambda = lam / n, intercept = FALSE, standardize = FALSE, control = list(thresh = 1e-14)) b <- as.numeric(coef(fit))[-1] Q <- crossprod(x) G <- drop(crossprod(x, x %*% b - y_noisy)) li <- LassoInference$new(b, G, Q, D = rep(lam, p), Z_full = drop(crossprod(x, y)), Sigma = Q, scalar_noise = kappa, level = 0.9) li$summary() # for a glmnet fit, glmnet_inference() builds these arguments
LASSO selection followed by screening of inactive variables on the
gradient at the LASSO solution, G = grad(beta_hat)_{-E}: keep variables with
|G_j| > threshold, or the top_k by |G_j|. Inference conditions on
the kept variables and the signs of their gradients; for top_k with
conditioning = "first_dropped" (default) also on the largest dropped variable
and its sign (O(p) constraints), with "exact" on all (kept, dropped) pairs.
Can be passed wherever a LassoInference is accepted by the estimand functions.
lasso_inferenceThe underlying LassoInference.
thresholdScreening threshold (or NULL).
top_kNumber of variables kept (or NULL).
conditioningConditioning for top_k.
screenedKept inactive variables (0-based), by decreasing |G_j|.
screened_signsSigns of their gradients.
first_droppedLargest dropped variable (0-based) for "first_dropped", else NULL.
first_dropped_signSign of its gradient.
AConstraints of the combined selection event (LinearOp).
bConstraint offsets.
A_screenScreening constraints alone (LinearOp).
b_screenScreening constraint offsets.
siThe AffineConstraints of the LASSO inference.
levelConfidence level.
ScreenedSelection$new()Screen inactive variables.
ScreenedSelection$new( lasso_inference, threshold = NULL, top_k = NULL, conditioning = "first_dropped" )
lasso_inferenceA LassoInference.
thresholdKeep |G_j| > threshold.
top_kKeep the top_k largest |G_j|.
conditioning"first_dropped" or "exact" (for top_k).
ScreenedSelection$clone()The objects of this class are cloneable with this method.
ScreenedSelection$clone(deep = FALSE)
deepWhether to make a deep clone.
set.seed(1) n <- 100; p <- 10 x <- matrix(rnorm(n * p), n, p) y <- x[, 1] - x[, 2] + rnorm(n) fit <- glmnet::glmnet(x, y, lambda = c(0.3, 0.2), control = list(thresh = 1e-14)) li <- glmnet_inference(fit, x, y, level = 0.9) # index 0 is the intercept screen <- ScreenedSelection$new(li, top_k = 3) screen$screened inactive_summary(screen)set.seed(1) n <- 100; p <- 10 x <- matrix(rnorm(n * p), n, p) y <- x[, 1] - x[, 2] + rnorm(n) fit <- glmnet::glmnet(x, y, lambda = c(0.3, 0.2), control = list(thresh = 1e-14)) li <- glmnet_inference(fit, x, y, level = 0.9) # index 0 is the intercept screen <- ScreenedSelection$new(li, top_k = 3) screen$screened inactive_summary(screen)
The coordinates in which the selection constraints are expressed.
With basis = "refit": bar_beta_E = W Z_E and the inactive scores
U_{-E} = Z_{-E} - Q_{-E,E} W Z_E (in OLS, t(X_{-E}) (I - P_E) y).
With basis = "lasso": the LASSO solution beta_hat_E and the
negative gradient at it, -grad_{-E}; these differ from the refit
coordinates by constants, so they give the same contrast with a shifted value.
Everything is computed with Q_hat matrix-vector products.
cppThe C++ object.
EActive set (0-based).
E_cInactive set (0-based).
SelectionCoordinates$new()Create the coordinates for a selection.
SelectionCoordinates$new(selection, Q_diag = NULL)
selectionA LassoInference or ScreenedSelection.
Q_diagOptional diagonal of Q_hat; lets inactive_S use
|E| matvecs for many variables. Taken from Q_hat when known.
SelectionCoordinates$contrast()Contrast eta with sum(eta * Z) = sum(a_E * bar_beta_E) + sum(a_Ec * U_{-E}).
SelectionCoordinates$contrast(a_E = NULL, a_Ec = NULL)
a_ECoefficients on bar_beta_E (or NULL).
a_EcCoefficients on U_{-E} (or NULL).
SelectionCoordinates$estimand()Estimand linear in the selection coordinates.
SelectionCoordinates$estimand(a_E = NULL, a_Ec = NULL, basis = "refit")
a_ECoefficients on the active coordinates.
a_EcCoefficients on the inactive scores.
basis"refit" or "lasso".
SelectionCoordinates$inactive_score()Score of inactive variable j.
SelectionCoordinates$inactive_score(j, basis = "refit")
jInactive variable (0-based).
basis"refit" or "lasso".
SelectionCoordinates$inactive_coef()Coefficient of inactive variable j in the model E u {j}.
SelectionCoordinates$inactive_coef(j, basis = "refit")
jInactive variable (0-based).
basis"refit" or "lasso".
SelectionCoordinates$inactive_S()S_jj = Q_jj - Q_{j,E} W Q_{E,j} for inactive variables.
SelectionCoordinates$inactive_S(variables)
variablesInactive variables (0-based).
SelectionCoordinates$refit_coef()bar_beta_E for a score vector.
SelectionCoordinates$refit_coef(Z)
ZA score vector.
SelectionCoordinates$refit_score()U_{-E} for a score vector.
SelectionCoordinates$refit_score(Z)
ZA score vector.
SelectionCoordinates$lasso_offsets()Offsets of the lasso basis: active coefficients and inactive scores.
SelectionCoordinates$lasso_offsets()
SelectionCoordinates$score_operator()The inactive score operator.
SelectionCoordinates$score_operator()
SelectionCoordinates$clone()The objects of this class are cloneable with this method.
SelectionCoordinates$clone(deep = FALSE)
deepWhether to make a deep clone.
set.seed(1) n <- 100; p <- 10 x <- matrix(rnorm(n * p), n, p) y <- x[, 1] - x[, 2] + rnorm(n) fit <- glmnet::glmnet(x, y, lambda = c(0.3, 0.2), control = list(thresh = 1e-14)) li <- glmnet_inference(fit, x, y, level = 0.9) # index 0 is the intercept coords <- SelectionCoordinates$new(li) coords$E_c # coefficient of the first inactive variable in the model E u {j} res <- contrast_inference(li, coords$inactive_coef(coords$E_c[1])) res[c("lower_conf", "upper_conf", "p_value")]set.seed(1) n <- 100; p <- 10 x <- matrix(rnorm(n * p), n, p) y <- x[, 1] - x[, 2] + rnorm(n) fit <- glmnet::glmnet(x, y, lambda = c(0.3, 0.2), control = list(thresh = 1e-14)) li <- glmnet_inference(fit, x, y, level = 0.9) # index 0 is the intercept coords <- SelectionCoordinates$new(li) coords$E_c # coefficient of the first inactive variable in the model E u {j} res <- contrast_inference(li, coords$inactive_coef(coords$E_c[1])) res[c("lower_conf", "upper_conf", "p_value")]
Equal-tailed interval and two-sided p-value for H0: theta = null_value.
truncated_normal_inference( contrast, variance, A, b, level = 0.95, null_value = 0 )truncated_normal_inference( contrast, variance, A, b, level = 0.95, null_value = 0 )
contrast |
An |
variance |
Variance of the estimator. |
A |
Constraint matrix or |
b |
Constraint offsets. |
level |
Confidence level. |
null_value |
Null value of the estimand. |
Named vector c(lower, upper, p_value).
set.seed(1) n <- 100; p <- 10 x <- matrix(rnorm(n * p), n, p) y <- x[, 1] - x[, 2] + rnorm(n) fit <- glmnet::glmnet(x, y, lambda = c(0.3, 0.2), control = list(thresh = 1e-14)) li <- glmnet_inference(fit, x, y, level = 0.9) # index 0 is the intercept v <- li$contrasts[["1"]] # the coefficient of x1 in the selected model contrast <- li$si$compute_contrast(v) truncated_normal_inference(contrast, contrast$naive_variance, li$A, li$b, level = 0.9)set.seed(1) n <- 100; p <- 10 x <- matrix(rnorm(n * p), n, p) y <- x[, 1] - x[, 2] + rnorm(n) fit <- glmnet::glmnet(x, y, lambda = c(0.3, 0.2), control = list(thresh = 1e-14)) li <- glmnet_inference(fit, x, y, level = 0.9) # index 0 is the intercept v <- li$contrasts[["1"]] # the coefficient of x1 in the selected model contrast <- li$si$compute_contrast(v) truncated_normal_inference(contrast, contrast$naive_variance, li$A, li$b, level = 0.9)
Evaluates exact bounds and inference parameters for the selectively truncated bivariate normal distribution. Follows the bivariate normal computations of Liu (2023).
a_coeffInternal coefficient.
b_coeffInternal coefficient.
LLower bound of the truncation interval.
UUpper bound of the truncation interval.
sig_omegaStandard deviation of the noise component.
sig_xStandard deviation of the target statistic.
TruncBivariateNormal$new()Initialize the TruncBivariateNormal object.
TruncBivariateNormal$new( a_coeff, b_coeff, L, U, sig_omega, sig_x = 1, theta = 0 )
a_coeffThe 'a' coefficient for the conditional statistic.
b_coeffThe 'b' coefficient for the noise statistic.
LLower bound of the condition.
UUpper bound of the condition.
sig_omegaNoise standard deviation.
sig_xTarget statistic standard deviation. Default is 1.0.
thetaTrue underlying parameter. Default is 0.0.
TruncBivariateNormal$.get_stats()Get the computed statistics given the parameter.
TruncBivariateNormal$.get_stats(theta, x = NULL)
thetaTrue parameter value.
xObserved value. Default is NULL.
A list containing the computed stats.
TruncBivariateNormal$ccdf()Complementary CDF.
TruncBivariateNormal$ccdf(theta, x = NULL, gamma = 0)
thetaParameter.
xObserved value.
gammaIgnored.
The complementary CDF value.
TruncBivariateNormal$cdf()Cumulative Distribution Function.
TruncBivariateNormal$cdf(theta, x = NULL, gamma = 1)
thetaParameter.
xObserved value.
gammaIgnored.
The CDF value.
TruncBivariateNormal$E()Expected value.
TruncBivariateNormal$E(theta, func)
thetaParameter.
funcA function returning identity or x^2.
Expected value.
TruncBivariateNormal$Var()Variance.
TruncBivariateNormal$Var(theta, func)
thetaParameter.
funcIdentity function.
The conditional variance.
TruncBivariateNormal$equal_tailed_interval()Find the equal tailed confidence interval by inverting the CDF.
TruncBivariateNormal$equal_tailed_interval(observed, alpha = 0.05, tol = 1e-06)
observedThe observed value.
alphaType-I error rate. Default is 0.05.
tolTolerance for uniroot.
A length 2 vector containing the lower and upper bounds.
TruncBivariateNormal$clone()The objects of this class are cloneable with this method.
TruncBivariateNormal$clone(deep = FALSE)
deepWhether to make a deep clone.
Liu, S. (2023). An exact sampler for inference after polyhedral model selection. arXiv preprint. doi:10.48550/arXiv.2308.10346
tbn <- TruncBivariateNormal$new(a_coeff = 1, b_coeff = 1, L = 1, U = Inf, sig_omega = 1) tbn$cdf(theta = 0, x = 1.5) tbn$equal_tailed_interval(1.5, alpha = 0.1)tbn <- TruncBivariateNormal$new(a_coeff = 1, b_coeff = 1, L = 1, U = Inf, sig_omega = 1) tbn$cdf(theta = 0, x = 1.5) tbn$equal_tailed_interval(1.5, alpha = 0.1)