| Title: | Causal Model Selection with Genetic Instruments and Negative Controls |
|---|---|
| Description: | Provides a model selection workflow for causal inference with genetic instruments and negative controls in observational omics data. The package fits eight estimators of the natural direct and indirect effects (NDE/NIE), diagnoses which are valid for the user's data via instrument-strength checks and negative-control validity screens (A1, A2, A2'), stress-tests them against confounding and pleiotropy violations, and recommends the estimator most likely to be unbiased. A hybrid generative texture model (a torch GAN for sample-level structure and a Gaussian copula for the mediator panel) lets the sensitivity analysis mirror the marginal and joint structure of the user's cohort. The modular API comprises iconic_data() to standardize data, iconic_diagnose() to assess estimator eligibility, iconic_estimate() to fit all eligible estimators, iconic_sensitivity() to sweep instrument exogeneity, iconic_recommend() to rank estimators by identification strength and robustness, and iconic_prospect() to plan future studies. infer_confounding() estimates held-fixed confounding parameters from the data. Helper functions construct exposure instruments (polygenic scores from GWAS summary statistics or PGS Catalog scoring files), mediator instruments (cis-eQTL scans and elastic-net predicted-expression composites), and negative-control panels (principal components of residualized omics matrices), and data can be imported directly from SummarizedExperiment objects. Time-to-event outcomes are supported on the Cox log-hazard-ratio and restricted-mean-survival-time scales. Binary outcomes are supported on the log-odds-ratio (logistic two-stage predictor-substitution outcome stage) and risk-difference (linear probability model) scales. The torch package is required for the generative texture model (sensitivity and prospective analysis); all other functionality works without it. |
| Authors: | Sean T. Bresnahan [aut, cre], Charis Xiong [aut] |
| Maintainer: | Sean T. Bresnahan <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.99.3 |
| Built: | 2026-09-18 08:58:15 UTC |
| Source: | https://github.com/BiocStaging/iconic |
Reference-named entry point: applies UNADJ, DIRECT, COCA, IV2SLS, and PGC
(matrix bridge) mediation estimators to each tested outcome feature and
returns tidy per-feature results with significance flags for both NDE
and NIE. When the dataset includes a mediator instrument (Gm),
the 2-stage MR estimator fit_iv2sls_mediation2 (method
"IV2SLS2") is also run. When the dataset includes path-specific
negative controls (W1, W2;), the two-stage
proximal estimator fit_pgc_mediation2 is run as "PGC2"
(without Gm) and "PGC2Gm" (with Gm, when Gm is also present).
analyze_mediation_robust( iteration_data, test_features = NULL, alpha = 0.05, n_cores = 1, se_method = c("delta", "bootstrap", "composite"), n_boot = 500 )analyze_mediation_robust( iteration_data, test_features = NULL, alpha = 0.05, n_cores = 1, se_method = c("delta", "bootstrap", "composite"), n_boot = 500 )
iteration_data |
Dataset list from |
test_features |
Optional integer indices of outcome features to test.
Default |
alpha |
Significance threshold for the significance flags. Default 0.05. |
n_cores |
Number of parallel workers. Default 1 (sequential).
Uses |
se_method |
"delta" (default), "bootstrap", or
"composite". See
|
n_boot |
Number of bootstrap resamples when
|
Negative controls are always summarised over the full W panel for COCA,
so restricting test_features does not change its control summary.
The matrix-bridge PGC uses the full W panel.
A scalar-bridge variant (fit_pgc_scalar_mediation()) is exported for
standalone use but is not included in the default pipeline.
Data frame: feature, method, NDE, NDE_se,
NDE_p, NIE, NIE_se, NIE_p,
NDE_significant, NIE_significant.
dat <- generate_toy_data(n = 300, mo_confounding = 0.8, seed = 1) analyze_mediation_robust(dat)dat <- generate_toy_data(n = 300, mo_confounding = 0.8, seed = 1) analyze_mediation_robust(dat)
analyze_methods_robust()
Distributes the per-feature analysis across workers via
parallel::mclapply (falling back to sequential on Windows / one core).
analyze_methods_parallel( iteration_data, test_features = NULL, alpha = 0.05, debug = FALSE, n_cores = 1 )analyze_methods_parallel( iteration_data, test_features = NULL, alpha = 0.05, debug = FALSE, n_cores = 1 )
iteration_data |
Dataset list from |
test_features |
Optional integer indices of outcome features to test.
Default |
alpha |
Significance threshold for the |
debug |
If |
n_cores |
Number of workers. Default 1. |
Data frame: feature, method, beta, se, pvalue, significant.
dat <- run_single_iteration(NULL, n_synthetic_samples = 100, n_features = 5, n_confounders = 1, seed = 1) analyze_methods_parallel(dat, n_cores = 1)dat <- run_single_iteration(NULL, n_synthetic_samples = 100, n_features = 5, n_confounders = 1, seed = 1) analyze_methods_parallel(dat, n_cores = 1)
Reference-named entry point: applies UNADJ, DIRECT, COCA, IV2SLS, and PGC
(matrix bridge) to each tested outcome feature and returns tidy per-feature
results with a significance flag. Negative controls are summarised over
the full W panel for COCA, so restricting test_features does not change
its control summary. The matrix-bridge PGC uses the full W panel
(or a validity-screened subset if provided).
analyze_methods_robust( iteration_data, test_features = NULL, alpha = 0.05, debug = FALSE, n_cores = 1 )analyze_methods_robust( iteration_data, test_features = NULL, alpha = 0.05, debug = FALSE, n_cores = 1 )
iteration_data |
Dataset list from |
test_features |
Optional integer indices of outcome features to test.
Default |
alpha |
Significance threshold for the |
debug |
If |
n_cores |
Number of parallel workers. Default 1 (sequential).
Uses |
A scalar-bridge variant (fit_pgc_scalar()) is exported for standalone
use but is not included in the default pipeline.
Data frame: feature, method, beta, se, pvalue, significant.
dat <- run_single_iteration(NULL, n_synthetic_samples = 100, n_features = 5, n_confounders = 1, seed = 1) analyze_methods_robust(dat)dat <- run_single_iteration(NULL, n_synthetic_samples = 100, n_features = 5, n_confounders = 1, seed = 1) analyze_methods_robust(dat)
Predicts genetically regulated expression by applying published
FUSION per-gene weight files (.wgt.RDat) to a genotype dosage
matrix. For each gene, the model with the best cross-validation
performance (cv.performance["rsq", ]) is used. The resulting
predicted-expression matrix is germline-determined and — after PCA
via build_w_pcs() — serves as a negative-control panel that
captures confounding structure without direct exposure mediation.
apply_fusion_weights( genotypes, pos, weights_dir = NULL, weights = NULL, min_snps = 2, n_cores = 1 )apply_fusion_weights( genotypes, pos, weights_dir = NULL, weights = NULL, min_snps = 2, n_cores = 1 )
genotypes |
Numeric dosage matrix, variants x samples, with rownames matching the SNP IDs used in the weight files (usually rsIDs). |
pos |
A data.frame with (at least) columns |
weights_dir |
Directory containing the |
weights |
Optional named list of pre-loaded weight objects,
each a list with elements |
min_snps |
Integer: minimum number of matched SNPs required to predict a gene. Default 2. |
n_cores |
Integer: parallel workers via |
A list with:
numeric matrix, genes x samples: predicted expression.
data.frame per predicted gene: gene, n_snps,
best_method, rsq (cross-validation R2 of the selected
model).
# Simulate two FUSION-style weight objects in memory dos <- matrix(rbinom(10 * 50, 2, 0.3), nrow = 10, dimnames = list(paste0("rs", 1:10), paste0("S", 1:50))) mk_wgt <- function(snps, w) { list(wgt.matrix = matrix(w, ncol = 1, dimnames = list(NULL, "enet")), snps = data.frame(V2 = snps), cv.performance = matrix(0.2, nrow = 1, dimnames = list("rsq", "enet"))) } wlist <- list(GeneA = mk_wgt(paste0("rs", 1:5), rep(0.1, 5)), GeneB = mk_wgt(paste0("rs", 6:10), rep(-0.2, 5))) pos <- data.frame(ID = c("GeneA", "GeneB"), WGT = c("a.wgt.RDat", "b.wgt.RDat")) out <- apply_fusion_weights(dos, pos = pos, weights = wlist) dim(out$predicted)# Simulate two FUSION-style weight objects in memory dos <- matrix(rbinom(10 * 50, 2, 0.3), nrow = 10, dimnames = list(paste0("rs", 1:10), paste0("S", 1:50))) mk_wgt <- function(snps, w) { list(wgt.matrix = matrix(w, ncol = 1, dimnames = list(NULL, "enet")), snps = data.frame(V2 = snps), cv.performance = matrix(0.2, nrow = 1, dimnames = list("rsq", "enet"))) } wlist <- list(GeneA = mk_wgt(paste0("rs", 1:5), rep(0.1, 5)), GeneB = mk_wgt(paste0("rs", 6:10), rep(-0.2, 5))) pos <- data.frame(ID = c("GeneA", "GeneB"), WGT = c("a.wgt.RDat", "b.wgt.RDat")) out <- apply_fusion_weights(dos, pos = pos, weights = wlist) dim(out$predicted)
S3 generic bridging other data interfaces to the estimation interface. Methods:
as_iconic_data(input, ...) ## Default S3 method: as_iconic_data(input, ...) ## S3 method for class 'SummarizedExperiment' as_iconic_data( input, assay = 1, mediator_assay = NULL, exposure, instrument = NULL, mediator_instrument = NULL, negative_controls = NULL, covariates = NULL, surv_time = NULL, surv_event = NULL, bin_outcome = NULL, outcome_type = c("continuous", "survival", "binary"), ... )as_iconic_data(input, ...) ## Default S3 method: as_iconic_data(input, ...) ## S3 method for class 'SummarizedExperiment' as_iconic_data( input, assay = 1, mediator_assay = NULL, exposure, instrument = NULL, mediator_instrument = NULL, negative_controls = NULL, covariates = NULL, surv_time = NULL, surv_event = NULL, bin_outcome = NULL, outcome_type = c("continuous", "survival", "binary"), ... )
input |
An object to convert: a list returned by
|
... |
Named arguments passed to |
assay |
Name or index of the assay of |
mediator_assay |
Optional name or index of an assay holding the mediator panel (mediators x samples). |
exposure |
Character: name of the |
instrument |
Optional character: name of the |
mediator_instrument |
Optional character vector of |
negative_controls |
Optional character vector of |
covariates |
Optional character vector of |
surv_time, surv_event
|
Optional character: names of |
bin_outcome |
Optional character: name of a |
outcome_type |
|
default: a list returned by load_real_input_data(), an
existing iconic_data object (returned as-is), or the exposure
vector X with named arguments matching iconic_data() (Y, M,
G, Gm, W, W1, W2, covariates, feature_names, mediator_names),
which delegates to iconic_data().
SummarizedExperiment: extracts the outcome panel from an assay
and sample-level fields (exposure, instruments, negative
controls, covariates) from colData. Requires the
SummarizedExperiment package (listed under Suggests).
An iconic_data S3 object.
# From a load_real_input_data() result input <- load_real_input_data(example = TRUE) data <- as_iconic_data(input) print(data) # From named components (delegates to iconic_data()) data <- as_iconic_data(rnorm(100), Y = matrix(rnorm(100*10), 10, 100), G = rnorm(100), Gm = rnorm(100), W = matrix(rnorm(100*10), 10, 100)) if (requireNamespace("SummarizedExperiment", quietly = TRUE)) { se <- SummarizedExperiment::SummarizedExperiment( assays = list(expr = matrix(rnorm(20 * 60), 20, 60, dimnames = list(paste0("gene", 1:20), paste0("S", 1:60)))), colData = S4Vectors::DataFrame( bmi = rnorm(60), prs = rnorm(60), nc1 = rnorm(60), nc2 = rnorm(60), age = rnorm(60)) ) data <- as_iconic_data(se, assay = "expr", exposure = "bmi", instrument = "prs", negative_controls = c("nc1", "nc2"), covariates = "age") print(data) }# From a load_real_input_data() result input <- load_real_input_data(example = TRUE) data <- as_iconic_data(input) print(data) # From named components (delegates to iconic_data()) data <- as_iconic_data(rnorm(100), Y = matrix(rnorm(100*10), 10, 100), G = rnorm(100), Gm = rnorm(100), W = matrix(rnorm(100*10), 10, 100)) if (requireNamespace("SummarizedExperiment", quietly = TRUE)) { se <- SummarizedExperiment::SummarizedExperiment( assays = list(expr = matrix(rnorm(20 * 60), 20, 60, dimnames = list(paste0("gene", 1:20), paste0("S", 1:60)))), colData = S4Vectors::DataFrame( bmi = rnorm(60), prs = rnorm(60), nc1 = rnorm(60), nc2 = rnorm(60), age = rnorm(60)) ) data <- as_iconic_data(se, assay = "expr", exposure = "bmi", instrument = "prs", negative_controls = c("nc1", "nc2"), covariates = "age") print(data) }
Applies the logit transform after
clipping betas away from 0 and 1. M-values are approximately
homoscedastic and are the recommended scale for linear modeling and
PCA of methylation data.
beta_to_m(beta, clip = 1e-04, drop_nonfinite = TRUE)beta_to_m(beta, clip = 1e-04, drop_nonfinite = TRUE)
beta |
Numeric matrix of beta values (probes x samples), in [0, 1]. |
clip |
Numeric: betas are clipped to [ |
drop_nonfinite |
Logical: drop probes (rows) with any
missing/non-finite values after the transform. Default |
A numeric matrix of M-values, probes x samples.
b <- matrix(runif(200, 0.01, 0.99), nrow = 20, dimnames = list(paste0("cg", 1:20), paste0("S", 1:10))) m <- beta_to_m(b) range(m)b <- matrix(runif(200, 0.01, 0.99), nrow = 20, dimnames = list(paste0("cg", 1:20), paste0("S", 1:10))) m <- beta_to_m(b) range(m)
Trains one cross-validated elastic-net model per gene predicting
expression from cis SNPs, after residualizing both expression and
dosages on covariates (Frisch-Waugh-Lovell). Genes are kept when
the model is non-degenerate (at least one non-zero SNP weight), the
signed out-of-fold R2 exceeds cv_r2_min, and the one-sided
out-of-fold correlation p-value is below cv_p_max. The in-sample
predicted expression of passing genes is the composite mediator
instrument Gm consumed by iconic_data().
build_mediator_instruments( expression, genotypes, gene_pos, snp_pos, covariates = NULL, cis_snps = NULL, alpha = 0.3, nfolds = 5, cis_dist = 1e+06, cv_r2_min = 0.005, cv_p_max = 0.1, n_cores = 1, seed = NULL, verbose = TRUE )build_mediator_instruments( expression, genotypes, gene_pos, snp_pos, covariates = NULL, cis_snps = NULL, alpha = 0.3, nfolds = 5, cis_dist = 1e+06, cv_r2_min = 0.005, cv_p_max = 0.1, n_cores = 1, seed = NULL, verbose = TRUE )
expression |
Numeric matrix, genes x samples (e.g. inverse-normal-transformed TPM). |
genotypes |
Numeric dosage matrix, variants x samples (0-2). |
gene_pos |
data.frame with columns |
snp_pos |
data.frame with columns |
covariates |
Optional data.frame or matrix (n rows) of covariates to partial out (ancestry PCs, technical factors, ...). |
cis_snps |
Optional named list: gene name -> character vector
of candidate SNP IDs (e.g. significant GTEx cis-eQTL pairs, as from
|
alpha |
Numeric: elastic-net mixing (0 = ridge, 1 = lasso). Default 0.3. |
nfolds |
Integer: cross-validation folds. Default 5. |
cis_dist |
Numeric: cis window half-width around the gene start, in bp. Default 1e6. |
cv_r2_min |
Numeric: minimum signed out-of-fold R2. Default 0.005. |
cv_p_max |
Numeric: maximum one-sided out-of-fold correlation
p-value (nominal screen only; the load-bearing gates are
|
n_cores |
Integer: parallel workers via |
seed |
Optional integer for reproducible CV fold assignments. |
verbose |
Logical: print progress. Default |
Requires the glmnet package (listed under Suggests).
A list with:
numeric matrix, genes x samples: in-sample predicted
expression for genes passing the quality filter. Pass to
iconic_data() as Gm (it is transposed internally).
data.frame of per-gene quality metrics: gene,
n_cis_snps, n_nonzero, cv_r2, cor_oof, cv_p,
lambda_min, pass.
named list (all trained genes) of data.frames with
snp and weight (non-zero coefficients at lambda.min).
if (requireNamespace("glmnet", quietly = TRUE)) { set.seed(42) n <- 150; p <- 80 dos <- matrix(rbinom(p * n, 2, 0.3), nrow = p, dimnames = list(paste0("1:", 1:p, ":A:G"), paste0("S", 1:n))) expr <- matrix(rnorm(4 * n), nrow = 4, dimnames = list(paste0("Gene", 1:4), paste0("S", 1:n))) expr[1, ] <- expr[1, ] + 0.9 * scale(dos[5, ]) + 0.9 * scale(dos[15, ]) gp <- data.frame(gene = paste0("Gene", 1:4), chr = "1", tss = c(5, 25, 45, 65)) sp <- data.frame(snp = rownames(dos), chr = "1", pos = 1:p) fit <- build_mediator_instruments(expr, dos, gp, sp, seed = 1) fit$qc }if (requireNamespace("glmnet", quietly = TRUE)) { set.seed(42) n <- 150; p <- 80 dos <- matrix(rbinom(p * n, 2, 0.3), nrow = p, dimnames = list(paste0("1:", 1:p, ":A:G"), paste0("S", 1:n))) expr <- matrix(rnorm(4 * n), nrow = 4, dimnames = list(paste0("Gene", 1:4), paste0("S", 1:n))) expr[1, ] <- expr[1, ] + 0.9 * scale(dos[5, ]) + 0.9 * scale(dos[15, ]) gp <- data.frame(gene = paste0("Gene", 1:4), chr = "1", tss = c(5, 25, 45, 65)) sp <- data.frame(snp = rownames(dos), chr = "1", pos = 1:p) fit <- build_mediator_instruments(expr, dos, gp, sp, seed = 1) fit$qc }
Runs the LDpred2-auto Bayesian shrinkage pipeline (Privé et al.) via the bigsnpr package: match summary statistics to an LD reference, estimate SNP heritability by LD score regression, run a grid of auto chains, filter chains by convergence, and average the per-chain effects. Optionally scores a target genotype set.
build_prs_ldpred2( sumstats, ld_ref, genotypes = NULL, hapmap3 = NULL, max_variants = NULL, n_chains = 30, p_range = c(1e-04, 0.2), burn_in = 500, num_iter = 500, shrink_corr = 0.95, allow_jump_sign = FALSE, use_mle = FALSE, chain_filter_quantile = 0.95, impute_mean = TRUE, ncores = 1, verbose = TRUE )build_prs_ldpred2( sumstats, ld_ref, genotypes = NULL, hapmap3 = NULL, max_variants = NULL, n_chains = 30, p_range = c(1e-04, 0.2), burn_in = 500, num_iter = 500, shrink_corr = 0.95, allow_jump_sign = FALSE, use_mle = FALSE, chain_filter_quantile = 0.95, impute_mean = TRUE, ncores = 1, verbose = TRUE )
sumstats |
A data.frame of GWAS summary statistics, ideally
pre-processed by |
ld_ref |
Either a path to a bigsnpr |
genotypes |
Optional: path to a bigsnpr |
hapmap3 |
Optional character vector of variant IDs (rsIDs or
|
max_variants |
Optional integer: if the matched variant count
exceeds this, systematically thin to approximately |
n_chains |
Integer: number of LDpred2-auto chains. Default 30. |
p_range |
Numeric length-2: range of the initial per-chain
proportion-of-causal-variants grid, log-spaced. Default
|
burn_in |
Integer: burn-in iterations per chain. Default 500. |
num_iter |
Integer: sampling iterations per chain (after burn-in). Default 500. |
shrink_corr |
Numeric: LD shrinkage parameter. Default 0.95; reduce to ~0.4 when the SD-ratio QC flags >10% of variants. |
allow_jump_sign |
Logical: allow sign jumps across chains.
Default |
use_mle |
Logical: use MLE for the hyperparameter updates.
Default |
chain_filter_quantile |
Numeric: chains are kept when the range
of their posterior |
impute_mean |
Logical: mean-impute missing target genotypes
before scoring. Default |
ncores |
Integer: threads for bigsnpr. Default 1. |
verbose |
Logical: print progress. Default |
This function requires the bigsnpr package (listed under Suggests)
and a PLINK/bed-format LD reference panel converted with
bigsnpr::snp_readBed().
A list with:
data.frame of LDpred2 effect sizes (chr, pos,
a0, a1, beta) in LD-reference orientation.
numeric vector of per-sample polygenic scores for
genotypes (scaled to unit variance), or NULL when genotypes
was not supplied.
LD-score-regression SNP heritability estimate used to initialize the chains.
integer indices of chains passing the convergence filter.
list with variant counts at each stage.
if (requireNamespace("bigsnpr", quietly = TRUE)) { # Tiny fake genotype panel standing in for an LD reference # (snp_fake genotypes are all-missing mocks; fill with random dosages) fake <- bigsnpr::snp_fake(100, 500) fake$genotypes[] <- rbinom(100 * 500, 2, 0.3) fake$map$chromosome <- 1L fake$map$physical.pos <- sort(sample(1:1e6, 500)) ss <- data.frame( chr = fake$map$chromosome, pos = fake$map$physical.pos, a0 = fake$map$allele2, a1 = fake$map$allele1, beta = rnorm(500, 0, 0.05), beta_se = 0.02, n_eff = 20000 ) prs <- build_prs_ldpred2(ss, ld_ref = fake, n_chains = 3, burn_in = 50, num_iter = 50, verbose = FALSE) str(prs$beta) }if (requireNamespace("bigsnpr", quietly = TRUE)) { # Tiny fake genotype panel standing in for an LD reference # (snp_fake genotypes are all-missing mocks; fill with random dosages) fake <- bigsnpr::snp_fake(100, 500) fake$genotypes[] <- rbinom(100 * 500, 2, 0.3) fake$map$chromosome <- 1L fake$map$physical.pos <- sort(sample(1:1e6, 500)) ss <- data.frame( chr = fake$map$chromosome, pos = fake$map$physical.pos, a0 = fake$map$allele2, a1 = fake$map$allele1, beta = rnorm(500, 0, 0.05), beta_se = 0.02, n_eff = 20000 ) prs <- build_prs_ldpred2(ss, ld_ref = fake, n_chains = 3, burn_in = 50, num_iter = 50, verbose = FALSE) str(prs$beta) }
Computes the top n_pcs principal components of a features x
samples matrix (typically residualized with
residualize_matrix()) and returns the sample scores as a W panel
(PCs x samples) in the orientation expected by iconic_data().
Uses irlba for fast truncated PCA when available, falling back to
stats::prcomp().
build_w_pcs(x, n_pcs = 20, scale_features = FALSE, prefix = "PC")build_w_pcs(x, n_pcs = 20, scale_features = FALSE, prefix = "PC")
x |
Numeric matrix, features x samples (e.g. residualized M-values or predicted expression). |
n_pcs |
Integer: number of PCs to return. Default 20. |
scale_features |
Logical: scale features to unit variance
before PCA. Default |
prefix |
Character: row-name prefix for the PCs. Default
|
A list with:
numeric matrix, PCs x samples, suitable as the W
argument of iconic_data().
numeric vector: percent of total variance explained by each returned PC.
x <- matrix(rnorm(500 * 60), nrow = 500, dimnames = list(paste0("f", 1:500), paste0("S", 1:60))) w <- build_w_pcs(x, n_pcs = 5) dim(w$W) w$variance_explainedx <- matrix(rnorm(500 * 60), nrow = 500, dimnames = list(paste0("f", 1:500), paste0("S", 1:60))) w <- build_w_pcs(x, n_pcs = 5) dim(w$W) w$variance_explained
For each gene, tests every cis SNP (within cis_dist of the gene
start, optionally restricted to a supplied candidate set) for
association with expression, after residualizing both expression
and dosages on covariates (Frisch-Waugh-Lovell). A lightweight,
pure-R alternative to MatrixEQTL for preparing mediator-instrument
candidate sets.
call_cis_eqtls( expression, genotypes, gene_pos, snp_pos, covariates = NULL, cis_snps = NULL, cis_dist = 1e+06, min_maf = 0.01, fdr = 0.05, n_cores = 1 )call_cis_eqtls( expression, genotypes, gene_pos, snp_pos, covariates = NULL, cis_snps = NULL, cis_dist = 1e+06, min_maf = 0.01, fdr = 0.05, n_cores = 1 )
expression |
Numeric matrix, genes x samples (e.g. inverse-normal-transformed TPM). |
genotypes |
Numeric dosage matrix, variants x samples (0-2). |
gene_pos |
data.frame with columns |
snp_pos |
data.frame with columns |
covariates |
Optional data.frame or matrix (n rows) of covariates to partial out (e.g. ancestry PCs, technical factors). |
cis_snps |
Optional named list: gene name -> character vector of candidate SNP IDs (e.g. significant GTEx pairs). When supplied, only these SNPs are tested for each gene (still intersected with the cis window). |
cis_dist |
Numeric: cis window half-width around the gene start, in bp. Default 1e6. |
min_maf |
Numeric: minimum minor allele frequency (computed from mean dosage). Default 0.01. |
fdr |
Numeric: BH FDR level for the per-gene |
n_cores |
Integer: parallel workers via |
A list with:
data.frame of all tested gene-SNP pairs: gene,
snp, beta, se, t, p.
data.frame with the top SNP per gene (gene, snp,
beta, p, q (BH within gene), pass).
n <- 80 dos <- matrix(rbinom(200 * n, 2, 0.3), nrow = 200, dimnames = list(paste0("1:", 1:200, ":A:G"), NULL)) expr <- matrix(rnorm(20 * n), nrow = 20, dimnames = list(paste0("Gene", 1:20), NULL)) expr[1, ] <- expr[1, ] + 0.5 * dos[5, ] # one true cis-eQTL gp <- data.frame(gene = paste0("Gene", 1:20), chr = "1", tss = seq(1, 191, by = 10)) sp <- data.frame(snp = rownames(dos), chr = "1", pos = 1:200) hits <- call_cis_eqtls(expr, dos, gp, sp) head(hits$best)n <- 80 dos <- matrix(rbinom(200 * n, 2, 0.3), nrow = 200, dimnames = list(paste0("1:", 1:200, ":A:G"), NULL)) expr <- matrix(rnorm(20 * n), nrow = 20, dimnames = list(paste0("Gene", 1:20), NULL)) expr[1, ] <- expr[1, ] + 0.5 * dos[5, ] # one true cis-eQTL gp <- data.frame(gene = paste0("Gene", 1:20), chr = "1", tss = seq(1, 191, by = 10)) sp <- data.frame(snp = rownames(dos), chr = "1", pos = 1:200) hits <- call_cis_eqtls(expr, dos, gp, sp) head(hits$best)
Regresses the exposure X on the instrument G (plus optional
covariates) and reports the partial F statistic and partial R2 for
the instrument — the weak-instrument diagnostic. The partial F
(not the overall model F) is the relevant quantity when covariates
such as ancestry PCs explain much of the exposure.
check_instrument_strength(G, X, covariates = NULL, min_f = 10)check_instrument_strength(G, X, covariates = NULL, min_f = 10)
G |
Numeric vector (length n): the exposure instrument
(e.g. a polygenic score from |
X |
Numeric vector (length n): the exposure. |
covariates |
Optional data.frame or matrix (n rows) of covariates to partial out (e.g. ancestry PCs). |
min_f |
Numeric: weak-instrument threshold on the partial F. Default 10 (Staiger-Stock rule of thumb). |
A list with F (partial F statistic), df1, df2,
pvalue, partial_r2, n, and weak (TRUE when F < min_f).
n <- 300 G <- rnorm(n) X <- 0.3 * G + rnorm(n) pcs <- matrix(rnorm(n * 3), n, 3, dimnames = list(NULL, paste0("PC", 1:3))) check_instrument_strength(G, X, covariates = pcs)n <- 300 G <- rnorm(n) X <- 0.3 * G + rnorm(n) pcs <- matrix(rnorm(n * 3), n, 3, dimnames = list(NULL, paste0("PC", 1:3))) check_instrument_strength(G, X, covariates = pcs)
Verifies the package is installed, libtorch is present, and a basic tensor
op succeeds. Used to gate the GAN path; a FALSE result triggers the
multivariate-normal fallback.
check_torch_setup()check_torch_setup()
TRUE if torch is usable, otherwise FALSE.
check_torch_setup()check_torch_setup()
Computes the Huang (2019) JT-comp composite p-value for testing H0: alpha * beta = 0 against H1: alpha * beta != 0.
composite_p_value(a, b, var_a = 1, var_b = 1)composite_p_value(a, b, var_a = 1, var_b = 1)
a |
Standardized z-statistic for alpha (alpha_hat / SE). |
b |
Standardized z-statistic for beta (beta_hat / SE). |
var_a |
Variance of the z-statistic for alpha across the collection of tests. Under the point null this is ~1; under H0(2)/H0(3) it is >1. Default 1 (conservative). |
var_b |
Variance of the z-statistic for beta across the collection of tests. Default 1. |
The composite null decomposes into three cases (alpha=beta=0, alpha!=0/beta=0, alpha=0/beta!=0). The Wald/Sobel test is conservative under the first case because the product of two independent normals follows a normal product distribution, not a normal. This function computes a closed-form p-value that accounts for all three cases without estimating their proportions.
Numeric scalar in .
Huang, Y.-T. (2019). Genome-wide analyses of sparse mediation effects under composite null hypotheses. Annals of Applied Statistics, 13(1), 60-84.
composite_p_value(0, 0) # null: p = 1 composite_p_value(2, 2) # strong signal: small p composite_p_value(2, 0) # H0(2): p = 1 (no mediation)composite_p_value(0, 0) # null: p = 1 composite_p_value(2, 2) # strong signal: small p composite_p_value(2, 0) # H0(2): p = 1 (no mediation)
Implements the Correlated Outcome Control Approach (COCA). Fits
w ~ y + X + covars and recovers the causal effect as
. Standard errors are
obtained via the delta method.
fit_coca(y, X, w, covars = NULL, ratio_cap = 10, se_cap = 5)fit_coca(y, X, w, covars = NULL, ratio_cap = 10, se_cap = 5)
y |
Numeric primary outcome vector (length n). |
X |
Numeric exposure vector (length n). |
w |
Numeric negative-control outcome vector (length n).
Recommended: pass |
covars |
Optional data frame of additional covariates (n rows). |
ratio_cap |
Maximum absolute value of the ratio estimate before
flagging as unstable and returning |
se_cap |
Maximum SE before flagging as unstable. Default 5. |
The negative-control W should be an outcome that shares the same unmeasured confounders as Y but has no direct causal path from X.
Named list: beta, se, pvalue.
Returns list(beta=NA, se=NA, pvalue=NA) if estimation is
unstable (near-zero or extreme ratio).
set.seed(1) dat <- generate_toy_data(n = 200, seed = 1) fit_coca(dat$Y[, 1], dat$X, rowMeans(dat$W))set.seed(1) dat <- generate_toy_data(n = 200, seed = 1) fit_coca(dat$Y[, 1], dat$X, rowMeans(dat$W))
COCA (Correlated Outcome Control Approach) regresses the negative
control W on the outcome Y (W ~ y + X) and recovers the causal
effect as a ratio . That
identification argument assumes a linear structural outcome model:
the W-Y regression coefficient must be proportional to the
confounder-Outcome association on the same scale as the causal
effect. With a binary outcome the structural model is nonlinear
(logistic), so the linear COCA ratio recovers neither the causal
log-odds ratio nor the risk difference. COCA is therefore
unsupported for binary outcomes and always returns
list(beta=NA, se=NA, pvalue=NA).
fit_coca_bin(y, X, w, covars = NULL, ...)fit_coca_bin(y, X, w, covars = NULL, ...)
y |
Numeric 0/1 outcome vector (length n). |
X |
Numeric exposure vector (length n). |
w |
Numeric NC vector (length n). |
covars |
Optional data frame of covariates (n rows). |
... |
Ignored (accepted for signature compatibility). |
list(beta = NA, se = NA, pvalue = NA) with an
informative "reason" attribute.
set.seed(1) dat <- generate_toy_data(n = 200, outcome_type = "binary", seed = 1) fit_coca_bin(dat$y_bin, dat$X, dat$W[, 1]) # $beta [1] NA (COCA unsupported for binary outcomes)set.seed(1) dat <- generate_toy_data(n = 200, outcome_type = "binary", seed = 1) fit_coca_bin(dat$y_bin, dat$X, dat$W[, 1]) # $beta [1] NA (COCA unsupported for binary outcomes)
Uses the negative-control outcome W to calibrate both the mediator
and outcome regressions, extending fit_coca to the
mediation setting.
fit_coca_mediation(y, X, M, w, covars = NULL, ratio_cap = 10)fit_coca_mediation(y, X, M, w, covars = NULL, ratio_cap = 10)
y |
Numeric primary outcome vector (length n). |
X |
Numeric exposure vector (length n). |
M |
Numeric mediator vector (length n). |
w |
Numeric negative-control outcome vector (length n).
Recommended: pass |
covars |
Optional data frame of additional covariates (n rows). |
ratio_cap |
Maximum absolute value of any ratio estimate before flagging as unstable. Default 10. |
Stage 1: W ~ M + X -> calibrated alpha_M = -beta_X / beta_M.
Stage 2: W ~ Y + X + M -> calibrated NDE = -beta_X / beta_Y,
calibrated beta_M = -beta_M / beta_Y.
NIE = alpha_M * beta_M (both calibrated).
Named list: NDE, NDE_se, NDE_p,
NIE, NIE_se, NIE_p.
set.seed(1) dat <- generate_toy_data(n = 200, mo_confounding = 0.8, seed = 1) fit_coca_mediation(dat$Y[, 1], dat$X, dat$M, rowMeans(dat$W))set.seed(1) dat <- generate_toy_data(n = 200, mo_confounding = 0.8, seed = 1) fit_coca_mediation(dat$Y[, 1], dat$X, dat$M, rowMeans(dat$W))
COCA mediation regresses the negative control W on the outcome Y in
both stages (W ~ M + X, W ~ y + X + M) and recovers the
effect as a ratio of regression coefficients. That identification
argument assumes a linear structural outcome model; with a binary
(nonlinear, logistic) outcome the ratio recovers neither the causal
log-odds ratio nor the risk difference. COCA mediation is therefore
unsupported for binary outcomes and always returns NA.
fit_coca_mediation_bin(y, X, M, w, covars = NULL, ...)fit_coca_mediation_bin(y, X, M, w, covars = NULL, ...)
y |
Numeric 0/1 outcome vector (length n). |
X |
Numeric exposure vector (length n). |
M |
Numeric mediator vector (length n). |
w |
Numeric NC vector (length n). |
covars |
Optional data frame of covariates (n rows). |
... |
Ignored (signature compatibility). |
All-NA list with an informative "reason" attribute.
set.seed(1) dat <- generate_toy_data(n = 200, outcome_type = "binary", seed = 1) fit_coca_mediation_bin(dat$y_bin, dat$X, dat$M, dat$W[, 1]) # all NA (COCA mediation unsupported for binary outcomes)set.seed(1) dat <- generate_toy_data(n = 200, outcome_type = "binary", seed = 1) fit_coca_mediation_bin(dat$y_bin, dat$X, dat$M, dat$W[, 1]) # all NA (COCA mediation unsupported for binary outcomes)
COCA mediation regresses the negative control W on the outcome Y in
both stages (W ~ M + X, W ~ y + X + M), placing the
outcome on the right-hand side. This is structurally impossible when
the outcome is a survival::Surv object. COCA mediation is
therefore unsupported for survival outcomes and always returns NA.
fit_coca_mediation_surv(time, event, X, M, w, covars = NULL, ...)fit_coca_mediation_surv(time, event, X, M, w, covars = NULL, ...)
time |
Numeric follow-up time vector (length n). |
event |
Numeric 0/1 event indicator (length n). |
X |
Numeric exposure vector (length n). |
M |
Numeric mediator vector (length n). |
w |
Numeric NC vector (length n). |
covars |
Optional data frame of covariates (n rows). |
... |
Ignored (signature compatibility). |
All-NA list with an informative "reason" attribute.
set.seed(1) dat <- generate_toy_data(n = 200, outcome_type = "survival", seed = 1) fit_coca_mediation_surv(dat$surv_time, dat$surv_event, dat$X, dat$M, dat$W[, 1]) # all NA (COCA mediation unsupported for survival outcomes)set.seed(1) dat <- generate_toy_data(n = 200, outcome_type = "survival", seed = 1) fit_coca_mediation_surv(dat$surv_time, dat$surv_event, dat$X, dat$M, dat$W[, 1]) # all NA (COCA mediation unsupported for survival outcomes)
COCA (Correlated Outcome Control Approach) regresses the negative
control W on the outcome Y (W ~ y + X) and recovers the causal
effect as a ratio . This places the
outcome on the right-hand side of the regression, which is
structurally impossible when the outcome is a survival::Surv
object (time-to-event). COCA is therefore unsupported for survival
outcomes and always returns list(beta=NA, se=NA, pvalue=NA).
fit_coca_surv(time, event, X, w, covars = NULL, ...)fit_coca_surv(time, event, X, w, covars = NULL, ...)
time |
Numeric follow-up time vector (length n). |
event |
Numeric 0/1 event indicator (length n). |
X |
Numeric exposure vector (length n). |
w |
Numeric NC vector (length n). |
covars |
Optional data frame of covariates (n rows). |
... |
Ignored (accepted for signature compatibility). |
list(beta = NA, se = NA, pvalue = NA) with an
informative "reason" attribute.
set.seed(1) dat <- generate_toy_data(n = 200, outcome_type = "survival", seed = 1) fit_coca_surv(dat$surv_time, dat$surv_event, dat$X, dat$W[, 1]) # $beta [1] NA (COCA unsupported for survival outcomes)set.seed(1) dat <- generate_toy_data(n = 200, outcome_type = "survival", seed = 1) fit_coca_surv(dat$surv_time, dat$surv_event, dat$X, dat$W[, 1]) # $beta [1] NA (COCA unsupported for survival outcomes)
Regresses Y on X plus the genetic instrument G, the negative-control W, and any additional covariates. This is a "naive" adjustment that uses whatever observables are available but does NOT correct for unmeasured confounding via a ratio or IV approach.
fit_direct(y, X, g, w, covars = NULL)fit_direct(y, X, g, w, covars = NULL)
y |
Numeric outcome vector (length n). |
X |
Numeric exposure vector (length n), assumed pre-scaled. |
g |
Numeric instrument vector (length n). |
w |
Numeric negative-control vector (length n) or matrix (n x q). When a matrix, all q columns are included as separate covariates. |
covars |
Optional data frame of additional covariates (n rows). |
Named list: beta, se, pvalue.
set.seed(1) dat <- generate_toy_data(n = 200, seed = 1) fit_direct(dat$Y[, 1], dat$X, dat$G[, 1], dat$W[, 1])set.seed(1) dat <- generate_toy_data(n = 200, seed = 1) fit_direct(dat$Y[, 1], dat$X, dat$G[, 1], dat$W[, 1])
Regresses the binary outcome on X plus the genetic instrument G, the negative-control panel W, and covariates. Naive adjustment; does not correct for unmeasured confounding via a ratio or IV approach.
fit_direct_bin( y, X, g, w, covars = NULL, effect_scale = c("logor", "riskdiff") )fit_direct_bin( y, X, g, w, covars = NULL, effect_scale = c("logor", "riskdiff") )
y |
Numeric 0/1 outcome vector (length n). |
X |
Numeric exposure vector (length n). |
g |
Numeric instrument vector (length n). |
w |
Numeric NC vector (length n) or matrix (n x q). |
covars |
Optional data frame of covariates (n rows). |
effect_scale |
Character: |
Named list: beta, se, pvalue.
set.seed(1) dat <- generate_toy_data(n = 200, outcome_type = "binary", seed = 1) fit_direct_bin(dat$y_bin, dat$X, dat$G[, 1], dat$W[, 1])set.seed(1) dat <- generate_toy_data(n = 200, outcome_type = "binary", seed = 1) fit_direct_bin(dat$y_bin, dat$X, dat$G[, 1], dat$W[, 1])
Adjusts for the genetic instrument G and negative-control W in both
the mediator and outcome regressions. Like fit_direct,
this is a naive adjustment that does not correct for unmeasured
confounding via a ratio or IV approach.
fit_direct_mediation(y, X, M, g, w, covars = NULL)fit_direct_mediation(y, X, M, g, w, covars = NULL)
y |
Numeric outcome vector (length n). |
X |
Numeric exposure vector (length n). |
M |
Numeric mediator vector (length n). |
g |
Numeric instrument vector (length n). |
w |
Numeric negative-control vector (length n). |
covars |
Optional data frame of additional covariates (n rows). |
Named list: NDE, NDE_se, NDE_p,
NIE, NIE_se, NIE_p.
set.seed(1) dat <- generate_toy_data(n = 200, mo_confounding = 0.8, seed = 1) fit_direct_mediation(dat$Y[, 1], dat$X, dat$M, dat$G[, 1], dat$W[, 1])set.seed(1) dat <- generate_toy_data(n = 200, mo_confounding = 0.8, seed = 1) fit_direct_mediation(dat$Y[, 1], dat$X, dat$M, dat$G[, 1], dat$W[, 1])
Adjusts for the instrument G and negative-control W in both the mediator (OLS) and outcome (logistic / LPM) stages. Naive adjustment.
fit_direct_mediation_bin( y, X, M, g, w, covars = NULL, effect_scale = c("logor", "riskdiff") )fit_direct_mediation_bin( y, X, M, g, w, covars = NULL, effect_scale = c("logor", "riskdiff") )
y |
Numeric 0/1 outcome vector (length n). |
X |
Numeric exposure vector (length n). |
M |
Numeric mediator vector (length n). |
g |
Numeric instrument vector (length n). |
w |
Numeric NC vector (length n) or matrix (n x q). |
covars |
Optional data frame of covariates (n rows). |
effect_scale |
Character: |
Named list (same fields as fit_unadj_mediation_bin).
set.seed(1) dat <- generate_toy_data(n = 200, outcome_type = "binary", mo_confounding = 0.8, seed = 1) fit_direct_mediation_bin(dat$y_bin, dat$X, dat$M, dat$G[, 1], dat$W[, 1])set.seed(1) dat <- generate_toy_data(n = 200, outcome_type = "binary", mo_confounding = 0.8, seed = 1) fit_direct_mediation_bin(dat$y_bin, dat$X, dat$M, dat$G[, 1], dat$W[, 1])
Adjusts for the instrument G and negative-control W in both the mediator (OLS) and outcome (Cox / RMST) stages. Naive adjustment.
fit_direct_mediation_surv( time, event, X, M, g, w, covars = NULL, effect_scale = c("loghr", "rmst"), tau = NULL )fit_direct_mediation_surv( time, event, X, M, g, w, covars = NULL, effect_scale = c("loghr", "rmst"), tau = NULL )
time |
Numeric follow-up time vector (length n). |
event |
Numeric 0/1 event indicator (length n). |
X |
Numeric exposure vector (length n). |
M |
Numeric mediator vector (length n). |
g |
Numeric instrument vector (length n). |
w |
Numeric NC vector (length n) or matrix (n x q). |
covars |
Optional data frame of covariates (n rows). |
effect_scale |
Character: |
tau |
RMST horizon (rmst only). |
Named list (same fields as fit_unadj_mediation_surv).
set.seed(1) dat <- generate_toy_data(n = 200, outcome_type = "survival", mo_confounding = 0.8, seed = 1) fit_direct_mediation_surv(dat$surv_time, dat$surv_event, dat$X, dat$M, dat$G[, 1], dat$W[, 1])set.seed(1) dat <- generate_toy_data(n = 200, outcome_type = "survival", mo_confounding = 0.8, seed = 1) fit_direct_mediation_surv(dat$surv_time, dat$surv_event, dat$X, dat$M, dat$G[, 1], dat$W[, 1])
Regresses the survival outcome on X plus the genetic instrument G, the negative-control panel W, and covariates. Naive adjustment; does not correct for unmeasured confounding via a ratio or IV approach.
fit_direct_surv( time, event, X, g, w, covars = NULL, effect_scale = c("loghr", "rmst"), tau = NULL )fit_direct_surv( time, event, X, g, w, covars = NULL, effect_scale = c("loghr", "rmst"), tau = NULL )
time |
Numeric follow-up time vector (length n). |
event |
Numeric 0/1 event indicator (length n). |
X |
Numeric exposure vector (length n). |
g |
Numeric instrument vector (length n). |
w |
Numeric NC vector (length n) or matrix (n x q). |
covars |
Optional data frame of covariates (n rows). |
effect_scale |
Character: |
tau |
RMST horizon (rmst only). |
Named list: beta, se, pvalue.
set.seed(1) dat <- generate_toy_data(n = 200, outcome_type = "survival", seed = 1) fit_direct_surv(dat$surv_time, dat$surv_event, dat$X, dat$G[, 1], dat$W[, 1])set.seed(1) dat <- generate_toy_data(n = 200, outcome_type = "survival", seed = 1) fit_direct_surv(dat$surv_time, dat$surv_event, dat$X, dat$G[, 1], dat$W[, 1])
Uses the genetic instrument G to instrument for the exposure X, controlling for the negative-control W and any additional covariates. Requires AER.
fit_iv2sls(y, X, g, w, covars = NULL, min_f = 10)fit_iv2sls(y, X, g, w, covars = NULL, min_f = 10)
y |
Numeric outcome vector (length n). |
X |
Numeric exposure vector (length n). |
g |
Numeric instrument vector (length n). |
w |
Numeric negative-control vector (length n). |
covars |
Optional data frame of additional covariates (n rows). |
min_f |
Minimum acceptable partial F-statistic for the excluded instrument. Default 10. |
A weak-instrument check is applied using the partial F-statistic for the
excluded instrument G (testing G conditional on W and covariates in the
first stage), following Stock & Yogo (2005). If the partial F is below
min_f, the function returns NA.
Named list: beta, se, pvalue.
set.seed(1) dat <- generate_toy_data(n = 200, seed = 1) fit_iv2sls(dat$Y[, 1], dat$X, dat$G[, 1], dat$W[, 1])set.seed(1) dat <- generate_toy_data(n = 200, seed = 1) fit_iv2sls(dat$Y[, 1], dat$X, dat$G[, 1], dat$W[, 1])
Two-stage predictor substitution (2SPS): the first stage regresses X on
the instrument G (plus W and covariates) via OLS, producing fitted
; the second stage regresses the binary outcome on
(plus W and covariates) via logistic regression (log-OR)
or a linear probability model (risk difference).
fit_iv2sls_bin( y, X, g, w, covars = NULL, min_f = 10, effect_scale = c("logor", "riskdiff") )fit_iv2sls_bin( y, X, g, w, covars = NULL, min_f = 10, effect_scale = c("logor", "riskdiff") )
y |
Numeric 0/1 outcome vector (length n). |
X |
Numeric exposure vector (length n). |
g |
Numeric instrument vector (length n). |
w |
Numeric NC vector (length n) or matrix (n x q). |
covars |
Optional data frame of covariates (n rows). |
min_f |
Minimum partial F for the excluded instrument. Default 10. |
effect_scale |
Character: |
A weak-instrument check (partial F for the excluded instrument G,
Stock & Yogo 2005) is applied to the OLS first stage. If the partial
F is below min_f, the function returns NA.
Named list: beta, se, pvalue.
set.seed(1) dat <- generate_toy_data(n = 200, outcome_type = "binary", seed = 1) fit_iv2sls_bin(dat$y_bin, dat$X, dat$G[, 1], dat$W[, 1])set.seed(1) dat <- generate_toy_data(n = 200, outcome_type = "binary", seed = 1) fit_iv2sls_bin(dat$y_bin, dat$X, dat$G[, 1], dat$W[, 1])
Uses the genetic instrument G to purge U1 from X, then estimates the mediator and outcome regressions with the cleaned exposure.
fit_iv2sls_mediation(y, X, M, g, w, covars = NULL, min_f = 10)fit_iv2sls_mediation(y, X, M, g, w, covars = NULL, min_f = 10)
y |
Numeric outcome vector (length n). |
X |
Numeric exposure vector (length n). |
M |
Numeric mediator vector (length n). |
g |
Numeric instrument vector (length n). |
w |
Numeric negative-control vector (length n). |
covars |
Optional data frame of additional covariates (n rows). |
min_f |
Minimum acceptable partial F-statistic for the excluded instrument. Default 10. |
Strategy:
X ~ G + W -> X_hat (purge U1 from X).
M ~ X_hat -> alpha_M (clean effect of X on M).
Y ~ X_hat + M + W -> NDE = beta_X, beta_M (OLS).
The IV cleans X of U1 confounding, but M remains endogenous via U1 -> M. With a single instrument, natural effects are not fully identified (Rudolph et al., 2024); NDE and NIE are approximations whose bias from M-O confounding is the key finding the simulation demonstrates.
Named list: NDE, NDE_se, NDE_p,
NIE, NIE_se, NIE_p.
Rudolph, K. E., et al. (2024). Natural direct and indirect effects with an instrumental variable. Biometrics.
set.seed(1) dat <- generate_toy_data(n = 300, mo_confounding = 0.8, seed = 1) fit_iv2sls_mediation(dat$Y[, 1], dat$X, dat$M, dat$G[, 1], dat$W[, 1])set.seed(1) dat <- generate_toy_data(n = 300, mo_confounding = 0.8, seed = 1) fit_iv2sls_mediation(dat$Y[, 1], dat$X, dat$M, dat$G[, 1], dat$W[, 1])
Two-stage predictor substitution with a single instrument (G for X, no mediator instrument):
OLS: X ~ g + W + covars -> X_hat (purge U1 from X).
OLS: M ~ X_hat + covars -> alpha_M.
Logistic / LPM: y ~ X_hat + M + W + covars ->
NDE (coef on X_hat), beta_M (coef on M).
NIE = alpha_M * beta_M. Weak-instrument gate (partial F for G)
applies to the OLS first stage. Unlike
fit_iv2sls_mediation2_bin, this estimator does not
instrument the mediator and is NOT point-identified under M-O
confounding — it is included for parity with the continuous
fit_iv2sls_mediation.
fit_iv2sls_mediation_bin( y, X, M, g, w, covars = NULL, min_f = 10, effect_scale = c("logor", "riskdiff") )fit_iv2sls_mediation_bin( y, X, M, g, w, covars = NULL, min_f = 10, effect_scale = c("logor", "riskdiff") )
y |
Numeric 0/1 outcome vector (length n). |
X |
Numeric exposure vector (length n). |
M |
Numeric mediator vector (length n). |
g |
Numeric instrument for X (length n). |
w |
Numeric NC vector (length n) or matrix (n x q). |
covars |
Optional data frame of covariates (n rows). |
min_f |
Minimum partial F for the excluded instrument. Default 10. |
effect_scale |
Character: |
Named list (same fields as fit_unadj_mediation_bin).
Returns all-NA if the first-stage partial F for G is below min_f.
set.seed(1) dat <- generate_toy_data(n = 500, outcome_type = "binary", mo_confounding = 0.8, seed = 1) fit_iv2sls_mediation_bin(dat$y_bin, dat$X, dat$M, dat$G[, 1], dat$W[, 1])set.seed(1) dat <- generate_toy_data(n = 500, outcome_type = "binary", mo_confounding = 0.8, seed = 1) fit_iv2sls_mediation_bin(dat$y_bin, dat$X, dat$M, dat$G[, 1], dat$W[, 1])
Two-stage predictor substitution with a single instrument (G for X, no mediator instrument):
OLS: X ~ g + W + covars -> X_hat (purge U1 from X).
OLS: M ~ X_hat + covars -> alpha_M.
Cox / RMST: Surv(t,e) ~ X_hat + M + W + covars ->
NDE (coef on X_hat), beta_M (coef on M).
NIE = alpha_M * beta_M. Weak-instrument gate (partial F for G)
applies to the OLS first stage. Unlike
fit_iv2sls_mediation2_surv, this estimator does not
instrument the mediator and is NOT point-identified under M-O
confounding — it is included for parity with the continuous
fit_iv2sls_mediation.
fit_iv2sls_mediation_surv( time, event, X, M, g, w, covars = NULL, min_f = 10, effect_scale = c("loghr", "rmst"), tau = NULL )fit_iv2sls_mediation_surv( time, event, X, M, g, w, covars = NULL, min_f = 10, effect_scale = c("loghr", "rmst"), tau = NULL )
time |
Numeric follow-up time vector (length n). |
event |
Numeric 0/1 event indicator (length n). |
X |
Numeric exposure vector (length n). |
M |
Numeric mediator vector (length n). |
g |
Numeric instrument for X (length n). |
w |
Numeric NC vector (length n) or matrix (n x q). |
covars |
Optional data frame of covariates (n rows). |
min_f |
Minimum partial F for the excluded instrument. Default 10. |
effect_scale |
Character: |
tau |
RMST horizon (rmst only). |
Named list (same fields as fit_unadj_mediation_surv).
set.seed(1) dat <- generate_toy_data(n = 200, outcome_type = "survival", seed = 1) fit_iv2sls_mediation_surv(dat$surv_time, dat$surv_event, dat$X, dat$M, dat$G[, 1], dat$W[, 1])set.seed(1) dat <- generate_toy_data(n = 200, outcome_type = "survival", seed = 1) fit_iv2sls_mediation_surv(dat$surv_time, dat$surv_event, dat$X, dat$M, dat$G[, 1], dat$W[, 1])
Uses TWO genetic instruments: G for the exposure X and Gm for the mediator M. This is the key extension: by instrumenting both endogenous variables, NDE and NIE become point-identified even under mediator-outcome (M-O) confounding, resolving the identification failure that limits the single-instrument estimators.
fit_iv2sls_mediation2( y, X, M, g, gm, covars = NULL, min_f = 10, W1 = NULL, W2 = NULL, w = NULL )fit_iv2sls_mediation2( y, X, M, g, gm, covars = NULL, min_f = 10, W1 = NULL, W2 = NULL, w = NULL )
y |
Numeric outcome vector (length n). |
X |
Numeric exposure vector (length n). |
M |
Numeric mediator vector (length n). |
g |
Numeric instrument for X (length n). |
gm |
Numeric instrument for M (length n). |
covars |
Optional data frame of additional covariates (n rows). |
min_f |
Minimum acceptable partial F-statistic for each excluded instrument. Default 10. |
W1 |
Optional negative-control panel (vector length n or matrix
n x q) proxying the exposure-mediator confounder (X->M path); added to
stage 1 only. Default |
W2 |
Optional negative-control panel (vector length n or matrix
n x q) proxying the mediator-outcome confounder (M->Y path); added to
stages 2 and 3. Default |
w |
Defunct. The pooled single-panel argument was removed
because conditioning on a pooled panel in all three stages opens
a collider path under multi-confounder designs. Use |
The motivating design uses two distinct genetic instruments: a mediator instrument (e.g. a cis-eQTL) instruments the mediator (M), while a separate exposure instrument (e.g. a polygenic score) instruments the exposure (X). The mediator set must be restricted to features for which a mediator instrument is available.
Strategy (sequential 2SLS, three OLS stages), with optional path-specific negative-control (NC) augmentation:
X ~ G (+ W1) + covars -> X_hat (purge U1 from X).
Weak-IV check: partial F for G >= min_f.
M ~ X_hat + Gm (+ W2) + covars -> M_hat, alpha_M = coef on X_hat.
Weak-IV check: partial F for Gm >= min_f.
Y ~ X_hat + M_hat (+ W2) + covars -> NDE = beta_X_hat, beta_M = coef on M_hat.
NIE = alpha_M * beta_M (delta-method SE).
Path-specific NC augmentation (optional). W1 proxies the
exposure-mediator confounder (U1, the X->M path) and is added to stage 1
only; W2 proxies the mediator-outcome confounder (U2, the M->Y
path) and is added to stages 2 and 3. Either panel may be omitted: with
W1 = NULL stage 1 is unaugmented, with W2 = NULL stages 2–3
are unaugmented, and with both NULL the estimator reduces to plain
two-instrument 2-stage MR. Conditioning on a pooled panel
(W1 + W2) / 2 in all three stages is a collider under
multi-confounder designs (the pooled panel is a common child of the
independent confounders U1 and U2) and is therefore not supported: if
W1 and W2 are identical they are treated as absent (pure
MR). Pass the two path-specific panels separately to obtain the
coverage-improves-accuracy behaviour.
Path-specific augmentation is only defined when W1 and W2
proxy distinct confounders. When the two panels are distinct-noise
proxies of the same latent composite (the single-confounder /
shared-loading design), their column spaces are near-collinear and
stage-1 W1 would inject the shared M->Y confounder into
X_hat. The estimator detects this (leading-PC correlation between
the panels) and falls back to plain two-instrument 2-stage MR, exactly as
for an identical pooled panel. Genuinely distinct panels are unaffected.
Including X_hat in the M first-stage (stage 2) is essential: M structurally depends on X, so the X -> M path must be captured for alpha_M and the NIE to be correctly estimated. Gm provides the exogenous variation that identifies the M -> Y effect net of U1 confounding.
The sequential 2SLS implementation matches the pattern of
fit_iv2sls_mediation (three separate OLS stages).
Standard errors in the outcome stage do not account for the
two-stage estimation of M_hat, consistent with the existing
estimators; the simulation benchmarks bias, not SE accuracy. A unit
test cross-validates this estimator's NDE and beta_M against
AER::ivreg on the just-identified system
(Y ~ X + M | G + Gm, pure-MR form).
Named list: NDE, NDE_se, NDE_p,
NIE, NIE_se, NIE_p. Returns all-NA if
either first-stage partial F is below min_f.
Rudolph, K. E., et al. (2024). Natural direct and indirect effects with an instrumental variable. Biometrics.
set.seed(1) dat <- generate_toy_data(n = 500, mo_confounding = 0.8, phi = 0.8, lambda_XM = c(1, 0), lambda_MY = c(0, 1), omega_1 = 0.7, omega_2 = 0.7, seed = 1) fit_iv2sls_mediation2(dat$Y[, 1], dat$X, dat$M, dat$G[, 1], dat$Gm, W1 = dat$W1, W2 = dat$W2)set.seed(1) dat <- generate_toy_data(n = 500, mo_confounding = 0.8, phi = 0.8, lambda_XM = c(1, 0), lambda_MY = c(0, 1), omega_1 = 0.7, omega_2 = 0.7, seed = 1) fit_iv2sls_mediation2(dat$Y[, 1], dat$X, dat$M, dat$G[, 1], dat$Gm, W1 = dat$W1, W2 = dat$W2)
Two-stage Mendelian-randomization mediation with a binary outcome stage (2SPS), with optional path-specific negative-control (NC) augmentation:
OLS: X ~ g (+ W1) + covars -> X_hat (purge U1 from X).
OLS: M ~ X_hat + gm (+ W2) + covars -> M_hat, alpha_M.
Logistic / LPM: y ~ X_hat + M_hat (+ W2) + covars ->
NDE (coef on X_hat), beta_M (coef on M_hat).
NIE = alpha_M * beta_M. Weak-instrument gates (partial F for
G and Gm) apply to the OLS first stages.
fit_iv2sls_mediation2_bin( y, X, M, g, gm, covars = NULL, min_f = 10, W1 = NULL, W2 = NULL, w = NULL, effect_scale = c("logor", "riskdiff") )fit_iv2sls_mediation2_bin( y, X, M, g, gm, covars = NULL, min_f = 10, W1 = NULL, W2 = NULL, w = NULL, effect_scale = c("logor", "riskdiff") )
y |
Numeric 0/1 outcome vector (length n). |
X |
Numeric exposure vector (length n). |
M |
Numeric mediator vector (length n). |
g |
Numeric instrument for X (length n). |
gm |
Numeric instrument for M (length n). |
covars |
Optional data frame of covariates (n rows). |
min_f |
Minimum partial F for each excluded instrument. Default 10. |
W1 |
Optional NC panel (vector length n or matrix n x q) proxying
the exposure-mediator confounder (X->M path); added to stage 1 only.
Default |
W2 |
Optional NC panel (vector length n or matrix n x q) proxying
the mediator-outcome confounder (M->Y path); added to stages 2 and 3.
Default |
w |
Defunct. The pooled single-panel argument was removed
(collider under multi-confounder designs). Use |
effect_scale |
Character: |
Path-specific NC augmentation (optional). W1 proxies the
exposure-mediator confounder (U1, X->M path) and is added to stage 1
only; W2 proxies the mediator-outcome confounder (U2, M->Y path)
and is added to stages 2 and 3. Either panel may be omitted; with both
NULL the estimator reduces to plain two-instrument 2-stage MR.
Conditioning on a pooled panel in all three stages is a collider
under multi-confounder designs and is not supported: identical
W1/W2 are treated as absent (pure MR). When W1 and
W2 are distinct-noise proxies of the same latent composite
(single-confounder design), their column spaces are near-collinear and
the estimator likewise falls back to plain two-instrument 2-stage MR.
Named list (same fields as fit_unadj_mediation_bin).
Returns all-NA if either first-stage partial F is below min_f.
Rudolph, K. E., et al. (2024). Natural direct and indirect effects with an instrumental variable. Biometrics.
set.seed(1) dat <- generate_toy_data(n = 500, outcome_type = "binary", mo_confounding = 0.8, phi = 0.8, lambda_XM = c(1, 0), lambda_MY = c(0, 1), omega_1 = 0.7, omega_2 = 0.7, seed = 1) fit_iv2sls_mediation2_bin(dat$y_bin, dat$X, dat$M, dat$G[, 1], dat$Gm, W1 = dat$W1, W2 = dat$W2)set.seed(1) dat <- generate_toy_data(n = 500, outcome_type = "binary", mo_confounding = 0.8, phi = 0.8, lambda_XM = c(1, 0), lambda_MY = c(0, 1), omega_1 = 0.7, omega_2 = 0.7, seed = 1) fit_iv2sls_mediation2_bin(dat$y_bin, dat$X, dat$M, dat$G[, 1], dat$Gm, W1 = dat$W1, W2 = dat$W2)
Two-stage Mendelian-randomization mediation with a survival outcome stage (2SPS), with optional path-specific negative-control (NC) augmentation:
OLS: X ~ g (+ W1) + covars -> X_hat (purge U1 from X).
OLS: M ~ X_hat + gm (+ W2) + covars -> M_hat, alpha_M.
Cox / RMST: Surv(t,e) ~ X_hat + M_hat (+ W2) + covars ->
NDE (coef on X_hat), beta_M (coef on M_hat).
NIE = alpha_M * beta_M. Weak-instrument gates (partial F for
G and Gm) apply to the OLS first stages.
fit_iv2sls_mediation2_surv( time, event, X, M, g, gm, covars = NULL, min_f = 10, W1 = NULL, W2 = NULL, w = NULL, effect_scale = c("loghr", "rmst"), tau = NULL )fit_iv2sls_mediation2_surv( time, event, X, M, g, gm, covars = NULL, min_f = 10, W1 = NULL, W2 = NULL, w = NULL, effect_scale = c("loghr", "rmst"), tau = NULL )
time |
Numeric follow-up time vector (length n). |
event |
Numeric 0/1 event indicator (length n). |
X |
Numeric exposure vector (length n). |
M |
Numeric mediator vector (length n). |
g |
Numeric instrument for X (length n). |
gm |
Numeric instrument for M (length n). |
covars |
Optional data frame of covariates (n rows). |
min_f |
Minimum partial F for each excluded instrument. Default 10. |
W1 |
Optional NC panel (vector length n or matrix n x q) proxying
the exposure-mediator confounder (X->M path); added to stage 1 only.
Default |
W2 |
Optional NC panel (vector length n or matrix n x q) proxying
the mediator-outcome confounder (M->Y path); added to stages 2 and 3.
Default |
w |
Defunct. The pooled single-panel argument was removed
(collider under multi-confounder designs). Use |
effect_scale |
Character: |
tau |
RMST horizon (rmst only). |
Path-specific NC augmentation (optional). W1 proxies the
exposure-mediator confounder (U1, X->M path) and is added to stage 1
only; W2 proxies the mediator-outcome confounder (U2, M->Y path)
and is added to stages 2 and 3. Either panel may be omitted; with both
NULL the estimator reduces to plain two-instrument 2-stage MR.
Conditioning on a pooled panel in all three stages is a collider
under multi-confounder designs and is not supported: identical
W1/W2 are treated as absent (pure MR). When W1 and
W2 are distinct-noise proxies of the same latent composite
(single-confounder design), their column spaces are near-collinear and
the estimator likewise falls back to plain two-instrument 2-stage MR.
Named list (same fields as fit_unadj_mediation_surv).
Returns all-NA if either first-stage partial F is below min_f.
Rudolph, K. E., et al. (2024). Natural direct and indirect effects with an instrumental variable. Biometrics.
set.seed(1) dat <- generate_toy_data(n = 500, outcome_type = "survival", mo_confounding = 0.8, phi = 0.8, lambda_XM = c(1, 0), lambda_MY = c(0, 1), omega_1 = 0.7, omega_2 = 0.7, seed = 1) fit_iv2sls_mediation2_surv(dat$surv_time, dat$surv_event, dat$X, dat$M, dat$G[, 1], dat$Gm, W1 = dat$W1, W2 = dat$W2)set.seed(1) dat <- generate_toy_data(n = 500, outcome_type = "survival", mo_confounding = 0.8, phi = 0.8, lambda_XM = c(1, 0), lambda_MY = c(0, 1), omega_1 = 0.7, omega_2 = 0.7, seed = 1) fit_iv2sls_mediation2_surv(dat$surv_time, dat$surv_event, dat$X, dat$M, dat$G[, 1], dat$Gm, W1 = dat$W1, W2 = dat$W2)
Two-stage predictor substitution (2SPS): the first stage regresses X on
the instrument G (plus W and covariates) via OLS, producing fitted
; the second stage regresses the survival outcome on
(plus W and covariates) via Cox (log-HR) or RMST
pseudo-observation OLS.
fit_iv2sls_surv( time, event, X, g, w, covars = NULL, min_f = 10, effect_scale = c("loghr", "rmst"), tau = NULL )fit_iv2sls_surv( time, event, X, g, w, covars = NULL, min_f = 10, effect_scale = c("loghr", "rmst"), tau = NULL )
time |
Numeric follow-up time vector (length n). |
event |
Numeric 0/1 event indicator (length n). |
X |
Numeric exposure vector (length n). |
g |
Numeric instrument vector (length n). |
w |
Numeric NC vector (length n) or matrix (n x q). |
covars |
Optional data frame of covariates (n rows). |
min_f |
Minimum partial F for the excluded instrument. Default 10. |
effect_scale |
Character: |
tau |
RMST horizon (rmst only). |
A weak-instrument check (partial F for the excluded instrument G,
Stock & Yogo 2005) is applied to the OLS first stage. If the partial
F is below min_f, the function returns NA.
Named list: beta, se, pvalue.
set.seed(1) dat <- generate_toy_data(n = 200, outcome_type = "survival", seed = 1) fit_iv2sls_surv(dat$surv_time, dat$surv_event, dat$X, dat$G[, 1], dat$W[, 1])set.seed(1) dat <- generate_toy_data(n = 200, outcome_type = "survival", seed = 1) fit_iv2sls_surv(dat$surv_time, dat$surv_event, dat$X, dat$G[, 1], dat$W[, 1])
A three-step bridge-function estimator:
Residualises X on G to isolate the U-driven component X_resid.
Regresses X_resid on the FULL W matrix to construct W_hat,
a proxy for unmeasured confounding. This step requires
ncol(W) >= k (the proximal completeness condition):
if W has fewer valid columns than confounders, the bridge
cannot span the confounder subspace and the estimator is
under-identified.
Fits Y ~ X + W_hat to absorb confounding bias.
fit_pgc(y, X, g, W, covars = NULL)fit_pgc(y, X, g, W, covars = NULL)
y |
Numeric outcome vector (length n). |
X |
Numeric exposure vector (length n). |
g |
Numeric instrument vector (length n). |
W |
Numeric negative-control matrix (n x q) or vector (length n). If a matrix, the bridge uses all q columns. Pass only validity-screened columns for the completeness condition to be meaningful. |
covars |
Optional data frame of additional covariates (n rows). |
Unlike the scalar version (fit_pgc_scalar), which
collapses W to rowMeans(W) and is algebraically equivalent
to IV/2SLS, the matrix bridge preserves the dimensional structure
of W and is the estimator for which the completeness condition is
binding.
Named list: beta, se, pvalue.
set.seed(1) dat <- generate_toy_data(n = 200, seed = 1) fit_pgc(dat$Y[, 1], dat$X, dat$G[, 1], dat$W)set.seed(1) dat <- generate_toy_data(n = 200, seed = 1) fit_pgc(dat$Y[, 1], dat$X, dat$G[, 1], dat$W)
Three-step bridge-function estimator with a binary outcome stage:
Residualise X on G -> X_resid (OLS).
Bridge X_resid on the FULL W matrix -> W_hat (OLS).
Regress the binary outcome on X + W_hat via logistic regression (log-OR) or a linear probability model (risk difference).
fit_pgc_bin(y, X, g, W, covars = NULL, effect_scale = c("logor", "riskdiff"))fit_pgc_bin(y, X, g, W, covars = NULL, effect_scale = c("logor", "riskdiff"))
y |
Numeric 0/1 outcome vector (length n). |
X |
Numeric exposure vector (length n). |
g |
Numeric instrument vector (length n). |
W |
Numeric NC matrix (n x q) or vector (length n). |
covars |
Optional data frame of covariates (n rows). |
effect_scale |
Character: |
Named list: beta, se, pvalue.
set.seed(1) dat <- generate_toy_data(n = 200, outcome_type = "binary", seed = 1) fit_pgc_bin(dat$y_bin, dat$X, dat$G[, 1], dat$W)set.seed(1) dat <- generate_toy_data(n = 200, outcome_type = "binary", seed = 1) fit_pgc_bin(dat$y_bin, dat$X, dat$G[, 1], dat$W)
Extends fit_pgc (matrix bridge) to the mediation setting
by constructing a confounding proxy from the full W matrix
and including it in both the mediator and outcome regressions.
fit_pgc_mediation(y, X, M, g, W, covars = NULL)fit_pgc_mediation(y, X, M, g, W, covars = NULL)
y |
Numeric outcome vector (length n). |
X |
Numeric exposure vector (length n). |
M |
Numeric mediator vector (length n). |
g |
Numeric instrument vector (length n). |
W |
Numeric negative-control matrix (n x q) or vector (length n). If a matrix, the bridge uses all q columns. |
covars |
Optional data frame of additional covariates (n rows). |
Steps:
Residualise X on G -> X_resid (U-driven residual).
Bridge X_resid on the FULL W matrix -> W_hat (proxy for U).
M ~ X + W_hat -> alpha_M (adjusted for confounding proxy).
Y ~ X + M + W_hat -> NDE = beta_X, beta_M (adjusted).
The matrix bridge requires ncol(W) >= k (proximal completeness)
for the bridge to span the confounder subspace.
Named list: NDE, NDE_se, NDE_p,
NIE, NIE_se, NIE_p.
set.seed(1) dat <- generate_toy_data(n = 200, mo_confounding = 0.8, seed = 1) fit_pgc_mediation(dat$Y[, 1], dat$X, dat$M, dat$G[, 1], dat$W)set.seed(1) dat <- generate_toy_data(n = 200, mo_confounding = 0.8, seed = 1) fit_pgc_mediation(dat$Y[, 1], dat$X, dat$M, dat$G[, 1], dat$W)
Bridge-function-adjusted natural direct and indirect effects with a
binary outcome stage, using a single negative-control panel W for
both the X->M and M->Y confounding paths. This is the binary
analogue of fit_pgc_mediation (continuous outcome):
OLS: residualise X on G -> X_resid.
OLS: bridge X_resid on the FULL W matrix -> W_hat (proxy for U).
OLS: M ~ X + W_hat + covars -> alpha_M.
Logistic / LPM: y ~ X + M + W_hat + covars ->
NDE (coef on X), beta_M (coef on M).
NIE = alpha_M * beta_M. Unlike
fit_pgc_mediation2_bin, which uses path-specific W1/W2
bridges, this estimator uses a single combined W panel and is
appropriate when separate conf_XM / conf_MY confounders are not assumed.
fit_pgc_mediation_bin( y, X, M, g, W, covars = NULL, min_f = 10, effect_scale = c("logor", "riskdiff") )fit_pgc_mediation_bin( y, X, M, g, W, covars = NULL, min_f = 10, effect_scale = c("logor", "riskdiff") )
y |
Numeric 0/1 outcome vector (length n). |
X |
Numeric exposure vector (length n). |
M |
Numeric mediator vector (length n). |
g |
Numeric instrument for X (length n). |
W |
Numeric NC matrix (n x q) or vector (length n). |
covars |
Optional data frame of covariates (n rows). |
min_f |
Minimum partial F for G. Default 10. |
effect_scale |
Character: |
Named list (same fields as fit_unadj_mediation_bin).
set.seed(1) dat <- generate_toy_data(n = 500, outcome_type = "binary", mo_confounding = 0.8, seed = 1) fit_pgc_mediation_bin(dat$y_bin, dat$X, dat$M, dat$G[, 1], dat$W)set.seed(1) dat <- generate_toy_data(n = 500, outcome_type = "binary", mo_confounding = 0.8, seed = 1) fit_pgc_mediation_bin(dat$y_bin, dat$X, dat$M, dat$G[, 1], dat$W)
Bridge-function-adjusted natural direct and indirect effects with a
survival outcome stage, using a single negative-control panel W for
both the X->M and M->Y confounding paths. This is the survival
analogue of fit_pgc_mediation (continuous outcome):
OLS: residualise X on G -> X_resid.
OLS: bridge X_resid on the FULL W matrix -> W_hat (proxy for U).
OLS: M ~ X + W_hat + covars -> alpha_M.
Cox / RMST: Surv(t,e) ~ X + M + W_hat + covars ->
NDE (coef on X), beta_M (coef on M).
NIE = alpha_M * beta_M. Unlike
fit_pgc_mediation2_surv, which uses path-specific W1/W2
bridges, this estimator uses a single combined W panel and is
appropriate when separate conf_XM / conf_MY confounders are not assumed.
fit_pgc_mediation_surv( time, event, X, M, g, W, covars = NULL, min_f = 10, effect_scale = c("loghr", "rmst"), tau = NULL )fit_pgc_mediation_surv( time, event, X, M, g, W, covars = NULL, min_f = 10, effect_scale = c("loghr", "rmst"), tau = NULL )
time |
Numeric follow-up time vector (length n). |
event |
Numeric 0/1 event indicator (length n). |
X |
Numeric exposure vector (length n). |
M |
Numeric mediator vector (length n). |
g |
Numeric instrument for X (length n). |
W |
Numeric NC matrix (n x q) or vector (length n). |
covars |
Optional data frame of covariates (n rows). |
min_f |
Minimum partial F for G. Default 10. |
effect_scale |
Character: |
tau |
RMST horizon (rmst only). |
Named list (same fields as fit_unadj_mediation_surv).
Returns all-NA if the first-stage partial F for G is below min_f.
set.seed(1) dat <- generate_toy_data(n = 200, outcome_type = "survival", seed = 1) fit_pgc_mediation_surv(dat$surv_time, dat$surv_event, dat$X, dat$M, dat$G[, 1], dat$W)set.seed(1) dat <- generate_toy_data(n = 200, outcome_type = "survival", seed = 1) fit_pgc_mediation_surv(dat$surv_time, dat$surv_event, dat$X, dat$M, dat$G[, 1], dat$W)
Extends the proximal-inference approach to the two-linked-DAG mediation setting. Uses path-specific negative controls — W1 capturing the X->M confounder conf_XM and W2 capturing the M->Y confounder conf_MY — to purge confounding at both stages via bridge functions.
fit_pgc_mediation2(y, X, M, g, W1, W2, gm = NULL, covars = NULL, min_f = 10)fit_pgc_mediation2(y, X, M, g, W1, W2, gm = NULL, covars = NULL, min_f = 10)
y |
Numeric outcome vector (length n). |
X |
Numeric exposure vector (length n). |
M |
Numeric mediator vector (length n). |
g |
Numeric instrument for X (length n). |
W1 |
Numeric negative-control matrix (n x q) or vector for the X->M path (captures conf_XM). If a matrix, the bridge uses all q columns. |
W2 |
Numeric negative-control matrix (n x q) or vector for the M->Y path (captures conf_MY). If a matrix, the bridge uses all q columns. |
gm |
Optional numeric mediator instrument vector (length n).
When |
covars |
Optional data frame of additional covariates (n rows). |
min_f |
Minimum acceptable partial F-statistic for the excluded instrument G1. Default 10. |
When gm is NULL (no mediator instrument), Stage 2 residualises
M on the cleaned exposure X_hat to isolate the conf_MY-driven component, then
bridges on W2. This is pure negative-control identification at both stages
— no mediator instrument is required.
When gm is supplied (mediator instrument present but its exogeneity
may be in doubt), Stage 2 uses Gm to help isolate conf_MY's effect on M before
bridging on W2. The bridge W_hat_M does the confounding removal, so the
estimator is robust to Gm being correlated with conf_MY — the residual bias
from Gm-U correlation is smaller than IV2SLS2's.
Strategy (three stages):
Bridge for X (purge conf_XM from X):
X_resid = residuals(X ~ G1 + C);
W_hat_X = bridge(X_resid ~ W1) (fitted values, proxy for conf_XM);
X_hat = fitted(X ~ G1 + W_hat_X + C).
Weak-IV check: partial F for G1 >= min_f.
Bridge for M (purge conf_MY from M):
M_resid = residuals(M ~ X_hat + C) (gm = NULL), or
M_resid = residuals(M ~ Gm + C) (gm supplied);
W_hat_M = bridge(M_resid ~ W2) (fitted values, proxy for conf_MY);
M_hat = fitted(M ~ X_hat + W_hat_M + C) (gm = NULL), or
M_hat = fitted(M ~ X_hat + Gm + W_hat_M + C) (gm supplied);
alpha_M = coefficient on X_hat.
Outcome:
Y ~ X_hat + M_hat + W_hat_X + W_hat_M + C;
NDE = coefficient on X_hat, beta_M = coefficient on M_hat.
NIE = alpha_M * beta_M (delta-method SE).
The key advantage over fit_iv2sls_mediation2: PGC-2 does not
require instrument exogeneity. When the mediator instrument Gm is
correlated with the confounder conf_MY (rho_G2 > 0), IV2SLS2 is biased but
PGC-2's bridge absorbs conf_MY regardless of the instrument violation. The
tipping-point simulation maps where PGC-2 bias crosses below IV2SLS2 bias.
Named list: NDE, NDE_se, NDE_p,
NIE, NIE_se, NIE_p. Returns all-NA if
the first-stage partial F for G1 is below min_f.
Miao, W., Geng, Z., & Tchetgen Tchetgen, E. (2018). Identifying causal effects with proxy variables of an unmeasured confounder. Biometrika, 105(4), 987-993.
set.seed(1) dat <- generate_toy_data(n = 500, mo_confounding = 0.8, rho_G2 = 0.3, lambda_XM = c(1, 0), lambda_MY = c(0, 1), omega_1 = 0.7, omega_2 = 0.7, seed = 1) # Without mediator instrument (pure NC identification) fit_pgc_mediation2(dat$Y[, 1], dat$X, dat$M, dat$G1, dat$W1, dat$W2) # With (possibly imperfect) mediator instrument fit_pgc_mediation2(dat$Y[, 1], dat$X, dat$M, dat$G1, dat$W1, dat$W2, gm = dat$Gm)set.seed(1) dat <- generate_toy_data(n = 500, mo_confounding = 0.8, rho_G2 = 0.3, lambda_XM = c(1, 0), lambda_MY = c(0, 1), omega_1 = 0.7, omega_2 = 0.7, seed = 1) # Without mediator instrument (pure NC identification) fit_pgc_mediation2(dat$Y[, 1], dat$X, dat$M, dat$G1, dat$W1, dat$W2) # With (possibly imperfect) mediator instrument fit_pgc_mediation2(dat$Y[, 1], dat$X, dat$M, dat$G1, dat$W1, dat$W2, gm = dat$Gm)
Two-stage proximal mediation with path-specific negative controls and
a binary outcome stage. Stages 1-2 (X bridge on W1, M bridge on W2,
X_hat / M_hat construction) are identical to
fit_pgc_mediation2 and remain OLS. Only stage 3 switches
to logistic regression (log-OR) or a linear probability model (risk
difference): y ~ X_hat + M_hat + W_hat_X + W_hat_M + covars.
fit_pgc_mediation2_bin( y, X, M, g, W1, W2, gm = NULL, covars = NULL, min_f = 10, effect_scale = c("logor", "riskdiff") )fit_pgc_mediation2_bin( y, X, M, g, W1, W2, gm = NULL, covars = NULL, min_f = 10, effect_scale = c("logor", "riskdiff") )
y |
Numeric 0/1 outcome vector (length n). |
X |
Numeric exposure vector (length n). |
M |
Numeric mediator vector (length n). |
g |
Numeric instrument for X (length n). |
W1 |
Numeric NC matrix (n x q) or vector for the X->M path. |
W2 |
Numeric NC matrix (n x q) or vector for the M->Y path. |
gm |
Optional numeric mediator instrument (length n). |
covars |
Optional data frame of covariates (n rows). |
min_f |
Minimum partial F for G1. Default 10. |
effect_scale |
Character: |
When gm = NULL (PGC2), stage 2 uses pure NC identification.
When gm is supplied (PGC2Gm), the mediator instrument helps
isolate conf_MY before bridging W2.
Named list (same fields as fit_unadj_mediation_bin).
Returns all-NA if the first-stage partial F for G1 is below min_f.
set.seed(1) dat <- generate_toy_data(n = 500, outcome_type = "binary", mo_confounding = 0.8, rho_G2 = 0.3, lambda_XM = c(1, 0), lambda_MY = c(0, 1), seed = 1) fit_pgc_mediation2_bin(dat$y_bin, dat$X, dat$M, dat$G[, 1], dat$W1, dat$W2, gm = dat$Gm)set.seed(1) dat <- generate_toy_data(n = 500, outcome_type = "binary", mo_confounding = 0.8, rho_G2 = 0.3, lambda_XM = c(1, 0), lambda_MY = c(0, 1), seed = 1) fit_pgc_mediation2_bin(dat$y_bin, dat$X, dat$M, dat$G[, 1], dat$W1, dat$W2, gm = dat$Gm)
Two-stage proximal mediation with path-specific negative controls and
a survival outcome stage. Stages 1-2 (X bridge on W1, M bridge on W2,
X_hat / M_hat construction) are identical to fit_pgc_mediation2
and remain OLS. Only stage 3 switches to Cox (log-HR) or RMST
pseudo-observation OLS:
Surv(t,e) ~ X_hat + M_hat + W_hat_X + W_hat_M + covars.
fit_pgc_mediation2_surv( time, event, X, M, g, W1, W2, gm = NULL, covars = NULL, min_f = 10, effect_scale = c("loghr", "rmst"), tau = NULL )fit_pgc_mediation2_surv( time, event, X, M, g, W1, W2, gm = NULL, covars = NULL, min_f = 10, effect_scale = c("loghr", "rmst"), tau = NULL )
time |
Numeric follow-up time vector (length n). |
event |
Numeric 0/1 event indicator (length n). |
X |
Numeric exposure vector (length n). |
M |
Numeric mediator vector (length n). |
g |
Numeric instrument for X (length n). |
W1 |
Numeric NC matrix (n x q) or vector for the X->M path. |
W2 |
Numeric NC matrix (n x q) or vector for the M->Y path. |
gm |
Optional numeric mediator instrument (length n). |
covars |
Optional data frame of covariates (n rows). |
min_f |
Minimum partial F for G1. Default 10. |
effect_scale |
Character: |
tau |
RMST horizon (rmst only). |
When gm = NULL (PGC2), stage 2 uses pure NC identification.
When gm is supplied (PGC2Gm), the mediator instrument helps
isolate conf_MY before bridging W2.
Named list (same fields as fit_unadj_mediation_surv).
Returns all-NA if the first-stage partial F for G1 is below min_f.
set.seed(1) dat <- generate_toy_data(n = 500, outcome_type = "survival", mo_confounding = 0.8, rho_G2 = 0.3, lambda_XM = c(1, 0), lambda_MY = c(0, 1), seed = 1) fit_pgc_mediation2_surv(dat$surv_time, dat$surv_event, dat$X, dat$M, dat$G[, 1], dat$W1, dat$W2, gm = dat$Gm)set.seed(1) dat <- generate_toy_data(n = 500, outcome_type = "survival", mo_confounding = 0.8, rho_G2 = 0.3, lambda_XM = c(1, 0), lambda_MY = c(0, 1), seed = 1) fit_pgc_mediation2_surv(dat$surv_time, dat$surv_event, dat$X, dat$M, dat$G[, 1], dat$W1, dat$W2, gm = dat$Gm)
The original ICONIC PGC implementation, which summarises the
negative-control panel as a scalar (rowMeans(W)) before
bridging. This version is numerically stable and works in small
samples, but the scalar bridge produces proportional
to the G-residualised exposure by construction, making the
estimator algebraically equivalent to IV/2SLS when the instrument
is valid. The proximal completeness condition
(dim(W_valid) >= k) is NOT binding for this estimator.
fit_pgc_scalar(y, X, g, w, covars = NULL)fit_pgc_scalar(y, X, g, w, covars = NULL)
y |
Numeric outcome vector (length n). |
X |
Numeric exposure vector (length n). |
g |
Numeric instrument vector (length n). |
w |
Numeric negative-control vector (length n).
Pass |
covars |
Optional data frame of additional covariates (n rows). |
Use fit_pgc (matrix bridge) when the completeness
condition is of interest. Use this function as a stable fallback
when n is small relative to the number of valid controls,
or as an IV-equivalent benchmark.
Named list: beta, se, pvalue.
set.seed(1) dat <- generate_toy_data(n = 200, seed = 1) fit_pgc_scalar(dat$Y[, 1], dat$X, dat$G[, 1], rowMeans(dat$W))set.seed(1) dat <- generate_toy_data(n = 200, seed = 1) fit_pgc_scalar(dat$Y[, 1], dat$X, dat$G[, 1], rowMeans(dat$W))
The original ICONIC PGC mediation implementation, which summarises the
negative-control panel as a scalar (rowMeans(W)) before bridging.
Like fit_pgc_scalar, this version is algebraically
equivalent to the IV2SLS mediation estimator when the instrument is
valid. Use fit_pgc_mediation (matrix bridge) when the
completeness condition is of interest.
fit_pgc_scalar_mediation(y, X, M, g, w, covars = NULL)fit_pgc_scalar_mediation(y, X, M, g, w, covars = NULL)
y |
Numeric outcome vector (length n). |
X |
Numeric exposure vector (length n). |
M |
Numeric mediator vector (length n). |
g |
Numeric instrument vector (length n). |
w |
Numeric negative-control vector (length n).
Pass |
covars |
Optional data frame of additional covariates (n rows). |
Named list: NDE, NDE_se, NDE_p,
NIE, NIE_se, NIE_p.
set.seed(1) dat <- generate_toy_data(n = 200, mo_confounding = 0.8, seed = 1) fit_pgc_scalar_mediation(dat$Y[, 1], dat$X, dat$M, dat$G[, 1], rowMeans(dat$W))set.seed(1) dat <- generate_toy_data(n = 200, mo_confounding = 0.8, seed = 1) fit_pgc_scalar_mediation(dat$Y[, 1], dat$X, dat$M, dat$G[, 1], rowMeans(dat$W))
Three-step bridge-function estimator with a survival outcome stage:
Residualise X on G -> X_resid (OLS).
Bridge X_resid on the FULL W matrix -> W_hat (OLS).
Regress the survival outcome on X + W_hat via Cox (log-HR) or RMST pseudo-observation OLS.
fit_pgc_surv( time, event, X, g, W, covars = NULL, effect_scale = c("loghr", "rmst"), tau = NULL )fit_pgc_surv( time, event, X, g, W, covars = NULL, effect_scale = c("loghr", "rmst"), tau = NULL )
time |
Numeric follow-up time vector (length n). |
event |
Numeric 0/1 event indicator (length n). |
X |
Numeric exposure vector (length n). |
g |
Numeric instrument vector (length n). |
W |
Numeric NC matrix (n x q) or vector (length n). |
covars |
Optional data frame of covariates (n rows). |
effect_scale |
Character: |
tau |
RMST horizon (rmst only). |
Named list: beta, se, pvalue.
set.seed(1) dat <- generate_toy_data(n = 200, outcome_type = "survival", seed = 1) fit_pgc_surv(dat$surv_time, dat$surv_event, dat$X, dat$G[, 1], dat$W)set.seed(1) dat <- generate_toy_data(n = 200, outcome_type = "survival", seed = 1) fit_pgc_surv(dat$surv_time, dat$surv_event, dat$X, dat$G[, 1], dat$W)
Regresses the binary outcome on the exposure X (plus covariates) with no instrument or negative-control adjustment. Bias reference.
fit_unadj_bin(y, X, covars = NULL, effect_scale = c("logor", "riskdiff"))fit_unadj_bin(y, X, covars = NULL, effect_scale = c("logor", "riskdiff"))
y |
Numeric 0/1 outcome vector (length n). |
X |
Numeric exposure vector (length n). |
covars |
Optional data frame of covariates (n rows). |
effect_scale |
Character: |
When effect_scale = "logor" (default), fits a logistic
regression and returns the conditional log-odds ratio for X. When
effect_scale = "riskdiff", fits a linear probability model
(OLS on the 0/1 outcome), returning a risk difference — a
collapsible, linear alternative to the non-collapsible odds ratio.
Named list: beta, se, pvalue.
set.seed(1) dat <- generate_toy_data(n = 200, outcome_type = "binary", seed = 1) fit_unadj_bin(dat$y_bin, dat$X) fit_unadj_bin(dat$y_bin, dat$X, effect_scale = "riskdiff")set.seed(1) dat <- generate_toy_data(n = 200, outcome_type = "binary", seed = 1) fit_unadj_bin(dat$y_bin, dat$X) fit_unadj_bin(dat$y_bin, dat$X, effect_scale = "riskdiff")
Stage 1: M ~ X (estimate alpha_M).
Stage 2: Y ~ X + M (estimate NDE = beta_X, beta_M).
NIE = alpha_M * beta_M.
fit_unadj_mediation(y, X, M, covars = NULL)fit_unadj_mediation(y, X, M, covars = NULL)
y |
Numeric outcome vector (length n). |
X |
Numeric exposure vector (length n). |
M |
Numeric mediator vector (length n). |
covars |
Optional data frame of additional covariates (n rows). |
Does not adjust for unmeasured confounding. Provided as a bias reference floor, analogous to UNADJ in the total-effect setting.
Named list: NDE, NDE_se, NDE_p,
NIE, NIE_se, NIE_p.
set.seed(1) dat <- generate_toy_data(n = 200, mo_confounding = 0.8, seed = 1) fit_unadj_mediation(dat$Y[, 1], dat$X, dat$M)set.seed(1) dat <- generate_toy_data(n = 200, mo_confounding = 0.8, seed = 1) fit_unadj_mediation(dat$Y[, 1], dat$X, dat$M)
Stage 1 (OLS): M ~ X -> alpha_M. Stage 2 (logistic / LPM):
binary outcome ~ X + M -> NDE (coef on X), beta_M (coef on M).
NIE = alpha_M * beta_M. No confounding adjustment; bias
reference.
fit_unadj_mediation_bin( y, X, M, covars = NULL, effect_scale = c("logor", "riskdiff") )fit_unadj_mediation_bin( y, X, M, covars = NULL, effect_scale = c("logor", "riskdiff") )
y |
Numeric 0/1 outcome vector (length n). |
X |
Numeric exposure vector (length n). |
M |
Numeric mediator vector (length n). |
covars |
Optional data frame of covariates (n rows). |
effect_scale |
Character: |
Named list: NDE, NDE_se, NDE_p,
NIE, NIE_se, NIE_p, alpha_M,
alpha_se, beta_M, beta_M_se.
set.seed(1) dat <- generate_toy_data(n = 200, outcome_type = "binary", mo_confounding = 0.8, seed = 1) fit_unadj_mediation_bin(dat$y_bin, dat$X, dat$M)set.seed(1) dat <- generate_toy_data(n = 200, outcome_type = "binary", mo_confounding = 0.8, seed = 1) fit_unadj_mediation_bin(dat$y_bin, dat$X, dat$M)
Stage 1 (OLS): M ~ X -> alpha_M. Stage 2 (Cox / RMST):
survival outcome ~ X + M -> NDE (coef on X), beta_M (coef on M).
NIE = alpha_M * beta_M. No confounding adjustment; bias
reference.
fit_unadj_mediation_surv( time, event, X, M, covars = NULL, effect_scale = c("loghr", "rmst"), tau = NULL )fit_unadj_mediation_surv( time, event, X, M, covars = NULL, effect_scale = c("loghr", "rmst"), tau = NULL )
time |
Numeric follow-up time vector (length n). |
event |
Numeric 0/1 event indicator (length n). |
X |
Numeric exposure vector (length n). |
M |
Numeric mediator vector (length n). |
covars |
Optional data frame of covariates (n rows). |
effect_scale |
Character: |
tau |
RMST horizon (rmst only). |
Named list: NDE, NDE_se, NDE_p,
NIE, NIE_se, NIE_p, alpha_M,
alpha_se, beta_M, beta_M_se.
set.seed(1) dat <- generate_toy_data(n = 200, outcome_type = "survival", mo_confounding = 0.8, seed = 1) fit_unadj_mediation_surv(dat$surv_time, dat$surv_event, dat$X, dat$M)set.seed(1) dat <- generate_toy_data(n = 200, outcome_type = "survival", mo_confounding = 0.8, seed = 1) fit_unadj_mediation_surv(dat$surv_time, dat$surv_event, dat$X, dat$M)
Regresses the survival outcome on the exposure X (plus covariates) with no instrument or negative-control adjustment. Bias reference.
fit_unadj_surv( time, event, X, covars = NULL, effect_scale = c("loghr", "rmst"), tau = NULL )fit_unadj_surv( time, event, X, covars = NULL, effect_scale = c("loghr", "rmst"), tau = NULL )
time |
Numeric follow-up time vector (length n). |
event |
Numeric 0/1 event indicator (length n). |
X |
Numeric exposure vector (length n). |
covars |
Optional data frame of covariates (n rows). |
effect_scale |
Character: |
tau |
RMST restriction time horizon. Default |
When effect_scale = "loghr" (default), fits a Cox
proportional-hazards model and returns the log-hazard ratio for X.
When effect_scale = "rmst", regresses leave-one-out RMST
pseudo-observations (Graw et al. 2009) on X via OLS, returning an
effect on the restricted-mean-survival-time (time) scale — a
collapsible, linear alternative to the non-collapsible hazard ratio.
Named list: beta, se, pvalue.
set.seed(1) dat <- generate_toy_data(n = 200, outcome_type = "survival", seed = 1) fit_unadj_surv(dat$surv_time, dat$surv_event, dat$X) fit_unadj_surv(dat$surv_time, dat$surv_event, dat$X, effect_scale = "rmst")set.seed(1) dat <- generate_toy_data(n = 200, outcome_type = "survival", seed = 1) fit_unadj_surv(dat$surv_time, dat$surv_event, dat$X) fit_unadj_surv(dat$surv_time, dat$surv_event, dat$X, effect_scale = "rmst")
For each scenario in the grid, generates n_iter synthetic datasets
with mediator-outcome confounding (via mo_confounding), runs every
mediation estimator, and summarises NDE/NIE bias / RMSE / Type I error.
This is the mediation analogue of gan_sensitivity().
gan_mediation_sensitivity( trained_gan = NULL, conf_grid = c(0.2, 0.5, 0.8), coverage_grid = c(0.3, 0.7, 1), k_grid = 1, mo_confounding = 0.8, phi = 0, rho_G1 = 0, rho_G2 = 0, rho_pop = 0, lambda_XM = NULL, lambda_MY = NULL, omega_1 = NULL, omega_2 = NULL, nc_model = "proxy", n_iter = 50, n_samples = 500, n_features = 20, beta_X = 0.1, alpha_M = 0.5, beta_M = 0.3, base_seed = 750, n_cores = 1, outcome_type = c("continuous", "survival", "binary"), effect_scale = c("loghr", "rmst", "logor", "riskdiff"), surv_h0 = 0.1, surv_event_frac = 0.6, surv_censor_rate = NULL, bin_prev = 0.5 )gan_mediation_sensitivity( trained_gan = NULL, conf_grid = c(0.2, 0.5, 0.8), coverage_grid = c(0.3, 0.7, 1), k_grid = 1, mo_confounding = 0.8, phi = 0, rho_G1 = 0, rho_G2 = 0, rho_pop = 0, lambda_XM = NULL, lambda_MY = NULL, omega_1 = NULL, omega_2 = NULL, nc_model = "proxy", n_iter = 50, n_samples = 500, n_features = 20, beta_X = 0.1, alpha_M = 0.5, beta_M = 0.3, base_seed = 750, n_cores = 1, outcome_type = c("continuous", "survival", "binary"), effect_scale = c("loghr", "rmst", "logor", "riskdiff"), surv_h0 = 0.1, surv_event_frac = 0.6, surv_censor_rate = NULL, bin_prev = 0.5 )
trained_gan |
An |
conf_grid |
Confounding-strength values to sweep. Default |
coverage_grid |
Negative-control coverage values in |
k_grid |
Numbers of latent confounders to sweep. Default |
mo_confounding |
Strength of U1 -> M (mediator-outcome confounding). Default 0.80. |
phi |
Strength of the mediator instrument Gm -> M. 0 = no mediator instrument (five estimators). > 0 = generates Gm and includes the 2-stage MR estimator (IV2SLS2). Default 0. |
rho_G1 |
Correlation of G1 with conf_XM. Default 0. |
rho_G2 |
Correlation of G2 with conf_MY. Default 0. |
rho_pop |
Shared population structure. Default 0. |
lambda_XM |
Optional per-path confounder loading vector (X->M path). |
lambda_MY |
Optional per-path confounder loading vector (M->Y path). |
omega_1 |
Coverage of conf_XM by W1. NULL = use |
omega_2 |
Coverage of conf_MY by W2. NULL = use |
nc_model |
Negative-control model (function or name). Default |
n_iter |
Replicates per scenario. Default 50. |
n_samples |
Samples per replicate. Default 500. |
n_features |
Features per replicate. Default 20. |
beta_X, alpha_M, beta_M
|
Causal paths (ground truth). Defaults 0.10 / 0.50 / 0.30. |
base_seed |
Base RNG seed. Default 750. |
n_cores |
Parallel workers across replicates. Default 1. |
outcome_type |
|
effect_scale |
|
surv_h0 |
Baseline hazard for survival DGP. See
|
surv_event_frac |
Target event fraction for survival DGP. |
surv_censor_rate |
Censoring rate for survival DGP. |
bin_prev |
Target prevalence for the binary DGP. Default 0.5. |
When phi > 0, a mediator-specific genetic instrument (Gm) is
generated and the 2-stage MR estimator (IV2SLS2) is included in the
results, enabling point identification of NDE/NIE under M-O confounding.
A list with summary (one row per scenario x method, with
conf_strength, coverage, k, mo_confounding, phi, true_NDE,
true_NIE and NDE/NIE bias/RMSE/Type I columns) and grid.
sens <- gan_mediation_sensitivity(NULL, conf_grid = 0.8, coverage_grid = 0.7, mo_confounding = 0.8, n_iter = 2, n_samples = 100, n_features = 5) head(sens$summary)sens <- gan_mediation_sensitivity(NULL, conf_grid = 0.8, coverage_grid = 0.7, mo_confounding = 0.8, n_iter = 2, n_samples = 100, n_features = 5) head(sens$summary)
For each cell in the grid (pleiotropy strength x confounding strength),
generates n_iter synthetic datasets with run_single_iteration(),
runs every estimator, and summarises bias / RMSE / power. Two arms are
run per cell:
gan_pleiotropy_sensitivity( trained_gan = NULL, pleio_grid = c(0, 0.05, 0.1), conf_grid = c(0.2, 0.5, 0.8), tau = 0.25, nc_model = "proxy", n_iter = 50, n_samples = 500, n_features = 10, coverage = 0.7, k = 1, base_seed = 900, n_cores = 1 )gan_pleiotropy_sensitivity( trained_gan = NULL, pleio_grid = c(0, 0.05, 0.1), conf_grid = c(0.2, 0.5, 0.8), tau = 0.25, nc_model = "proxy", n_iter = 50, n_samples = 500, n_features = 10, coverage = 0.7, k = 1, base_seed = 900, n_cores = 1 )
trained_gan |
An |
pleio_grid |
Horizontal-pleiotropy strengths (direct G -> Y
coefficients) to sweep. Default |
conf_grid |
Confounding-strength values. Default |
tau |
True total effect for the alternative arm. Default 0.25. |
nc_model |
Negative-control model (function or name). Default |
n_iter |
Replicates per cell per arm. Default 50. |
n_samples |
Samples per replicate. Default 500. |
n_features |
Features per replicate. Default 10. |
coverage |
Negative-control coverage. Default 0.7. |
k |
Number of latent confounders. Default 1. |
base_seed |
Base RNG seed. Default 900. |
n_cores |
Parallel workers across replicates. Default 1. |
alternative (effect_size = tau): the true total effect is tau,
so power is the empirical power to detect it.
null (effect_size = 0): the true total effect is 0, so power
is the empirical Type I error rate.
The pleio parameter adds a direct G -> Y path of the requested
strength, violating the exclusion restriction. IV/2SLS is consistent
only when pleio = 0; any pleio > 0 introduces bias that does not
shrink with sample size.
A list with summary (one row per cell x arm x method, with
pleio, conf_strength, arm, true_total, and the columns from
summarise_results()) and grid.
sens <- gan_pleiotropy_sensitivity(NULL, pleio_grid = c(0, 0.10), conf_grid = 0.8, n_iter = 2, n_samples = 100, n_features = 5) head(sens$summary)sens <- gan_pleiotropy_sensitivity(NULL, pleio_grid = c(0, 0.10), conf_grid = 0.8, n_iter = 2, n_samples = 100, n_features = 5) head(sens$summary)
For each scenario in the grid, generates n_iter synthetic datasets with
run_single_iteration() (using the trained generator for texture), runs every
estimator, and summarises bias / RMSE / power. This is the multi-confounder,
negative-control-aware generalisation of the package's parameter sweeps.
gan_sensitivity( trained_gan = NULL, conf_grid = c(0.2, 0.5, 0.8), coverage_grid = c(0.3, 0.7, 1), k_grid = 1, nc_model = "proxy", n_iter = 50, n_samples = 500, n_features = 20, beta_X = 0.1, alpha_M = 0.5, beta_M = 0.3, effect_size = NULL, base_seed = 700, n_cores = 1, outcome_type = c("continuous", "survival", "binary"), effect_scale = c("loghr", "rmst", "logor", "riskdiff"), surv_h0 = 0.1, surv_event_frac = 0.6, surv_censor_rate = NULL, bin_prev = 0.5 )gan_sensitivity( trained_gan = NULL, conf_grid = c(0.2, 0.5, 0.8), coverage_grid = c(0.3, 0.7, 1), k_grid = 1, nc_model = "proxy", n_iter = 50, n_samples = 500, n_features = 20, beta_X = 0.1, alpha_M = 0.5, beta_M = 0.3, effect_size = NULL, base_seed = 700, n_cores = 1, outcome_type = c("continuous", "survival", "binary"), effect_scale = c("loghr", "rmst", "logor", "riskdiff"), surv_h0 = 0.1, surv_event_frac = 0.6, surv_censor_rate = NULL, bin_prev = 0.5 )
trained_gan |
An |
conf_grid |
Confounding-strength values to sweep. Default |
coverage_grid |
Negative-control coverage values in |
k_grid |
Numbers of latent confounders to sweep. Default |
nc_model |
Negative-control model (function or name). Default |
n_iter |
Replicates per scenario. Default 50. |
n_samples |
Samples per replicate. Default 500. |
n_features |
Features per replicate. Default 20. |
beta_X, alpha_M, beta_M
|
Causal paths (ground truth). Defaults 0.10 / 0.50 / 0.30. |
effect_size |
Optional pure-direct total effect override (see
|
base_seed |
Base RNG seed. Default 700. |
n_cores |
Parallel workers across replicates. Default 1. |
outcome_type |
|
effect_scale |
|
surv_h0 |
Baseline hazard for the survival DGP. Default 0.1. |
surv_event_frac |
Target fraction of observed events. Default 0.6. |
surv_censor_rate |
Explicit censoring rate. Default NULL. |
bin_prev |
Target prevalence for the binary DGP. Default 0.5. |
A list with summary (one row per scenario x method, with
conf_strength, coverage, k, true_total and the columns from
summarise_results) and grid (the scenario grid).
sens <- gan_sensitivity(NULL, conf_grid = 0.8, coverage_grid = 0.7, n_iter = 2, n_samples = 100, n_features = 5) head(sens$summary)sens <- gan_sensitivity(NULL, conf_grid = 0.8, coverage_grid = 0.7, n_iter = 2, n_samples = 100, n_features = 5) head(sens$summary)
When mo_confounding > 0, the unmeasured confounder U1 also
affects the mediator M, creating mediator-outcome confounding — the
key extension for the mediation simulation. When mo_confounding = 0
(default), the original DGP is preserved exactly.
generate_toy_data( n = 500, n_features = 20, n_mediators = 1, beta_X = 0.1, alpha_M = 0.5, beta_M = 0.3, conf_str = 0.8, w_signal = 0.7, mo_confounding = 0, pleio = 0, phi = 0, gamma_G = 0.6, rho_G1 = 0, rho_G2 = 0, rho_pop = 0, lambda_XM = NULL, lambda_MY = NULL, omega_1 = NULL, omega_2 = NULL, feat_cor = 0, separate_U = NULL, u_strength = NULL, w_coverage_profile = NULL, outcome_type = c("continuous", "survival", "binary"), surv_h0 = 0.1, surv_event_frac = 0.6, surv_censor_rate = NULL, bin_prev = 0.5, seed = NULL, beta_Z = NULL )generate_toy_data( n = 500, n_features = 20, n_mediators = 1, beta_X = 0.1, alpha_M = 0.5, beta_M = 0.3, conf_str = 0.8, w_signal = 0.7, mo_confounding = 0, pleio = 0, phi = 0, gamma_G = 0.6, rho_G1 = 0, rho_G2 = 0, rho_pop = 0, lambda_XM = NULL, lambda_MY = NULL, omega_1 = NULL, omega_2 = NULL, feat_cor = 0, separate_U = NULL, u_strength = NULL, w_coverage_profile = NULL, outcome_type = c("continuous", "survival", "binary"), surv_h0 = 0.1, surv_event_frac = 0.6, surv_censor_rate = NULL, bin_prev = 0.5, seed = NULL, beta_Z = NULL )
n |
Sample size. Default 500. |
n_features |
Number of outcome and negative-control features. Default 20. |
n_mediators |
Number of independent mediators. When > 1,
each mediator M_m has its own genetic instrument Gm_m
and contributes additively to Y. M and Gm are returned
as |
beta_X |
Direct effect of X on Y (true NDE). Default 0.10. |
alpha_M |
Effect of X on mediator M. Default 0.50. |
beta_M |
Effect of M on Y (per-mediator NIE = alpha_M * beta_M; total NIE = n_mediators * alpha_M * beta_M). Default 0.30. |
conf_str |
Confounding strength delta. Default 0.80. |
w_signal |
Proxy quality omega (0 = noise, 1 = perfect U proxy). Default 0.70. |
mo_confounding |
Strength of U1 -> M (mediator-outcome confounding). 0 = no M-O confounding (original DGP). Default 0. |
pleio |
Strength of a direct G -> Y path (horizontal pleiotropy), violating the exclusion restriction. 0 = no pleiotropy (valid instrument, original DGP). Default 0. |
phi |
Strength of the mediator instrument Gm -> M.
0 = no mediator instrument (original DGP, no Gm generated).
Default 0. When > 0, a valid instrument for M is generated,
enabling point identification of NDE/NIE via
|
gamma_G |
Strength of the exposure instrument G -> X. Default 0.6. |
rho_G1 |
Correlation of G1 with conf_XM. Default 0. |
rho_G2 |
Correlation of G2 with conf_MY. Default 0. |
rho_pop |
Shared population structure inducing G1-G2 correlation . Default 0. |
lambda_XM |
Optional per-path loading vector for the X->M confounder composite. NULL (default) = shared loadings. |
lambda_MY |
Optional per-path loading vector for the M->Y confounder composite. NULL (default) = shared loadings. |
omega_1 |
Coverage of conf_XM by W1. NULL = use w_signal. |
omega_2 |
Coverage of conf_MY by W2. NULL = use w_signal. |
feat_cor |
Within-module feature correlation. When > 0,
the idiosyncratic noise in the Y and W panels is drawn
from a multivariate normal with a block-diagonal
correlation matrix modelling co-expression modules.
0 = independent noise (backward compatible). Default 0.
Sensitivity sweeps in the ICONIC preprint show estimator
performance is largely insensitive to |
separate_U |
Defunct. Passing a value errors with a message pointing
to the replacement per-path loading vectors
|
u_strength |
Numeric scalar: scales the single
confounder's effect on X, M, and Y. Default NULL → 1
(backward compatible). See |
w_coverage_profile |
A list with optional |
outcome_type |
|
surv_h0 |
Baseline hazard for the survival DGP. Default 0.1. |
surv_event_frac |
Target fraction of observed events. Default 0.6. The censoring rate is solved internally from this. |
surv_censor_rate |
Explicit censoring rate. Default NULL
→ solved from |
bin_prev |
Target prevalence (marginal event probability) for the binary DGP. Default 0.5. The logistic intercept is solved internally from this. |
seed |
Optional integer RNG seed for reproducibility. |
beta_Z |
Defunct. Renamed to |
When phi > 0, a mediator-specific genetic instrument
Gm is generated: , independent of
G, U, and all other variables, and the mediator equation
becomes .
Gm is a valid instrument for M: it moves M but is
independent of the confounders and has no direct path to Y. This
enables the 2-stage MR mediation estimator (fit_iv2sls_mediation2)
to point-identify NDE and NIE even under M-O confounding. When
phi = 0 (default), no Gm is generated and the original DGP
is preserved exactly.
A named list with elements X, G (n x n_features matrix), Y, W, U1, M,
synthetic_data, true_total, true_NDE, true_NIE. When phi > 0, also
includes Gm (numeric vector, length n, or n_mediators x n
matrix when n_mediators > 1). When any parameter
is non-default, also includes G1, G2, W1, W2,
conf_XM, conf_MY, and (when rho_pop > 0) P.
When n_mediators > 1, M is an n_mediators x n matrix.
The generates instruments as pure noise, independent of the confounders — the best possible case. adds parameters that violate this independence, modelling realistic genomic structure:
rho_G1Correlation of the exposure instrument G1 with the X->M confounder conf_XM (instrument exogeneity violation). Default 0.
rho_G2Correlation of the mediator instrument G2 with the M->Y confounder conf_MY (instrument exogeneity violation). Default 0.
rho_popShared population-structure factor P that induces correlation between G1 and G2 (linkage / stratification). Default 0.
lambda_XM, lambda_MY
Per-path confounder loading vectors; distinct values draw conf_XM and conf_MY as
independent confounders for the X->M and M->Y paths respectively.
If FALSE (default), conf_XM = conf_MY = U1 (backward compatible).
omega_1Coverage of conf_XM by the path-specific negative
controls W1. NULL (default) uses w_signal.
omega_2Coverage of conf_MY by the path-specific negative
controls W2. NULL (default) uses w_signal.
When all are at their defaults, the DGP is identical
to the default. When any is non-default, the output additionally includes
G1, G2, W1, W2, conf_XM, conf_MY,
and (when rho_pop > 0) P.
# Basic total-effect data dat <- generate_toy_data(n = 200, n_features = 10, seed = 42) # Mediation with mediator instrument and path-specific NCs dat <- generate_toy_data(n = 200, n_features = 10, phi = 0.8, mo_confounding = 0.8, lambda_XM = c(1, 0), lambda_MY = c(0, 1), omega_1 = 0.7, omega_2 = 0.7, seed = 42) idata <- iconic_data(X = dat$X, Y = dat$Y, M = dat$M, G = dat$G[, 1], Gm = dat$Gm, W1 = dat$W1, W2 = dat$W2)# Basic total-effect data dat <- generate_toy_data(n = 200, n_features = 10, seed = 42) # Mediation with mediator instrument and path-specific NCs dat <- generate_toy_data(n = 200, n_features = 10, phi = 0.8, mo_confounding = 0.8, lambda_XM = c(1, 0), lambda_MY = c(0, 1), omega_1 = 0.7, omega_2 = 0.7, seed = 42) idata <- iconic_data(X = dat$X, Y = dat$Y, M = dat$M, G = dat$G[, 1], Gm = dat$Gm, W1 = dat$W1, W2 = dat$W2)
Creates an iconic_data S3 object from the user's real data,
standardizing vectors and matrices into a consistent format that
all downstream model selection functions consume.
iconic_data( X, Y = NULL, M = NULL, G = NULL, Gm = NULL, W = NULL, W1 = NULL, W2 = NULL, covariates = NULL, feature_names = NULL, mediator_names = NULL, trained_gan = NULL, outcome_type = c("continuous", "survival", "binary"), surv_time = NULL, surv_event = NULL, scale = TRUE, recycle_lone_panel = FALSE, Z = NULL )iconic_data( X, Y = NULL, M = NULL, G = NULL, Gm = NULL, W = NULL, W1 = NULL, W2 = NULL, covariates = NULL, feature_names = NULL, mediator_names = NULL, trained_gan = NULL, outcome_type = c("continuous", "survival", "binary"), surv_time = NULL, surv_event = NULL, scale = TRUE, recycle_lone_panel = FALSE, Z = NULL )
X |
Exposure: numeric vector (length n) or features x samples matrix. If a matrix, column means are taken and scaled (one exposure per sample). |
Y |
Outcome: numeric vector (length n) or features x samples
matrix. When a matrix, estimation runs per-feature. When
|
M |
Optional mediator: numeric vector (length n) or features x samples matrix. When a matrix, estimation runs per-mediator x per-outcome. |
G |
Optional exposure instrument: numeric vector (length n) or
n x n_features matrix (as returned by |
Gm |
Optional mediator instrument: numeric vector (length n) or matrix (n x n_mediators). When a matrix, each column is the instrument for the corresponding mediator (e.g., per-isoform cis-eQTLs). |
W |
Optional negative-control panel: features x samples matrix. Single-panel NCs used for COCA, PGC. |
W1 |
Optional path-specific NCs for the X->M path: features x samples matrix. Captures conf_XM. When W1/W2 are absent but W is present, W1 = W2 = W. |
W2 |
Optional path-specific NCs for the M->Y path: features x samples matrix. Captures conf_MY. |
covariates |
Optional data frame of sample-level covariates (n rows).
Recognized columns |
feature_names |
Optional character vector of outcome feature names. |
mediator_names |
Optional character vector of mediator names. |
trained_gan |
Optional |
outcome_type |
Character: |
surv_time |
Numeric follow-up time vector (length n). Required
when |
surv_event |
Numeric 0/1 event indicator (length n; 1 = event
observed, 0 = censored). Required when
|
scale |
Logical: center and scale all continuous inputs (X, Y, M,
G, Gm, W, W1, W2, and numeric covariates) to mean 0 / sd 1. Default
|
recycle_lone_panel |
Logical: when exactly one of |
Z |
Defunct. Renamed to |
An iconic_data S3 object: a named list with $X, $Y, $M,
$G, $Gm, $W, $W1, $W2, $covariates, $n, $n_features,
$n_mediators, $has_instrument, $has_mediator_instrument,
$has_nc, $has_path_nc, $is_mediation, $feature_names,
$mediator_names, $trained_gan, $outcome_type, and (when
survival) $surv_time, $surv_event, or (when binary) $Y_bin.
# Total-effect only (no mediation) data <- iconic_data(X = rnorm(100), Y = matrix(rnorm(100 * 10), 10, 100)) # Full mediation with instruments and NCs data <- iconic_data( X = rnorm(100), Y = matrix(rnorm(100 * 10), 10, 100), M = rnorm(100), G = rnorm(100), Gm = rnorm(100), W = matrix(rnorm(100 * 10), 10, 100) ) print(data) # Survival outcome data <- iconic_data( X = rnorm(100), outcome_type = "survival", surv_time = rexp(100), surv_event = rbinom(100, 1, 0.6), M = rnorm(100), G = rnorm(100), Gm = rnorm(100), W = matrix(rnorm(100 * 10), 10, 100) ) print(data) # Binary outcome (Y is the 0/1 outcome vector) data <- iconic_data( X = rnorm(100), Y = rbinom(100, 1, 0.4), outcome_type = "binary", M = rnorm(100), G = rnorm(100), Gm = rnorm(100), W = matrix(rnorm(100 * 10), 10, 100) ) print(data)# Total-effect only (no mediation) data <- iconic_data(X = rnorm(100), Y = matrix(rnorm(100 * 10), 10, 100)) # Full mediation with instruments and NCs data <- iconic_data( X = rnorm(100), Y = matrix(rnorm(100 * 10), 10, 100), M = rnorm(100), G = rnorm(100), Gm = rnorm(100), W = matrix(rnorm(100 * 10), 10, 100) ) print(data) # Survival outcome data <- iconic_data( X = rnorm(100), outcome_type = "survival", surv_time = rexp(100), surv_event = rbinom(100, 1, 0.6), M = rnorm(100), G = rnorm(100), Gm = rnorm(100), W = matrix(rnorm(100 * 10), 10, 100) ) print(data) # Binary outcome (Y is the 0/1 outcome vector) data <- iconic_data( X = rnorm(100), Y = rbinom(100, 1, 0.4), outcome_type = "binary", M = rnorm(100), G = rnorm(100), Gm = rnorm(100), W = matrix(rnorm(100 * 10), 10, 100) ) print(data)
Runs a battery of diagnostic checks on the user's data and returns an eligibility report for all eight ICONIC estimators. The checks include:
iconic_diagnose( data, fdr_level = 0.1, min_f = 10, k = NULL, g_threshold = NULL, gm_threshold = NULL, n_cores = 1, allow_no_proxy = TRUE )iconic_diagnose( data, fdr_level = 0.1, min_f = 10, k = NULL, g_threshold = NULL, gm_threshold = NULL, n_cores = 1, allow_no_proxy = TRUE )
data |
An |
fdr_level |
FDR level for NC screens. Default 0.10. |
min_f |
Minimum partial F for instrument strength. Default 10.
Used as the scalar threshold when |
k |
Number of latent confounders assumed for the
completeness check. Default |
g_threshold |
Optional list |
gm_threshold |
Optional list |
n_cores |
Number of parallel workers for NC validity screens and
panel instrument-strength computation. Default 1 (sequential). Uses
|
allow_no_proxy |
Logical: when |
Instrument strength: first-stage partial F-statistics for G (exposure instrument) and Gm (mediator instrument), with the Stock-Yogo weak-instrument threshold (F >= 10).
NC validity (A1): screens each negative-control
feature for association with the exposure X (the empirically testable
projection of "W independent of X given C, U";
nc_validity_screen()).
NC independence (A2): screens each NC for association
with the instrument G (the empirically testable projection of
"W independent of G given C, U"; nc_independence_check()).
NC independence (A2'): screens each NC for association
with the mediator instrument Gm (the empirically testable projection
of "W independent of G_m given C, U";
nc_independence_check_gm()).
Path completeness: checks whether the valid NC panel
has enough features to span the confounder subspace and captures the
confounder covariance
(nc_completeness_check()).
An iconic_diagnosis S3 object: a named list with
$instrument_strength, $nc_validity,
$nc_independence, $nc_independence_gm,
$completeness, $eligibility, and $summary.
$instrument_strength$F_G and $instrument_strength$F_Gm
are numeric vectors containing the full panel distributions (one entry
per instrument / mediator).
| Parameter | Default | Source |
fdr_level |
0.10 | BH-FDR level for NC screens |
min_f |
10 | Stock-Yogo weak-instrument threshold |
k |
1 | Single-confounder assumption (typical mediation) |
g_threshold |
NULL | Legacy scalar (median F_G vs min_f) |
gm_threshold |
NULL | Legacy scalar (median F_Gm vs min_f) |
n_cores |
1 | Sequential |
allow_no_proxy |
TRUE | Proceed with UNADJ-only when no IV/NC |
set.seed(1) data <- iconic_data(X = rnorm(100), Y = matrix(rnorm(100 * 10), 10, 100), G = rnorm(100), W = matrix(rnorm(100 * 10), 10, 100)) diag <- iconic_diagnose(data) print(diag)set.seed(1) data <- iconic_data(X = rnorm(100), Y = matrix(rnorm(100 * 10), 10, 100), G = rnorm(100), W = matrix(rnorm(100 * 10), 10, 100)) diag <- iconic_diagnose(data) print(diag)
Applies all eligible causal estimators to the user's real data
(supplied as an iconic_data object) and returns tidy
per-feature (and per-mediator) point estimates, standard errors, and
p-values.
iconic_estimate( data, methods = NULL, diagnosis = NULL, alpha = 0.05, n_cores = 1, min_f = NULL, run_all = FALSE, se_method = c("delta", "bootstrap", "composite"), n_boot = 500, effect_scale = c("loghr", "rmst", "logor", "riskdiff"), tau = NULL )iconic_estimate( data, methods = NULL, diagnosis = NULL, alpha = 0.05, n_cores = 1, min_f = NULL, run_all = FALSE, se_method = c("delta", "bootstrap", "composite"), n_boot = 500, effect_scale = c("loghr", "rmst", "logor", "riskdiff"), tau = NULL )
data |
An |
methods |
Optional character vector of estimator names to run
(e.g. |
diagnosis |
Optional |
alpha |
Significance threshold for significance flags. Default 0.05. |
n_cores |
Number of parallel workers for per-feature (and
per-mediator) estimation. Default 1 (sequential). Uses
|
min_f |
Minimum partial F for the per-transcript weak-instrument
gate inside IV2SLS, IV2SLS2, and PGC2Gm. Default |
run_all |
Logical. Default |
se_method |
Character: |
n_boot |
Integer: number of bootstrap resamples when
|
effect_scale |
Character: |
tau |
Numeric: RMST restriction time horizon. Default
|
If a diagnosis from iconic_diagnose() is supplied,
only eligible estimators are run by default. If methods is
supplied, only those methods are run (user override). If neither is
supplied, all estimators whose required inputs are present are run.
A data frame. For total-effect mode: feature, method,
beta, se, pvalue, significant. For mediation
mode: feature, mediator, method, NDE,
NDE_se, NDE_p, NIE, NIE_se, NIE_p,
NDE_significant, NIE_significant. When
outcome_type = "survival", estimates are on the log-HR scale
(effect_scale = "loghr") or the RMST/time scale
(effect_scale = "rmst"). When outcome_type = "binary",
estimates are on the log-OR scale (effect_scale = "logor") or
the risk-difference scale (effect_scale = "riskdiff").
set.seed(1) data <- iconic_data(X = rnorm(100), Y = matrix(rnorm(100 * 10), 10, 100), G = rnorm(100), W = matrix(rnorm(100 * 10), 10, 100)) diag <- iconic_diagnose(data) est <- iconic_estimate(data, diagnosis = diag) head(est) # Survival outcome sdat <- iconic_data(X = rnorm(100), outcome_type = "survival", surv_time = rexp(100), surv_event = rbinom(100, 1, 0.6), G = rnorm(100), W = matrix(rnorm(100 * 10), 10, 100)) est <- iconic_estimate(sdat, effect_scale = "loghr") est_rmst <- iconic_estimate(sdat, effect_scale = "rmst") # Binary outcome bdat <- iconic_data(X = rnorm(100), Y = rbinom(100, 1, 0.4), outcome_type = "binary", G = rnorm(100), W = matrix(rnorm(100 * 10), 10, 100)) est <- iconic_estimate(bdat, effect_scale = "logor") est_rd <- iconic_estimate(bdat, effect_scale = "riskdiff")set.seed(1) data <- iconic_data(X = rnorm(100), Y = matrix(rnorm(100 * 10), 10, 100), G = rnorm(100), W = matrix(rnorm(100 * 10), 10, 100)) diag <- iconic_diagnose(data) est <- iconic_estimate(data, diagnosis = diag) head(est) # Survival outcome sdat <- iconic_data(X = rnorm(100), outcome_type = "survival", surv_time = rexp(100), surv_event = rbinom(100, 1, 0.6), G = rnorm(100), W = matrix(rnorm(100 * 10), 10, 100)) est <- iconic_estimate(sdat, effect_scale = "loghr") est_rmst <- iconic_estimate(sdat, effect_scale = "rmst") # Binary outcome bdat <- iconic_data(X = rnorm(100), Y = rbinom(100, 1, 0.4), outcome_type = "binary", G = rnorm(100), W = matrix(rnorm(100 * 10), 10, 100)) est <- iconic_estimate(bdat, effect_scale = "logor") est_rd <- iconic_estimate(bdat, effect_scale = "riskdiff")
Colour palette for iconic methods
iconic_method_colorsiconic_method_colors
A named character vector of colors for the eight ICONIC estimators.
iconic_method_colorsiconic_method_colors
Default method display order
iconic_method_ordericonic_method_order
A character vector giving the canonical display order of the eight ICONIC estimators.
iconic_method_ordericonic_method_order
For users who have exposure (X), mediator (M), and outcome (Y) but no genetic instruments (G, Gm) or negative controls (W), this function simulates what estimates they could expect if they were to collect such data.
iconic_prospect( data, trained_gan = NULL, confounding = c("default", "inferred", "manual"), gan_epochs = 100, gamma_G_grid = c(0.2, 0.4, 0.6, 0.8, 1), target_gamma_G = 0.6, n_iter = 30, n_features = 10, mo_confounding = 0.8, phi = 0.8, lambda_XM = NULL, lambda_MY = NULL, omega_1 = 0.7, omega_2 = 0.7, rho_G1_grid = c(0, 0.1, 0.2, 0.3, 0.5), rho_G2_grid = c(0, 0.1, 0.2, 0.3, 0.5), omega_grid_rho = c(0.3, 0.7, 1), run_rho_sweep = TRUE, bias_threshold = 0.1, base_seed = 500, verbose = FALSE, allow_no_proxy = TRUE, outcome_type = c("continuous", "survival", "binary"), effect_scale = c("loghr", "rmst", "logor", "riskdiff"), surv_h0 = 0.1, surv_event_frac = 0.6, surv_censor_rate = NULL, bin_prev = 0.5 )iconic_prospect( data, trained_gan = NULL, confounding = c("default", "inferred", "manual"), gan_epochs = 100, gamma_G_grid = c(0.2, 0.4, 0.6, 0.8, 1), target_gamma_G = 0.6, n_iter = 30, n_features = 10, mo_confounding = 0.8, phi = 0.8, lambda_XM = NULL, lambda_MY = NULL, omega_1 = 0.7, omega_2 = 0.7, rho_G1_grid = c(0, 0.1, 0.2, 0.3, 0.5), rho_G2_grid = c(0, 0.1, 0.2, 0.3, 0.5), omega_grid_rho = c(0.3, 0.7, 1), run_rho_sweep = TRUE, bias_threshold = 0.1, base_seed = 500, verbose = FALSE, allow_no_proxy = TRUE, outcome_type = c("continuous", "survival", "binary"), effect_scale = c("loghr", "rmst", "logor", "riskdiff"), surv_h0 = 0.1, surv_event_frac = 0.6, surv_censor_rate = NULL, bin_prev = 0.5 )
data |
An |
trained_gan |
Optional |
confounding |
Confounding parameter source: |
gan_epochs |
Epochs for auto-trained GAN. Default 100. |
gamma_G_grid |
Instrument strength values to sweep (Phase 1).
Default |
target_gamma_G |
Target instrument strength for Phase 2. Default 0.6 (matching the DGP default). |
n_iter |
Replicates per grid cell. Default 30. |
n_features |
Features per replicate. Default 10. |
mo_confounding |
Assumed M-O confounding strength. Default 0.8. |
phi |
Assumed mediator instrument strength. Default 0.8. |
lambda_XM |
Optional per-path confounder loading vector (X->M path). |
lambda_MY |
Optional per-path confounder loading vector (M->Y path). Default TRUE. |
omega_1, omega_2
|
Assumed NC coverage. Default 0.7. |
rho_G1_grid, rho_G2_grid
|
Instrument-exogeneity violation values
(correlation of each instrument with its path's confounder composite) to
sweep in Phase 3 at the target instrument strength. Default
|
omega_grid_rho |
Negative-control coverage values swept jointly with
the rho grid in Phase 3, on the diagonal ( |
run_rho_sweep |
Logical: run the Phase 3 robustness sweep
(default |
bias_threshold |
Tipping-point threshold. Default 0.10. |
base_seed |
Base RNG seed. Default 500. |
verbose |
Logical: print progress messages during the sweep.
Default |
allow_no_proxy |
Logical: when |
outcome_type |
|
effect_scale |
|
surv_h0, surv_event_frac, surv_censor_rate
|
Survival DGP parameters
See |
bin_prev |
Target prevalence for the binary DGP. Default 0.5. |
Phase 1 sweeps instrument strength (gamma_G) to show how
estimates converge as the instrument strengthens. Phase 2 runs a
full prospective simulation at a target strength, generating
synthetic instruments and NCs calibrated to the user's sample size
and confounding level.
An iconic_prospect S3 object: a named list with
$strength_surface (Phase 1: gamma_G x method estimates),
$prospective (Phase 2: full simulation at target strength),
$rho_surface (Phase 3: rho_G1 x rho_G2 exogeneity-robustness
surface at the target strength; NULL when
run_rho_sweep = FALSE),
$summary, $recommendation,
$texture_source, and $inferred_confounding (when
confounding = "inferred").
Use this as a bias-reduction prospective when you have an observational exposure-mediator-outcome triplet but lack the genetic instruments and negative controls that the core estimators require. It quantifies the relative bias improvement you could expect by collecting such data: the sweep shows how much of the naive confounding bias is removed as instrument strength and NC coverage increase, letting you decide whether the marginal gain justifies the cost of genotyping / profiling the additional assays. It is a planning tool, not an estimator – it does not produce a causal estimate from your current data, but tells you what a future instrumented study would yield.
When trained_gan is NULL and no GAN is attached to
data, a texture model is auto-trained from the user's data.
The confounding argument controls how the held-fixed
confounding parameters are set. In the prospective setting (no
instruments or NCs), most parameters cannot be inferred and will
fall back to defaults with warnings – this is an honest limitation,
not a silent failure.
| Parameter | Default | Source |
confounding |
"default" | Use DGP defaults below |
gan_epochs |
100 | Texture-model training budget |
gamma_G_grid |
c(0.2,0.4,0.6,0.8,1.0) | Instrument-strength sweep |
target_gamma_G |
0.6 | DGP default (gamma_G) |
n_iter |
30 | Replicates per grid cell |
n_features |
10 | Features per replicate |
mo_confounding |
0.8 | Simulation calibration (delta_mo) |
phi |
0.8 | Strong mediator instrument assumption |
lambda_XM, lambda_MY |
shared | Per-path confounder loadings |
omega_1, omega_2 |
0.7 | NC coverage (simulation calibration) |
rho_G1_grid, rho_G2_grid |
c(0,0.1,0.2,0.3,0.5) | Phase 3 exogeneity sweep |
omega_grid_rho |
c(0.3,0.7,1.0) | Phase 3 NC-coverage sweep (diagonal) |
run_rho_sweep |
TRUE | Run Phase 3 robustness sweep |
bias_threshold |
0.10 | Tipping-point threshold |
allow_no_proxy |
TRUE | Proceed in prospective setting |
if (check_torch_setup()) { data <- iconic_data(X = rnorm(100), Y = matrix(rnorm(100 * 10), 10, 100), M = rnorm(100)) result <- iconic_prospect(data, n_iter = 2, gan_epochs = 5, gamma_G_grid = c(0.4, 0.8), run_rho_sweep = FALSE) print(result) }if (check_torch_setup()) { data <- iconic_data(X = rnorm(100), Y = matrix(rnorm(100 * 10), 10, 100), M = rnorm(100)) result <- iconic_prospect(data, n_iter = 2, gan_epochs = 5, gamma_G_grid = c(0.4, 0.8), run_rho_sweep = FALSE) print(result) }
Ranks all eligible estimators directly on per-estimand robustness (NDE and NIE separately) from a sensitivity analysis. Returns the top-ranked estimator with a transparent rationale. Every estimator is ranked on its measured robustness to assumption violations, not on an a priori identification-strength class.
iconic_recommend( data, diagnosis = NULL, estimate = NULL, sensitivity = NULL, criterion = c("combined", "minimax_bias", "ci_coverage"), completeness_penalty = c(satisfied = 1, borderline = 0.7, `weak-capture` = 0.5, `under-identified` = 0), min_f = 10, g_threshold = NULL, gm_threshold = NULL, auto_sensitivity = TRUE, rho_G1_grid = c(0, 0.1, 0.2, 0.3, 0.5), rho_G2_grid = c(0, 0.1, 0.2, 0.3, 0.5), omega_1 = c(0.3, 0.7, 1), omega_2 = c(0.3, 0.7, 1), n_iter_sens = 30, gan_epochs = 100, n_cores = 1, verbose = FALSE )iconic_recommend( data, diagnosis = NULL, estimate = NULL, sensitivity = NULL, criterion = c("combined", "minimax_bias", "ci_coverage"), completeness_penalty = c(satisfied = 1, borderline = 0.7, `weak-capture` = 0.5, `under-identified` = 0), min_f = 10, g_threshold = NULL, gm_threshold = NULL, auto_sensitivity = TRUE, rho_G1_grid = c(0, 0.1, 0.2, 0.3, 0.5), rho_G2_grid = c(0, 0.1, 0.2, 0.3, 0.5), omega_1 = c(0.3, 0.7, 1), omega_2 = c(0.3, 0.7, 1), n_iter_sens = 30, gan_epochs = 100, n_cores = 1, verbose = FALSE )
data |
An |
diagnosis |
Optional |
estimate |
Optional estimate data frame from
|
sensitivity |
Optional |
criterion |
Character: |
completeness_penalty |
Named numeric vector with values in
|
min_f |
Instrument-strength gate (first-stage F threshold) used when
|
g_threshold, gm_threshold
|
Optional instrument-selection thresholds
passed to |
auto_sensitivity |
Logical: when |
rho_G1_grid, rho_G2_grid
|
Instrument-exogeneity violation grid used
for the auto-run sensitivity suite. Default |
omega_1, omega_2
|
Negative-control coverage grid for the auto-run.
Default |
n_iter_sens |
Replicates per grid cell for the auto-run. Default 30. |
gan_epochs |
GAN training epochs for the auto-run. Default 100. |
n_cores |
Cores for the auto-run sensitivity sweep. Default 1. |
verbose |
Logical: print progress messages. Default |
When sensitivity is supplied (from
iconic_sensitivity()), eligible estimators are ranked by
robustness: the estimator whose estimates degrade least across the
assumption-violation surface ranks higher. NDE and NIE robustness are
computed separately, so a mediation estimator is ranked on the
estimand of interest rather than a pooled maximum.
An iconic_recommendation S3 object: a named list with
$ranking (data frame: estimator, eligible, rank, per-estimand
robustness scores, composite, confidence_mult,
final_score, rationale), $recommended (top eligible,
non-naive estimator by the data-driven composite score),
$recommended_NDE and $recommended_NIE (top estimator per
estimand), $per_scenario (when sensitivity is supplied),
$completeness_penalty, and $summary.
data <- iconic_data(X = rnorm(100), Y = matrix(rnorm(100*10), 10, 100), G = rnorm(100), W = matrix(rnorm(100*10), 10, 100)) diag <- iconic_diagnose(data) est <- iconic_estimate(data, diagnosis = diag) rec <- iconic_recommend(data, diagnosis = diag, estimate = est, auto_sensitivity = FALSE) print(rec)data <- iconic_data(X = rnorm(100), Y = matrix(rnorm(100*10), 10, 100), G = rnorm(100), W = matrix(rnorm(100*10), 10, 100)) diag <- iconic_diagnose(data) est <- iconic_estimate(data, diagnosis = diag) rec <- iconic_recommend(data, diagnosis = diag, estimate = est, auto_sensitivity = FALSE) print(rec)
Sweeps a 2D grid of instrument-independence violations
(rho_G1 x rho_G2) and reports how each estimator's
NDE/NIE bias degrades as the genetic instruments become correlated
with the unmeasured confounders.
iconic_sensitivity( data, diagnosis = NULL, trained_gan = NULL, confounding = c("default", "inferred", "manual"), gan_epochs = 100, rho_G1_grid = c(0, 0.1, 0.2, 0.3, 0.5), rho_G2_grid = c(0, 0.1, 0.2, 0.3, 0.5), n_iter = 30, n_samples = NULL, n_features = 10, phi = NULL, mo_confounding = 0.8, lambda_XM = NULL, lambda_MY = NULL, omega_1 = c(0.3, 0.7, 1), omega_2 = c(0.3, 0.7, 1), bias_threshold = 0.1, base_seed = 700, n_cores = 1, verbose = FALSE, outcome_type = NULL, effect_scale = c("loghr", "rmst", "logor", "riskdiff"), surv_h0 = 0.1, surv_event_frac = 0.6, surv_censor_rate = NULL, bin_prev = 0.5 )iconic_sensitivity( data, diagnosis = NULL, trained_gan = NULL, confounding = c("default", "inferred", "manual"), gan_epochs = 100, rho_G1_grid = c(0, 0.1, 0.2, 0.3, 0.5), rho_G2_grid = c(0, 0.1, 0.2, 0.3, 0.5), n_iter = 30, n_samples = NULL, n_features = 10, phi = NULL, mo_confounding = 0.8, lambda_XM = NULL, lambda_MY = NULL, omega_1 = c(0.3, 0.7, 1), omega_2 = c(0.3, 0.7, 1), bias_threshold = 0.1, base_seed = 700, n_cores = 1, verbose = FALSE, outcome_type = NULL, effect_scale = c("loghr", "rmst", "logor", "riskdiff"), surv_h0 = 0.1, surv_event_frac = 0.6, surv_censor_rate = NULL, bin_prev = 0.5 )
data |
An |
diagnosis |
Optional |
trained_gan |
Optional |
confounding |
Confounding parameter source: |
gan_epochs |
Epochs for auto-trained GAN. Default 100. |
rho_G1_grid |
Values of rho_G1 (G correlation with conf_XM).
Default |
rho_G2_grid |
Values of rho_G2 (Gm correlation with conf_MY).
Default |
n_iter |
Replicates per grid cell. Default 30. |
n_samples |
Samples per replicate. If NULL, uses |
n_features |
Features per replicate. Default 10. |
phi |
Mediator instrument strength. If NULL, inferred from diagnosis (F_Gm) or defaults to 0.8. |
mo_confounding |
M-O confounding strength. Default 0.8.
Used when |
lambda_XM |
Optional per-path confounder loading vector (X->M path). NULL (default) = shared loadings. |
lambda_MY |
Optional per-path confounder loading vector (M->Y path). NULL (default) = shared loadings. |
omega_1, omega_2
|
NC coverage of each path's confounder composite
(conf_XM / conf_MY). Default |
bias_threshold |
Absolute bias threshold for tipping-point annotation. Default 0.10. |
base_seed |
Base RNG seed. Default 700. |
n_cores |
Number of parallel workers for simulation replicates.
Default 1 (sequential). Uses |
verbose |
Logical: print progress messages during the sweep.
Default |
outcome_type |
|
effect_scale |
|
surv_h0 |
Baseline hazard for survival DGP. Default 0.1. |
surv_event_frac |
Target fraction of observed events. Default 0.6. |
surv_censor_rate |
Explicit censoring rate. Default NULL. |
bin_prev |
Target prevalence for the binary DGP. Default 0.5. |
The grid is calibrated to the user's data: sample size, mediator
instrument strength (phi), confounding level, and covariate
/ outcome texture are inferred from the iconic_data object
and diagnosis when available.
An iconic_sensitivity S3 object: a named list with
$surface (data frame: rho_G1, rho_G2, method, NDE_bias,
NIE_bias, NDE_rmse, NIE_rmse, NDE_type1, NIE_type1, tipped),
$grid, $tipping_points, $summary,
$texture_source (how the GAN was obtained), and
$inferred_confounding (when confounding = "inferred").
Use this as an effect-decomposition bias sweep when you need to know how robust a mediation estimate (NDE/NIE) is to violations of the genetic instruments' independence from the confounders. The degradation surface shows, for each estimator, the point at which bias becomes material — the "tipping point" — so you can report not just a point estimate but the range of violations it tolerates. This is the mediation analogue of a total-effect sensitivity sweep and is most informative when comparing estimators that make different independence assumptions (e.g. IV2SLS2 vs PGC2Gm).
When trained_gan is NULL and no GAN is attached to
data, a texture model is auto-trained from the user's data
(GAN via torch if available, otherwise a multivariate-normal
fallback). This ensures the synthetic covariate and outcome texture
matches the user's cohort. Supply trained_gan explicitly or
attach one via iconic_data(trained_gan = ...) to reuse
a pre-trained model and avoid retraining.
The confounding argument controls how the held-fixed
confounding parameters (mo_confounding, omega_1,
omega_2) are set:
"default": fixed defaults (0.8, 0.7, 0.7).
"inferred": calls infer_confounding() to
estimate parameters from the data. Parameters that cannot be
inferred fall back to defaults with warnings.
"manual": use the explicitly supplied arguments.
| Parameter | Default | Source |
confounding |
"default" | Use DGP defaults below |
gan_epochs |
100 | Texture-model training budget |
rho_G1_grid |
c(0,0.1,0.2,0.3,0.5) | Independence-violation sweep |
rho_G2_grid |
c(0,0.1,0.2,0.3,0.5) | Independence-violation sweep |
n_iter |
30 | Replicates per grid cell |
n_features |
10 | Features per replicate |
mo_confounding |
0.8 | Simulation calibration (delta_mo) |
lambda_XM, lambda_MY |
shared | Per-path confounder loadings |
omega_1, omega_2 |
c(0.3,0.7,1.0) | NC coverage (swept on the diagonal) |
bias_threshold |
0.10 | Tipping-point threshold |
if (check_torch_setup()) { data <- iconic_data(X = rnorm(100), Y = matrix(rnorm(100 * 10), 10, 100), M = rnorm(100), G = rnorm(100), Gm = rnorm(100), W = matrix(rnorm(100 * 10), 10, 100)) sens <- iconic_sensitivity(data, n_iter = 2, gan_epochs = 5, rho_G1_grid = c(0, 0.2), rho_G2_grid = c(0, 0.2)) print(sens) }if (check_torch_setup()) { data <- iconic_data(X = rnorm(100), Y = matrix(rnorm(100 * 10), 10, 100), M = rnorm(100), G = rnorm(100), Gm = rnorm(100), W = matrix(rnorm(100 * 10), 10, 100)) sens <- iconic_sensitivity(data, n_iter = 2, gan_epochs = 5, rho_G1_grid = c(0, 0.2), rho_G2_grid = c(0, 0.2)) print(sens) }
Estimates the held-fixed confounding parameters that
iconic_sensitivity() and iconic_prospect()
use when confounding = "inferred".
infer_confounding( data, diagnosis = NULL, estimate = NULL, n_cores = 1, max_infer_tasks = 50 )infer_confounding( data, diagnosis = NULL, estimate = NULL, n_cores = 1, max_infer_tasks = 50 )
data |
An |
diagnosis |
Optional |
estimate |
Optional estimate data frame from
|
n_cores |
Number of parallel workers for omega inference and
k permutation. Default 1 (sequential). Uses
|
max_infer_tasks |
Cap on the number of mediators and of outcome
features used when |
An iconic_confounding S3 object: a named list with
$conf_strength, $mo_confounding, $omega_1,
$omega_2, $k, $unavailable (character vector
of parameters that could not be inferred), and $warnings
(character vector of accumulated warnings). Each parameter slot
is a list with estimate, method, assumption,
available (logical), and warning (character or NULL).
conf_strength (delta): the gap between the unadjusted OLS estimate and the IV2SLS estimate, averaged across features. Requires a valid exposure instrument G (F >= 10). Assumes the exclusion restriction holds – using IV2SLS validity to calibrate a benchmark that tests IV2SLS validity is circular, so the estimate should be interpreted as a best-case calibration.
mo_confounding (delta_mo): the gap between the IV2SLS NIE and the IV2SLS2 NIE, averaged across features. Requires valid G + Gm (both F >= 10). Same circularity caveat.
omega (omega_1, omega_2): the square root of the R-squared from regressing each negative-control feature on the outcome residualized on X + C, averaged across features. Conflates NC coverage with confounder strength – reported as a composite, not pure coverage.
k: the number of latent confounders, estimated via parallel analysis (Horn, 1965) on the correlation matrix of outcomes residualized on X + C. Requires at least 5 outcome features. Returns a point estimate and a bootstrap confidence interval.
When a parameter cannot be inferred from the data, the following defaults are used (from the simulation calibration):
| Parameter | Default | Source |
conf_strength (delta) |
0.8 | Simulation calibration |
mo_confounding (delta_mo) |
0.8 | Simulation calibration |
omega_1, omega_2 |
0.7 | NC coverage (simulation calibration) |
k |
1 | Single-confounder assumption (typical mediation) |
phi |
0.8 | Strong mediator instrument assumption |
lambda_XM, lambda_MY |
shared | Per-path confounder loadings |
These defaults are reported with a warning so the user knows the value was not inferred from their data.
data <- iconic_data(X = rnorm(100), Y = matrix(rnorm(100*10), 10, 100), M = rnorm(100), G = rnorm(100), Gm = rnorm(100), W = matrix(rnorm(100*10), 10, 100)) diag <- iconic_diagnose(data) est <- iconic_estimate(data, diagnosis = diag) conf <- infer_confounding(data, diagnosis = diag, estimate = est, max_infer_tasks = 5) print(conf)data <- iconic_data(X = rnorm(100), Y = matrix(rnorm(100*10), 10, 100), M = rnorm(100), G = rnorm(100), Gm = rnorm(100), W = matrix(rnorm(100*10), 10, 100)) diag <- iconic_diagnose(data) est <- iconic_estimate(data, diagnosis = diag) conf <- infer_confounding(data, diagnosis = diag, estimate = est, max_infer_tasks = 5) print(conf)
List the built-in negative-control models
list_nc_models()list_nc_models()
Character vector of registered NC model names.
list_nc_models()list_nc_models()
Accepts the exposure, outcome, mediator (optional), and negative-control
panels as features x samples matrices together with a sample-level
covariate frame, and returns the tidy structures used by
train_gan_on_real_data() and the calibration step of
run_single_iteration().
load_real_input_data( X_matrix = NULL, Y_matrix = NULL, M_matrix = NULL, W_matrix = NULL, covariates_df = NULL, example = FALSE, marginal_method = "auto", residualize_on = c("XC", "XCW"), Z_matrix = NULL )load_real_input_data( X_matrix = NULL, Y_matrix = NULL, M_matrix = NULL, W_matrix = NULL, covariates_df = NULL, example = FALSE, marginal_method = "auto", residualize_on = c("XC", "XCW"), Z_matrix = NULL )
X_matrix |
Exposure panel, |
Y_matrix |
Outcome, either a scalar vector of length |
M_matrix |
Mediator panel, |
W_matrix |
Optional negative-control panel, |
covariates_df |
Optional data frame of sample-level covariates. A
|
example |
Force return of the built-in example dataset. Default
|
marginal_method |
Marginal fitting method for the feature-level copula
texture model (mediator panel): |
residualize_on |
Character: |
Z_matrix |
Defunct. Renamed to |
In addition to the sample-level training frame (one row per sample with exposure level, outcome level, and encoded covariates), the function computes feature-level residual correlation matrices for each available panel (Y, M, W). These capture the cross-feature correlation structure that remains after removing the sample-level signal, so the simulation can inject realistic correlated noise into the mediator and negative- control panels.
If no matrices are supplied a small built-in example dataset is returned, so the pipeline can be exercised end-to-end without real data.
A list with gan_training_data (sample x variable data frame with
exposure level, outcome level, mediator level, and encoded covariates),
original_matrices (X, Y, M, W), covariates (encoded frame),
feature_correlations (list with Y, W residual correlation matrices,
or NULL for absent panels), feature_texture (an
iconic_feature_texture object for the mediator panel, or NULL when
no M matrix is supplied), sample_names, feature_names, n_samples,
n_features.
dat <- load_real_input_data(example = TRUE) names(dat)dat <- load_real_input_data(example = TRUE) names(dat)
Operationalizes the proximal completeness condition as whether the negative-control panel W captures the confounder covariance — i.e., whether adding W reduces U's contribution to the outcome toward zero.
nc_completeness_capture( dat, outcome = "Y", n_perm = 1000, n_cores = 1, thresholds = list(strong = 0.3, weak = 0.1) )nc_completeness_capture( dat, outcome = "Y", n_perm = 1000, n_cores = 1, thresholds = list(strong = 0.3, weak = 0.1) )
dat |
Dataset list from |
outcome |
Outcome block to test against: |
n_perm |
Number of permutations for the null. Default 1000. |
n_cores |
Number of parallel workers. Default 1. |
thresholds |
Named list with |
The test computes the incremental R^2 of W for the outcome above
covariates C alone: R^2(W | C) = R^2(Y ~ C + W) - R^2(Y ~ C), averaged
across outcome features. A permutation null is generated by permuting the
W-outcome association (holding the C-outcome association fixed) over
n_perm permutations, yielding a permutation p-value for "W captures
U-signal beyond chance."
This addresses the concern that the count-based completeness check
(dim(W_valid) >= k) is necessary but not sufficient: completeness is
about the covariance the controls capture, not the number of proxies. A
panel can have many controls that each weakly capture U, or few controls
that together capture it well.
A list with:
capture_R2 (point estimate, averaged across outcome features),
capture_pvalue (permutation p-value),
capture_verdict ("strong", "weak", or "negligible"),
null_distribution (numeric vector of permuted R^2 values),
n_features (number of outcome features tested).
dat <- run_single_iteration(n_features = 10, n_confounders = 1, seed = 1) nc_completeness_capture(dat, n_perm = 99)dat <- run_single_iteration(n_features = 10, n_confounders = 1, seed = 1) nc_completeness_capture(dat, n_perm = 99)
Reports two components of the proximal-inference completeness condition:
nc_completeness_check( dat, n_valid_controls = NULL, fdr_level = 0.1, n_cores = 1, outcome = "Y", n_perm = 1000, capture_thresholds = list(strong = 0.3, weak = 0.1) )nc_completeness_check( dat, n_valid_controls = NULL, fdr_level = 0.1, n_cores = 1, outcome = "Y", n_perm = 1000, capture_thresholds = list(strong = 0.3, weak = 0.1) )
dat |
Dataset list from |
n_valid_controls |
Optional override: the number of valid
controls known from the study design. If
|
fdr_level |
FDR level for the empirical screens (used
only when |
n_cores |
Number of parallel workers for the empirical screens. Default 1 (sequential). |
outcome |
Outcome block for the capture test: |
n_perm |
Number of permutations for the capture-test null. Default 1000. |
capture_thresholds |
Named list with |
(1) Dimensional (the legacy count-based check): the number of valid
negative-control features vs the number of latent confounders k.
Bridge-function estimators (COCA, PGC) require at least as many
valid controls as confounders (Miao, Geng & Tchetgen Tchetgen,
2018). When dim(W_valid) < k, no estimator built on those
controls can recover the causal effect, regardless of sample size.
(2) Covariance-capture: whether the controls actually capture the confounder covariance — i.e., whether adding W reduces U's contribution to the outcome toward zero, operationalized as the incremental R^2 of W for the outcome above covariates alone, with a permutation null. This addresses the concern that completeness is about covariance captured, not proxy number.
The composite completeness verdict requires the dimensional component
to pass (satisfied/borderline) AND the capture component to be non-
negligible (strong/weak). When the dimensional component passes but
capture is negligible, the verdict is "weak-capture".
A list with:
n_valid_controls (count), k (number of confounders), dim_W,
dimensional ("satisfied", "borderline", "under-identified"),
capture (output of nc_completeness_capture(), or NULL),
completeness (composite: "satisfied", "borderline",
"under-identified", or "weak-capture"),
screen_X (A1 screen results, if run), screen_G (A2 screen
results, if run).
dat <- run_single_iteration(n_features = 10, n_confounders = 1, seed = 1) nc_completeness_check(dat, n_perm = 50)dat <- run_single_iteration(n_features = 10, n_confounders = 1, seed = 1) nc_completeness_check(dat, n_perm = 50)
Simulates spatially correlated CpG methylation whose signal is partly driven by the captured confounders, then forms each negative control as a linear prediction from the methylation sites ("CpG-predicted expression"). The controls therefore carry confounder information only to the extent the methylation does, mediated through a realistic spatial methylation layer.
nc_cpg(U, covariates, params)nc_cpg(U, covariates, params)
U |
|
covariates |
Covariate data frame (unused; kept for the NC contract). |
params |
List with |
When noise_cor is supplied (a p x p correlation matrix), the
idiosyncratic noise added to each control is drawn from a multivariate
normal with that correlation structure, so the controls retain realistic
cross-feature correlations conditional on the confounder.
n x n_features matrix of CpG-predicted negative controls.
U <- matrix(rnorm(100), 100, 1) W <- nc_cpg(U, covariates = NULL, params = list(n_features = 10, coverage = 0.7, n_cpg = 20)) dim(W)U <- matrix(rnorm(100), 100, 1) W <- nc_cpg(U, covariates = NULL, params = list(n_features = 10, coverage = 0.7, n_cpg = 20)) dim(W)
For each control feature, computes the partial correlation with the genetic instrument G after residualising both on observed covariates C, and reports the p-value. Controls significantly associated with G after FDR correction may carry meQTL / allele-specific effects that violate the instrument-independence assumption (A2).
nc_independence_check(dat, fdr_level = 0.1, n_cores = 1)nc_independence_check(dat, fdr_level = 0.1, n_cores = 1)
dat |
Dataset list from |
fdr_level |
Target FDR for BH correction. Default 0.10. |
n_cores |
Number of parallel workers. Default 1 (sequential).
Uses |
A data frame with one row per control feature:
feature, partial_r, p_value, fdr, significant, verdict.
dat <- run_single_iteration(n_features = 10, seed = 1) nc_independence_check(dat)dat <- run_single_iteration(n_features = 10, seed = 1) nc_independence_check(dat)
For each control feature, computes the partial correlation with the
mediator-specific genetic instrument Gm after residualising both on
observed covariates C, and reports the p-value. Controls significantly
associated with Gm after FDR correction may carry eQTL / allele-specific
effects that violate the mediator-instrument-independence assumption
(A2'). This is the mediator-instrument analogue of
nc_independence_check(): just as the exposure instrument G must be
independent of the negative controls, so must the mediator instrument Gm.
nc_independence_check_gm(dat, fdr_level = 0.1, n_cores = 1)nc_independence_check_gm(dat, fdr_level = 0.1, n_cores = 1)
dat |
Dataset list from |
fdr_level |
Target FDR for BH correction. Default 0.10. |
n_cores |
Number of parallel workers. Default 1 (sequential).
Uses |
For a mediator cis-eQTL instrument, this screen tests whether the eQTL instrument is associated with the negative-control panel – a violation would indicate shared genomic structure between the instrument SNPs and the control features.
A data frame with one row per control feature:
feature, partial_r, p_value, fdr, significant, verdict.
Returns NULL if dat$Gm is not present.
dat <- run_single_iteration(n_features = 10, phi = 0.8, seed = 1) nc_independence_check_gm(dat)dat <- run_single_iteration(n_features = 10, phi = 0.8, seed = 1) nc_independence_check_gm(dat)
Each control is a coverage-weighted mixture of the captured confounders plus
noise. With one confounder and coverage = omega this reduces to
the classic w_signal proxy in generate_toy_data(). Setting captured to a
strict subset of the confounders models controls that miss part of the
confounder subspace (invalidating the negative-control assumption).
nc_proxy(U, covariates, params)nc_proxy(U, covariates, params)
U |
|
covariates |
Covariate data frame (unused; kept for the NC contract). |
params |
List with |
When noise_cor is supplied (a p x p correlation matrix), the noise
component is drawn from a multivariate normal with that correlation
structure, so the negative controls retain realistic cross-feature
correlations conditional on the confounder. When noise_cor is NULL
(default), the noise is independent across features.
n x n_features matrix of negative controls.
"shared"All columns carry the same rowMeans(U[, captured])
signal. This is the original behaviour (backward-compatible) and is
numerically stable, but the W matrix has only one effective dimension
regardless of n_features, so the proximal completeness condition is
never binding for the matrix-bridge PGC.
"distinct"Column f captures confounder
captured[((f - 1) \%\% length(captured)) + 1]. Different columns
therefore carry signals from different confounders, giving the W
matrix genuine dimensional structure. This is the mode to use when
benchmarking the completeness cliff: the matrix-bridge PGC is
identified only when n_features >= k.
U <- matrix(rnorm(100), 100, 1) W <- nc_proxy(U, covariates = NULL, params = list(n_features = 10, coverage = 0.7)) dim(W)U <- matrix(rnorm(100), 100, 1) W <- nc_proxy(U, covariates = NULL, params = list(n_features = 10, coverage = 0.7)) dim(W)
Diagnostic for whether the negative-control panel captures the full
support of the confounder, or only part of it. Uses a confounder proxy
U_tilde = resid(X ~ G + C) (the instrument-purged exposure
residual, which carries the confounding signal) and asks how much of
U_tilde the NC panel explains, and whether each individual NC
covers a distinct share of that signal.
nc_support_check(dat, fdr_level = 0.1)nc_support_check(dat, fdr_level = 0.1)
dat |
Dataset list from |
fdr_level |
FDR level for flagging controls whose unique contribution is indistinguishable from zero. Default 0.10. |
A panel can pass the count-based completeness check
(dim(W_valid) >= k) and the covariance-capture test while still
covering only part of the confounder support — for example when every
control loads on the same single confounder direction. This diagnostic
reports the multivariate R^2(U_tilde | W) (how much of the
confounder proxy the panel explains) and a per-NC support_ratio
(the partial correlation of each control with U_tilde given the
other controls), which flags controls that add no unique coverage.
This is a diagnostic, not a gate: U_tilde is itself an imperfect
proxy (it mixes the confounder with exposure noise), so the values are
interpretable only comparatively across controls and panels.
A list with:
R2_utilde_given_W (multivariate R^2 of the confounder proxy on
the full NC panel),
support (data frame with per-NC support_ratio = partial
correlation of the control with U_tilde given the other
controls, p_value, and adds_coverage flag),
n_controls, and verdict ("broad" when
R2_utilde_given_W >= 0.5, "partial" when >= 0.2, else "narrow").
dat <- run_single_iteration(n_features = 10, n_confounders = 1, seed = 1) nc_support_check(dat)dat <- run_single_iteration(n_features = 10, n_confounders = 1, seed = 1) nc_support_check(dat)
Focuses on whether the negative-control estimators (COCA, PGC) hold as the controls' coverage of the confounder subspace drops and as the number of latent confounders grows. Reports their bias/RMSE alongside IV2SLS, and flags scenarios where identification is not credible — in particular when the number of latent confounders exceeds the effective number of valid controls (the proximal-inference completeness condition).
nc_validity_check( trained_gan = NULL, coverage_grid = c(0.2, 0.5, 0.8, 1), k_grid = c(1, 2, 3), conf_strength = 0.8, n_valid_controls = 1, n_iter = 50, n_samples = 500, n_features = 20, nc_model = "proxy", base_seed = 800, n_cores = 1 )nc_validity_check( trained_gan = NULL, coverage_grid = c(0.2, 0.5, 0.8, 1), k_grid = c(1, 2, 3), conf_strength = 0.8, n_valid_controls = 1, n_iter = 50, n_samples = 500, n_features = 20, nc_model = "proxy", base_seed = 800, n_cores = 1 )
trained_gan |
An |
coverage_grid |
Coverage values to sweep. Default |
k_grid |
Numbers of confounders. Default |
conf_strength |
Fixed confounding strength. Default 0.8. |
n_valid_controls |
Number of distinct valid controls the design provides (for the identifiability flag). Default 1. |
n_iter, n_samples, n_features, nc_model, base_seed, n_cores
|
As in
|
The matrix-bridge PGC is the estimator for which the completeness condition
is binding: when k > n_valid_controls, PGC is under-identified while
IV2SLS remains unbiased (it does not depend on NC completeness).
A list with summary (COCA/PGC/IV2SLS bias & RMSE per scenario,
with an identified flag) and verdict (short per-scenario diagnosis).
chk <- nc_validity_check(NULL, coverage_grid = 0.7, k_grid = 1, n_iter = 2, n_samples = 100, n_features = 5) chk$verdictchk <- nc_validity_check(NULL, coverage_grid = 0.7, k_grid = 1, n_iter = 2, n_samples = 100, n_features = 5) chk$verdict
Regresses each negative-control feature on the exposure X (plus observed covariates C) and flags controls whose association with X survives Benjamini-Hochberg FDR control at the requested level.
nc_validity_screen( dat, fdr_level = 0.1, alpha = 0.05, n_cores = 1, criterion = c("both", "fdr", "magnitude"), magnitude_threshold = 0.1, u_proxy = NULL )nc_validity_screen( dat, fdr_level = 0.1, alpha = 0.05, n_cores = 1, criterion = c("both", "fdr", "magnitude"), magnitude_threshold = 0.1, u_proxy = NULL )
dat |
Dataset list from |
fdr_level |
Target false-discovery rate for BH correction. Default 0.10. |
alpha |
Per-test significance level used before FDR adjustment (informational only). Default 0.05. |
n_cores |
Number of parallel workers. Default 1 (sequential).
Uses |
criterion |
Which criterion to use: |
magnitude_threshold |
|partial_r(W,X|C)| cutoff for the magnitude branch. Default 0.10. |
u_proxy |
Optional outcome-derived proxy for U (e.g. the
leading residualized-outcome principal component) used
to compute |
A control that is significantly associated with X after adjusting for C violates the negative-control assumption (A1): it is either Such controls should be dropped before running COCA / PGC.
A data frame with one row per control feature:
feature, p_value, fdr, partial_r, relative_effect,
significant (composite, per criterion),
verdict (composite), verdict_fdr, verdict_magnitude.
A1 states that negative-control outcomes are independent of the exposure conditional on observed covariates C and the unmeasured confounder U (U is unobserved). NC outcome proxies may cause or be caused by the outcome or instruments; the screen tests the observable marginal association with X given C.
criterion = "fdr" (the legacy behavior) flags controls whose
association with X survives BH-FDR. criterion = "magnitude" flags
controls by partial-correlation size (|partial_r(W,X|C)| >
magnitude_threshold), which is less vulnerable to the "always
significant via U" problem: because W shares U with X by construction,
the FDR test can flag valid controls as violations purely from the
intended confounder-sharing signal. criterion = "both" (default)
requires a control to pass both branches to be deemed valid.
Distinguishing "W downstream of X" (a true A1 violation) from "W shares
a cause with X" (the intended NC behavior) remains an open problem
without a clean test statistic. The relative_effect column
(|partial_r(W,X|C)| / |partial_r(W,U_proxy|C)|, when u_proxy is
supplied) is exposed as a diagnostic: a control downstream of X tends
to have a larger W-X association relative to its W-U association, but
the separation is not always clean. Analysts should inspect
partial_r and relative_effect together rather than relying on a
single gate.
dat <- run_single_iteration(n_features = 10, seed = 1) nc_validity_screen(dat)dat <- run_single_iteration(n_features = 10, seed = 1) nc_validity_screen(dat)
Plot absolute bias vs a swept parameter
plot_bias( sweep_df, param_label = "Parameter Value", methods = iconic_method_order, title = "Absolute Bias vs Parameter" )plot_bias( sweep_df, param_label = "Parameter Value", methods = iconic_method_order, title = "Absolute Bias vs Parameter" )
sweep_df |
Data frame from sweep_param()$summary. |
param_label |
X-axis label. Default "Parameter Value". |
methods |
Methods to include. Default: all five. |
title |
Plot title. |
Invisibly returns NULL; called for the side effect of plotting absolute bias vs a parameter.
sweep <- sweep_param("conf_str", c(0.5, 0.8), n_iter = 2, n_samples = 100, n_features = 5) plot_bias(sweep$summary)sweep <- sweep_param("conf_str", c(0.5, 0.8), n_iter = 2, n_samples = 100, n_features = 5) plot_bias(sweep$summary)
Grouped boxplots of per-seed bias across a parameter sweep
plot_bias_boxplot( iter_bias, param_grid, xlab, ylab = "Bias (mean estimate - true)", main = "", xfmt = "%.2f", legend_pos = "topleft", methods = iconic_method_order )plot_bias_boxplot( iter_bias, param_grid, xlab, ylab = "Bias (mean estimate - true)", main = "", xfmt = "%.2f", legend_pos = "topleft", methods = iconic_method_order )
iter_bias |
Data frame with columns iter, method, bias, pval. |
param_grid |
Sorted numeric vector of parameter values. |
xlab |
X-axis label. |
ylab |
Y-axis label. Default "Bias (mean estimate - true)". |
main |
Plot title. |
xfmt |
Format string for x-axis labels. Default "%.2f". |
legend_pos |
Legend position. Default "topleft". |
methods |
Methods to include. Default: all five. |
Invisibly returns NULL; called for the side effect of drawing a boxplot of bias across parameter values.
sweep <- sweep_param("conf_str", c(0.5, 0.8), n_iter = 2, n_samples = 100, n_features = 5) plot_bias_boxplot(sweep$iter_bias, c(0.5, 0.8), xlab = "Confounding strength")sweep <- sweep_param("conf_str", c(0.5, 0.8), n_iter = 2, n_samples = 100, n_features = 5) plot_bias_boxplot(sweep$iter_bias, c(0.5, 0.8), xlab = "Confounding strength")
Baseline bias distribution (single-setting hero plot)
plot_bias_distribution(sim_result, methods = iconic_method_order, title = NULL)plot_bias_distribution(sim_result, methods = iconic_method_order, title = NULL)
sim_result |
Object returned by run_simulation(). |
methods |
Methods to include. Default: all five. |
title |
Plot title. If NULL a default is constructed. |
A ggplot object showing the distribution of bias estimates by method.
sim <- run_simulation(n_iter = 2, n_samples = 100, n_features = 5) plot_bias_distribution(sim)sim <- run_simulation(n_iter = 2, n_samples = 100, n_features = 5) plot_bias_distribution(sim)
Produces a 3-panel figure showing estimator bias under simultaneous instrument exogeneity violations. Panels: (A) IV2SLS2 NDE bias surface, (B) PGC2Gm NDE bias surface, (C) crossover map showing which estimator has lower |NDE bias| at each grid cell.
plot_degradation_surface( results, rho_G1_grid = c(0, 0.1, 0.2, 0.3, 0.5), rho_G2_grid = c(0, 0.1, 0.2, 0.3, 0.5), omega_facet = TRUE, file = NULL, width = 15, height = 5.5 )plot_degradation_surface( results, rho_G1_grid = c(0, 0.1, 0.2, 0.3, 0.5), rho_G2_grid = c(0, 0.1, 0.2, 0.3, 0.5), omega_facet = TRUE, file = NULL, width = 15, height = 5.5 )
results |
Data frame with columns |
rho_G1_grid |
Numeric vector of exposure-instrument violation values. |
rho_G2_grid |
Numeric vector of mediator-instrument violation values. |
omega_facet |
Logical: when the sensitivity surface swept
|
file |
Optional file path to save the figure. |
width |
Figure width in inches. |
height |
Figure height in inches. |
A patchwork ggplot object.
results <- expand.grid(rho_G1 = c(0, 0.2), rho_G2 = c(0, 0.2), method = c("IV2SLS2", "PGC2Gm")) results$NDE_bias <- rnorm(nrow(results), 0, 0.05) results$NIE_bias <- rnorm(nrow(results), 0, 0.05) plot_degradation_surface(results, rho_G1_grid = c(0, 0.2), rho_G2_grid = c(0, 0.2))results <- expand.grid(rho_G1 = c(0, 0.2), rho_G2 = c(0, 0.2), method = c("IV2SLS2", "PGC2Gm")) results$NDE_bias <- rnorm(nrow(results), 0, 0.05) results$NIE_bias <- rnorm(nrow(results), 0, 0.05) plot_degradation_surface(results, rho_G1_grid = c(0, 0.2), rho_G2_grid = c(0, 0.2))
Boxplot of estimate distributions from run_simulation()
plot_estimate_distribution( sim_result, methods = iconic_method_order, title = NULL )plot_estimate_distribution( sim_result, methods = iconic_method_order, title = NULL )
sim_result |
Object returned by run_simulation(). |
methods |
Methods to include. Default: all five. |
title |
Plot title. |
A ggplot object showing the distribution of estimates by method.
sim <- run_simulation(n_iter = 2, n_samples = 100, n_features = 5) plot_estimate_distribution(sim)sim <- run_simulation(n_iter = 2, n_samples = 100, n_features = 5) plot_estimate_distribution(sim)
Plot estimated effect vs true effect
plot_estimated_vs_true( sweep_df, methods = iconic_method_order, title = "Estimated vs True Effect" )plot_estimated_vs_true( sweep_df, methods = iconic_method_order, title = "Estimated vs True Effect" )
sweep_df |
Data frame from sweep_param()$summary. |
methods |
Methods to plot. Default: all five. |
title |
Plot title. |
Invisibly returns NULL; called for the side effect of plotting estimated vs true effects.
sweep <- sweep_param("beta_X", c(0.1, 0.3), n_iter = 2, n_samples = 100, n_features = 5) plot_estimated_vs_true(sweep$summary)sweep <- sweep_param("beta_X", c(0.1, 0.3), n_iter = 2, n_samples = 100, n_features = 5) plot_estimated_vs_true(sweep$summary)
Produces a 4-panel publication figure benchmarking the eight ICONIC estimators under unmeasured confounding and mediator-outcome confounding. Panels: (A) total-effect bias vs confounding strength, (B) NDE/NIE bias vs confounding strength, (C) NDE/NIE bias vs sample size, (D) NIE Type I error vs confounding strength.
plot_estimator_benchmark( panel_a, panel_b, panel_c, panel_d, conf_grid = c(0.2, 0.4, 0.6, 0.8, 1), n_grid = c(100, 200, 500, 1000), file = NULL, width = 8, height = 6 )plot_estimator_benchmark( panel_a, panel_b, panel_c, panel_d, conf_grid = c(0.2, 0.4, 0.6, 0.8, 1), n_grid = c(100, 200, 500, 1000), file = NULL, width = 8, height = 6 )
panel_a |
Result of |
panel_b |
Result of |
panel_c |
Result of |
panel_d |
Result of |
conf_grid |
Numeric vector of confounding-strength values. |
n_grid |
Numeric vector of sample sizes. |
file |
Optional file path to save the figure (PDF or PNG). |
width |
Figure width in inches. |
height |
Figure height in inches. |
A patchwork ggplot object.
# Toy inputs standing in for sweep_param()/sweep_mediation_param() output methods <- c("UNADJ", "DIRECT", "COCA", "IV2SLS", "PGC", "IV2SLS2", "PGC2", "PGC2Gm") it <- expand.grid(method = methods, pval = c(0.5, 0.8), rep = 1:3) it$bias <- rnorm(nrow(it), 0, 0.05) it$NDE_bias <- rnorm(nrow(it), 0, 0.05) it$NIE_bias <- rnorm(nrow(it), 0, 0.05) panel <- list(iter_bias = it) panel_d <- expand.grid(method = methods, conf_str = c(0.5, 0.8)) panel_d$NIE_type1 <- runif(nrow(panel_d), 0, 0.1) plot_estimator_benchmark(panel, panel, panel, panel_d, conf_grid = c(0.5, 0.8), n_grid = c(0.5, 0.8))# Toy inputs standing in for sweep_param()/sweep_mediation_param() output methods <- c("UNADJ", "DIRECT", "COCA", "IV2SLS", "PGC", "IV2SLS2", "PGC2", "PGC2Gm") it <- expand.grid(method = methods, pval = c(0.5, 0.8), rep = 1:3) it$bias <- rnorm(nrow(it), 0, 0.05) it$NDE_bias <- rnorm(nrow(it), 0, 0.05) it$NIE_bias <- rnorm(nrow(it), 0, 0.05) panel <- list(iter_bias = it) panel_d <- expand.grid(method = methods, conf_str = c(0.5, 0.8)) panel_d$NIE_type1 <- runif(nrow(panel_d), 0, 0.1) plot_estimator_benchmark(panel, panel, panel, panel_d, conf_grid = c(0.5, 0.8), n_grid = c(0.5, 0.8))
Produces a 3-panel figure showing how estimator performance changes as within-module feature correlation increases, modelling co-expression modules in a correlated omics panel.
plot_feature_correlation_sweep( sweep_results, null_results = NULL, file = NULL, width = 10, height = 12 )plot_feature_correlation_sweep( sweep_results, null_results = NULL, file = NULL, width = 10, height = 12 )
sweep_results |
Results from
|
null_results |
Optional results from
|
file |
Optional file path to save the figure. |
width |
Figure width in inches. Default 10. |
height |
Figure height in inches. Default 12. |
Panel A: NDE and NIE bias vs feature correlation strength, faceted by estimand. Panel B: NDE and NIE RMSE vs feature correlation strength, faceted by estimand. Panel C: NIE Type I error vs feature correlation strength.
A patchwork ggplot object.
# Toy inputs standing in for sweep_mediation_param("feat_cor", ...) output sweep <- list(summary = expand.grid(param_value = c(0, 0.5), method = c("UNADJ", "IV2SLS2", "PGC2Gm"))) sweep$summary$NDE_bias <- rnorm(nrow(sweep$summary), 0, 0.05) sweep$summary$NIE_bias <- rnorm(nrow(sweep$summary), 0, 0.05) sweep$summary$NDE_rmse <- runif(nrow(sweep$summary), 0.05, 0.15) sweep$summary$NIE_rmse <- runif(nrow(sweep$summary), 0.05, 0.15) null <- expand.grid(feat_cor = c(0, 0.5), method = c("UNADJ", "IV2SLS2", "PGC2Gm")) null$NIE_type1 <- runif(nrow(null), 0, 0.1) plot_feature_correlation_sweep(sweep, null)# Toy inputs standing in for sweep_mediation_param("feat_cor", ...) output sweep <- list(summary = expand.grid(param_value = c(0, 0.5), method = c("UNADJ", "IV2SLS2", "PGC2Gm"))) sweep$summary$NDE_bias <- rnorm(nrow(sweep$summary), 0, 0.05) sweep$summary$NIE_bias <- rnorm(nrow(sweep$summary), 0, 0.05) sweep$summary$NDE_rmse <- runif(nrow(sweep$summary), 0.05, 0.15) sweep$summary$NIE_rmse <- runif(nrow(sweep$summary), 0.05, 0.15) null <- expand.grid(feat_cor = c(0, 0.5), method = c("UNADJ", "IV2SLS2", "PGC2Gm")) null$NIE_type1 <- runif(nrow(null), 0, 0.1) plot_feature_correlation_sweep(sweep, null)
For continuous variables, overlays kernel densities of the real training
variables against draws from the trained generator. For binary (0/1)
variables – detected at training time and stored in the iconic_gan object
– uses side-by-side bar charts of the proportion of 1s, which is the
appropriate visualisation for discrete data. One panel per variable.
plot_gan_diagnostics(trained_gan, real_data, n_draw = NULL, M_matrix = NULL)plot_gan_diagnostics(trained_gan, real_data, n_draw = NULL, M_matrix = NULL)
trained_gan |
An |
real_data |
The data frame the generator was trained on. |
n_draw |
Synthetic rows to draw for the overlay. Default: number of real rows. |
M_matrix |
Optional mediator panel (features x samples) for feature-level marginal comparison. When supplied and the GAN carries a feature_texture, an additional panel of density overlays compares real vs copula-sampled mediator marginals. |
Called for its side effects; invisibly returns NULL.
if (check_torch_setup()) { dat <- load_real_input_data(example = TRUE) gan <- train_gan_on_real_data(dat$gan_training_data, epochs = 5, verbose = FALSE) plot_gan_diagnostics(gan, dat$gan_training_data) }if (check_torch_setup()) { dat <- load_real_input_data(example = TRUE) gan <- train_gan_on_real_data(dat$gan_training_data, epochs = 5, verbose = FALSE) plot_gan_diagnostics(gan, dat$gan_training_data) }
Produces a 2-panel figure showing IV2SLS bias and Type I error as a function of first-stage partial F-statistic, with the conventional weak-instrument threshold (F = 10) marked.
plot_instrument_strength_sweep( results, tau = 0.25, file = NULL, width = 10, height = 4.5 )plot_instrument_strength_sweep( results, tau = 0.25, file = NULL, width = 10, height = 4.5 )
results |
Data frame from |
tau |
True total effect used in the sweep. |
file |
Optional file path to save the figure. |
width |
Figure width in inches. |
height |
Figure height in inches. |
A patchwork ggplot object.
res <- sweep_instrument_strength(pi_GX_grid = c(0.05, 0.2), n_iter = 2) plot_instrument_strength_sweep(res)res <- sweep_instrument_strength(pi_GX_grid = c(0.05, 0.2), n_iter = 2) plot_instrument_strength_sweep(res)
Produces a 3-panel figure showing the ICONIC model selection
workflow on example data. Panels: (A) eligibility table from
iconic_diagnose(), (B) NDE/NIE forest plot from
iconic_estimate(), (C) degradation surface heatmap from
iconic_sensitivity().
plot_model_selection( diagnosis, estimate, sensitivity, recommendation, file = NULL, width = 12, height = 14 )plot_model_selection( diagnosis, estimate, sensitivity, recommendation, file = NULL, width = 12, height = 14 )
diagnosis |
Result of |
estimate |
Result of |
sensitivity |
Result of |
recommendation |
Result of |
file |
Optional file path to save the figure. |
width |
Figure width in inches. |
height |
Figure height in inches. |
A patchwork ggplot object.
if (check_torch_setup()) { data <- iconic_data(X = rnorm(100), Y = matrix(rnorm(100 * 10), 10, 100), M = rnorm(100), G = rnorm(100), Gm = rnorm(100), W = matrix(rnorm(100 * 10), 10, 100)) diag <- iconic_diagnose(data) est <- iconic_estimate(data, diagnosis = diag) sens <- iconic_sensitivity(data, n_iter = 2, gan_epochs = 5, rho_G1_grid = c(0, 0.2), rho_G2_grid = c(0, 0.2)) rec <- iconic_recommend(data, diagnosis = diag, estimate = est, sensitivity = sens, auto_sensitivity = FALSE) plot_model_selection(diag, est, sens, rec) }if (check_torch_setup()) { data <- iconic_data(X = rnorm(100), Y = matrix(rnorm(100 * 10), 10, 100), M = rnorm(100), G = rnorm(100), Gm = rnorm(100), W = matrix(rnorm(100 * 10), 10, 100)) diag <- iconic_diagnose(data) est <- iconic_estimate(data, diagnosis = diag) sens <- iconic_sensitivity(data, n_iter = 2, gan_epochs = 5, rho_G1_grid = c(0, 0.2), rho_G2_grid = c(0, 0.2)) rec <- iconic_recommend(data, diagnosis = diag, estimate = est, sensitivity = sens, auto_sensitivity = FALSE) plot_model_selection(diag, est, sens, rec) }
Produces a 4-panel figure comparing PGC2, PGC2Gm, and IV2SLS2 as negative-control coverage of each path's confounder composite (omega_1, omega_2) drops. This is the coverage-axis analogue of the instrument-violation degradation surface: it shows how bias accumulates as the proxy panel captures less of the confounder.
plot_nc_coverage_comparison( omega1_sweep, omega2_sweep, t1e_omega2 = NULL, file = NULL, width = 10, height = 12 )plot_nc_coverage_comparison( omega1_sweep, omega2_sweep, t1e_omega2 = NULL, file = NULL, width = 10, height = 12 )
omega1_sweep |
Summary from
|
omega2_sweep |
Same, sweeping |
t1e_omega2 |
Result of
|
file |
Optional file path to save the figure. |
width |
Figure width in inches. |
height |
Figure height in inches. |
Panels: (A) NDE bias vs omega_1, (B) NIE bias vs omega_2, (C) NDE/NIE bias vs omega (both paths), (D) NIE Type I error vs omega_2.
A patchwork ggplot object.
# Toy sweeps standing in for sweep_mediation_param("omega_1"/"omega_2", ...) mk <- function() { s <- expand.grid(param_value = c(0.3, 0.7), method = c("IV2SLS2", "PGC2", "PGC2Gm")) s$NDE_bias <- rnorm(nrow(s), 0, 0.05) s$NIE_bias <- rnorm(nrow(s), 0, 0.05) list(summary = s) } plot_nc_coverage_comparison(mk(), mk())# Toy sweeps standing in for sweep_mediation_param("omega_1"/"omega_2", ...) mk <- function() { s <- expand.grid(param_value = c(0.3, 0.7), method = c("IV2SLS2", "PGC2", "PGC2Gm")) s$NDE_bias <- rnorm(nrow(s), 0, 0.05) s$NIE_bias <- rnorm(nrow(s), 0, 0.05) list(summary = s) } plot_nc_coverage_comparison(mk(), mk())
Produces a 5-panel figure sweeping the empirical NC validity diagnostics. Panels: (A) A1 W perp X | C, (B) A2 W perp G | C, (C) A2' W perp Gm | C, (D) A3 covariance-capture versus true coverage omega (with permutation-null mean and the fraction of replicates with permutation p < 0.05 on a secondary axis), and (E) A3 support R2(Utilde | W) versus omega.
plot_nc_validity_diagnostics(panels, file = NULL, width = 10, height = 6.5)plot_nc_validity_diagnostics(panels, file = NULL, width = 10, height = 6.5)
panels |
List returned by |
file |
Optional file path to save the figure (cairo_pdf, so the omega and Utilde glyphs render). |
width |
Figure width in inches. Default 10. |
height |
Figure height in inches. Default 6.5. |
A patchwork ggplot object.
# Toy panels standing in for sweep_nc_validity() output panels <- list( panel_a = data.frame(contamination = c(0, 0.1), violated_mean = c(0.1, 0.3), violated_sd = 0.05, confounding_mean = c(0.1, 0.12), confounding_sd = 0.05), panel_b = data.frame(meqtl = c(0, 0.1), violated_mean = c(0.1, 0.3), violated_sd = 0.05, clean_mean = c(0.1, 0.12), clean_sd = 0.05), panel_d = data.frame(eqtl = c(0, 0.1), violated_mean = c(0.1, 0.3), violated_sd = 0.05, clean_mean = c(0.1, 0.12), clean_sd = 0.05), panel_capture_support = data.frame( omega = rep(c(0, 0.5), each = 2), rep = 1:2, capture_R2 = c(0.02, 0.03, 0.25, 0.30), capture_p = c(0.40, 0.60, 0.01, 0.02), null_R2 = 0.02, support_R2 = c(0.02, 0.03, 0.35, 0.40), frac_adds_coverage = 1)) plot_nc_validity_diagnostics(panels)# Toy panels standing in for sweep_nc_validity() output panels <- list( panel_a = data.frame(contamination = c(0, 0.1), violated_mean = c(0.1, 0.3), violated_sd = 0.05, confounding_mean = c(0.1, 0.12), confounding_sd = 0.05), panel_b = data.frame(meqtl = c(0, 0.1), violated_mean = c(0.1, 0.3), violated_sd = 0.05, clean_mean = c(0.1, 0.12), clean_sd = 0.05), panel_d = data.frame(eqtl = c(0, 0.1), violated_mean = c(0.1, 0.3), violated_sd = 0.05, clean_mean = c(0.1, 0.12), clean_sd = 0.05), panel_capture_support = data.frame( omega = rep(c(0, 0.5), each = 2), rep = 1:2, capture_R2 = c(0.02, 0.03, 0.25, 0.30), capture_p = c(0.40, 0.60, 0.01, 0.02), null_R2 = 0.02, support_R2 = c(0.02, 0.03, 0.35, 0.40), frac_adds_coverage = 1)) plot_nc_validity_diagnostics(panels)
Produces a faceted figure showing estimator bias and Type I error under horizontal pleiotropy, across three confounding strengths.
plot_pleiotropy_sweep(sensitivity, file = NULL, width = 10, height = 5.5)plot_pleiotropy_sweep(sensitivity, file = NULL, width = 10, height = 5.5)
sensitivity |
Result of |
file |
Optional file path to save the figure. |
width |
Figure width in inches. |
height |
Figure height in inches. |
A ggplot object.
sens <- gan_pleiotropy_sensitivity(NULL, pleio_grid = c(0, 0.1), conf_grid = 0.8, n_iter = 2, n_samples = 100, n_features = 5) plot_pleiotropy_sweep(sens)sens <- gan_pleiotropy_sensitivity(NULL, pleio_grid = c(0, 0.1), conf_grid = 0.8, n_iter = 2, n_samples = 100, n_features = 5) plot_pleiotropy_sweep(sens)
Plot detection rate (power) vs a swept parameter
plot_power( sweep_df, param_label = "Parameter Value", methods = iconic_method_order, title = "Detection Rate vs Parameter", legend_pos = "bottomleft" )plot_power( sweep_df, param_label = "Parameter Value", methods = iconic_method_order, title = "Detection Rate vs Parameter", legend_pos = "bottomleft" )
sweep_df |
Data frame from sweep_param()$summary. |
param_label |
X-axis label. |
methods |
Methods to include. |
title |
Plot title. |
legend_pos |
Legend position. Default "bottomleft". |
Invisibly returns NULL; called for the side effect of plotting detection rate vs a parameter.
sweep <- sweep_param("conf_str", c(0.5, 0.8), n_iter = 2, n_samples = 100, n_features = 5) plot_power(sweep$summary)sweep <- sweep_param("conf_str", c(0.5, 0.8), n_iter = 2, n_samples = 100, n_features = 5) plot_power(sweep$summary)
Produces a 2-panel figure showing expected gains from adding genetic instruments. Panel A: NDE bias vs instrument strength. Panel B: prospective NDE/NIE estimates at target instrument strength.
plot_prospective_analysis(prospect, file = NULL, width = 12, height = 8)plot_prospective_analysis(prospect, file = NULL, width = 12, height = 8)
prospect |
Result of |
file |
Optional file path to save the figure. |
width |
Figure width in inches. |
height |
Figure height in inches. |
A patchwork ggplot object.
if (check_torch_setup()) { data <- iconic_data(X = rnorm(100), Y = matrix(rnorm(100 * 10), 10, 100), M = rnorm(100)) pros <- iconic_prospect(data, n_iter = 2, gan_epochs = 5, gamma_G_grid = c(0.4, 0.8), run_rho_sweep = FALSE) plot_prospective_analysis(pros) }if (check_torch_setup()) { data <- iconic_data(X = rnorm(100), Y = matrix(rnorm(100 * 10), 10, 100), M = rnorm(100)) pros <- iconic_prospect(data, n_iter = 2, gan_epochs = 5, gamma_G_grid = c(0.4, 0.8), run_rho_sweep = FALSE) plot_prospective_analysis(pros) }
Draws a coverage x confounding-strength heatmap of a chosen metric for one estimator, at a fixed number of confounders. Useful for reading off where a method's bias/RMSE blows up.
plot_sensitivity_heatmap( sens, metric = "rmse", method = "IV2SLS", k = NULL, title = NULL )plot_sensitivity_heatmap( sens, metric = "rmse", method = "IV2SLS", k = NULL, title = NULL )
sens |
Object returned by |
metric |
Column of |
method |
Estimator to display. Default "IV2SLS". |
k |
Number of confounders to slice on. Default: first in the grid. |
title |
Optional plot title. |
Invisibly returns NULL; called for the side effect of drawing a sensitivity heatmap.
sens <- gan_sensitivity(NULL, conf_grid = c(0.5, 0.8), coverage_grid = c(0.3, 0.7), n_iter = 2, n_samples = 100, n_features = 5) plot_sensitivity_heatmap(sens)sens <- gan_sensitivity(NULL, conf_grid = c(0.5, 0.8), coverage_grid = c(0.3, 0.7), n_iter = 2, n_samples = 100, n_features = 5) plot_sensitivity_heatmap(sens)
Type I error boxplot per method
plot_type1_boxplot( null_result, methods = iconic_method_order, conf_str = 0.8, alpha = 0.05 )plot_type1_boxplot( null_result, methods = iconic_method_order, conf_str = 0.8, alpha = 0.05 )
null_result |
Object returned by run_null_sim(). |
methods |
Methods to include. Default: all five. |
conf_str |
Confounding strength used (for the title). Default 0.80. |
alpha |
Nominal significance level. Default 0.05. |
Invisibly returns NULL; called for the side effect of drawing a boxplot of Type I error rates.
null <- run_null_sim(n_iter = 2, n_samples = 100, n_features = 5) plot_type1_boxplot(null)null <- run_null_sim(n_iter = 2, n_samples = 100, n_features = 5) plot_type1_boxplot(null)
Bar chart of Type I error rates
plot_type1_error(null_df, title = "Type I Error Rate by Method")plot_type1_error(null_df, title = "Type I Error Rate by Method")
null_df |
Data frame: either run_null_sim()$rates or a data frame with columns method and type1_error. |
title |
Plot title. |
Invisibly returns NULL; called for the side effect of plotting Type I error rates by method.
t1e <- sweep_null_by_conf(0.8, n_iter = 2, n_samples = 100, n_features = 5) plot_type1_error(t1e)t1e <- sweep_null_by_conf(0.8, n_iter = 2, n_samples = 100, n_features = 5) plot_type1_error(t1e)
Type I error rate vs confounding strength
plot_type1_vs_conf( t1e_df, methods = iconic_method_order, alpha = 0.05, title = "Type I Error Rate vs Confounding Strength" )plot_type1_vs_conf( t1e_df, methods = iconic_method_order, alpha = 0.05, title = "Type I Error Rate vs Confounding Strength" )
t1e_df |
Data frame returned by sweep_null_by_conf(). |
methods |
Methods to plot. Default: all five. |
alpha |
Nominal significance level. Default 0.05. |
title |
Plot title. |
Invisibly returns NULL; called for the side effect of drawing Type I error vs confounding strength.
t1e <- sweep_null_by_conf(c(0.5, 0.8), n_iter = 2, n_samples = 100, n_features = 5) plot_type1_vs_conf(t1e)t1e <- sweep_null_by_conf(c(0.5, 0.8), n_iter = 2, n_samples = 100, n_features = 5) plot_type1_vs_conf(t1e)
Print method for iconic_confounding objects
## S3 method for class 'iconic_confounding' print(x, ...)## S3 method for class 'iconic_confounding' print(x, ...)
x |
An |
... |
Unused. |
Invisibly returns x (the iconic_confounding object); called for its side effect of printing a human-readable summary.
set.seed(1) data <- iconic_data(X = rnorm(100), Y = matrix(rnorm(100 * 10), 10, 100), G = rnorm(100), W = matrix(rnorm(100 * 10), 10, 100)) cf <- infer_confounding(data, max_infer_tasks = 5) print(cf)set.seed(1) data <- iconic_data(X = rnorm(100), Y = matrix(rnorm(100 * 10), 10, 100), G = rnorm(100), W = matrix(rnorm(100 * 10), 10, 100)) cf <- infer_confounding(data, max_infer_tasks = 5) print(cf)
Print method for iconic_data objects
## S3 method for class 'iconic_data' print(x, ...)## S3 method for class 'iconic_data' print(x, ...)
x |
An |
... |
Unused. |
Invisibly returns x (the iconic_data object); called for its side effect of printing a human-readable summary.
data <- iconic_data(X = rnorm(50), Y = matrix(rnorm(50 * 5), 5, 50), G = rnorm(50), W = matrix(rnorm(50 * 5), 5, 50)) print(data)data <- iconic_data(X = rnorm(50), Y = matrix(rnorm(50 * 5), 5, 50), G = rnorm(50), W = matrix(rnorm(50 * 5), 5, 50)) print(data)
Print method for iconic_diagnosis objects
## S3 method for class 'iconic_diagnosis' print(x, ...)## S3 method for class 'iconic_diagnosis' print(x, ...)
x |
An |
... |
Unused. |
Invisibly returns x (the iconic_diagnosis object); called for its side effect of printing a human-readable summary.
set.seed(1) data <- iconic_data(X = rnorm(100), Y = matrix(rnorm(100 * 10), 10, 100), G = rnorm(100), W = matrix(rnorm(100 * 10), 10, 100)) diag <- iconic_diagnose(data) print(diag)set.seed(1) data <- iconic_data(X = rnorm(100), Y = matrix(rnorm(100 * 10), 10, 100), G = rnorm(100), W = matrix(rnorm(100 * 10), 10, 100)) diag <- iconic_diagnose(data) print(diag)
Print method for iconic_feature_texture objects
## S3 method for class 'iconic_feature_texture' print(x, ...)## S3 method for class 'iconic_feature_texture' print(x, ...)
x |
An |
... |
Unused. |
Invisibly returns x (the iconic_feature_texture object); called for its side effect of printing a human-readable summary.
ft <- train_feature_texture(matrix(rnorm(20 * 100), 20, 100)) print(ft)ft <- train_feature_texture(matrix(rnorm(20 * 100), 20, 100)) print(ft)
Print method for iconic_gan objects
## S3 method for class 'iconic_gan' print(x, ...)## S3 method for class 'iconic_gan' print(x, ...)
x |
An |
... |
Unused. |
Invisibly returns x (the iconic_gan object); called for its side effect of printing a human-readable summary.
if (check_torch_setup()) { dat <- load_real_input_data(example = TRUE) gan <- train_gan_on_real_data(dat$gan_training_data, epochs = 5, verbose = FALSE) print(gan) }if (check_torch_setup()) { dat <- load_real_input_data(example = TRUE) gan <- train_gan_on_real_data(dat$gan_training_data, epochs = 5, verbose = FALSE) print(gan) }
Print method for iconic_prospect objects
## S3 method for class 'iconic_prospect' print(x, ...)## S3 method for class 'iconic_prospect' print(x, ...)
x |
An |
... |
Unused. |
Invisibly returns x (the iconic_prospect object); called for its side effect of printing a human-readable summary.
if (check_torch_setup()) { data <- iconic_data(X = rnorm(100), Y = matrix(rnorm(100 * 10), 10, 100), M = rnorm(100)) result <- iconic_prospect(data, n_iter = 2, gan_epochs = 5, gamma_G_grid = c(0.4, 0.8), run_rho_sweep = FALSE) print(result) }if (check_torch_setup()) { data <- iconic_data(X = rnorm(100), Y = matrix(rnorm(100 * 10), 10, 100), M = rnorm(100)) result <- iconic_prospect(data, n_iter = 2, gan_epochs = 5, gamma_G_grid = c(0.4, 0.8), run_rho_sweep = FALSE) print(result) }
Print method for iconic_recommendation objects
## S3 method for class 'iconic_recommendation' print(x, ...)## S3 method for class 'iconic_recommendation' print(x, ...)
x |
An |
... |
Unused. |
Invisibly returns x (the iconic_recommendation object); called for its side effect of printing a human-readable summary.
data <- iconic_data(X = rnorm(100), Y = matrix(rnorm(100 * 10), 10, 100), G = rnorm(100), W = matrix(rnorm(100 * 10), 10, 100)) diag <- iconic_diagnose(data) est <- iconic_estimate(data, diagnosis = diag) rec <- iconic_recommend(data, diagnosis = diag, estimate = est, auto_sensitivity = FALSE) print(rec)data <- iconic_data(X = rnorm(100), Y = matrix(rnorm(100 * 10), 10, 100), G = rnorm(100), W = matrix(rnorm(100 * 10), 10, 100)) diag <- iconic_diagnose(data) est <- iconic_estimate(data, diagnosis = diag) rec <- iconic_recommend(data, diagnosis = diag, estimate = est, auto_sensitivity = FALSE) print(rec)
Print method for iconic_sensitivity objects
## S3 method for class 'iconic_sensitivity' print(x, ...)## S3 method for class 'iconic_sensitivity' print(x, ...)
x |
An |
... |
Unused. |
Invisibly returns x (the iconic_sensitivity object); called for its side effect of printing a human-readable summary.
if (check_torch_setup()) { data <- iconic_data(X = rnorm(100), Y = matrix(rnorm(100 * 10), 10, 100), M = rnorm(100), G = rnorm(100), Gm = rnorm(100), W = matrix(rnorm(100 * 10), 10, 100)) sens <- iconic_sensitivity(data, n_iter = 2, gan_epochs = 5, rho_G1_grid = c(0, 0.2), rho_G2_grid = c(0, 0.2)) print(sens) }if (check_torch_setup()) { data <- iconic_data(X = rnorm(100), Y = matrix(rnorm(100 * 10), 10, 100), M = rnorm(100), G = rnorm(100), Gm = rnorm(100), W = matrix(rnorm(100 * 10), 10, 100)) sens <- iconic_sensitivity(data, n_iter = 2, gan_epochs = 5, rho_G1_grid = c(0, 0.2), rho_G2_grid = c(0, 0.2)) print(sens) }
Renames common GWAS summary-statistic column conventions to the
bigsnpr convention (chr, pos, a0, a1, beta, beta_se,
n_eff, freq) and applies the standard LDpred2 QC filters:
missing/invalid values, extreme effect sizes, ambiguous-strand
(A/T and C/G) SNPs, and — when effect allele frequencies are
available — the standard-deviation ratio check from the LDpred2
tutorial.
qc_gwas_sumstats( sumstats, n_eff = NULL, drop_ambiguous = TRUE, beta_iqr_mult = 10, sd_ratio_range = c(0.5, 2), sd_y = NULL )qc_gwas_sumstats( sumstats, n_eff = NULL, drop_ambiguous = TRUE, beta_iqr_mult = 10, sd_ratio_range = c(0.5, 2), sd_y = NULL )
sumstats |
A data.frame of GWAS summary statistics. Recognized
column aliases: |
n_eff |
Optional scalar: effective sample size, used when the input has no sample-size column. |
drop_ambiguous |
Logical: drop A/T and C/G SNPs whose strand
cannot be resolved. Default |
beta_iqr_mult |
Numeric: drop SNPs with
|
sd_ratio_range |
Numeric length-2: acceptable range for the
ratio of the summary-statistic-implied genotype SD to the
frequency-implied SD, |
sd_y |
Optional scalar: phenotypic SD used in the SD-ratio check.
When |
A list with:
data.frame of QC-passing variants with standardized
columns chr, pos, a0, a1, beta, beta_se, and (when
available) n_eff, freq, p, rsid.
data.frame with counts of input variants and variants dropped by each filter.
numeric vector of SD ratios for retained variants,
or NULL when the check was skipped.
ss <- data.frame( chromosome = 1, base_pair_location = 1000 + 0:9 * 1000L, effect_allele = rep(c("A", "C"), 5), other_allele = rep(c("G", "T"), 5), beta = rnorm(10, 0, 0.05), standard_error = 0.01, effect_allele_frequency = runif(10, 0.1, 0.5) ) out <- qc_gwas_sumstats(ss, n_eff = 50000) str(out$sumstats) out$qcss <- data.frame( chromosome = 1, base_pair_location = 1000 + 0:9 * 1000L, effect_allele = rep(c("A", "C"), 5), other_allele = rep(c("G", "T"), 5), beta = rnorm(10, 0, 0.05), standard_error = 0.01, effect_allele_frequency = runif(10, 0.1, 0.5) ) out <- qc_gwas_sumstats(ss, n_eff = 50000) str(out$sumstats) out$qc
Ranks methods by RMSE within each scenario, and identifies an overall pick that is robust across scenarios (smallest worst-case RMSE). UNADJ is excluded from recommendations (it is a bias reference).
recommend_estimator(sens, exclude = c("UNADJ"))recommend_estimator(sens, exclude = c("UNADJ"))
sens |
Object returned by |
exclude |
Methods to exclude from the recommendation. Default
|
A list with per_scenario (best method + RMSE per scenario),
worst_case (max RMSE per method across scenarios), and overall (the
method minimising worst-case RMSE).
sens <- gan_sensitivity(NULL, conf_grid = 0.8, coverage_grid = 0.7, n_iter = 2, n_samples = 100, n_features = 5) recommend_estimator(sens)sens <- gan_sensitivity(NULL, conf_grid = 0.8, coverage_grid = 0.7, n_iter = 2, n_samples = 100, n_features = 5) recommend_estimator(sens)
Regresses each feature (row) of x on the covariate design and
returns the residuals, using a precomputed projection for memory-
efficient chunked processing of large (e.g. 450k-probe) matrices.
This is the workhorse for removing cell composition, batch, and
technical factors before extracting negative-control PCs.
residualize_matrix(x, covariates, chunk_size = 5000)residualize_matrix(x, covariates, chunk_size = 5000)
x |
Numeric matrix, features x samples. |
covariates |
data.frame or matrix with one row per sample
(i.e. |
chunk_size |
Integer: number of features processed per block. Default 5000. |
A numeric matrix of residuals, features x samples, centered per feature.
x <- matrix(rnorm(100 * 40), nrow = 100, dimnames = list(paste0("f", 1:100), paste0("S", 1:40))) batch <- factor(rep(c("A", "B"), each = 20)) cv <- model.matrix(~ batch)[, -1, drop = FALSE] xr <- residualize_matrix(x, cv) cor(as.numeric(xr[1, ]), as.numeric(cv))x <- matrix(rnorm(100 * 40), nrow = 100, dimnames = list(paste0("f", 1:100), paste0("S", 1:40))) batch <- factor(rep(c("A", "B"), each = 20)) cv <- model.matrix(~ batch)[, -1, drop = FALSE] xr <- residualize_matrix(x, cv) cor(as.numeric(xr[1, ]), as.numeric(cv))
Generates n_iter synthetic datasets with mediator-outcome
confounding (via mo_confounding), runs all five (or six, when
phi > 0) mediation estimators, and summarises bias / RMSE / Type I
error for both NDE and NIE.
run_mediation_sim( n_iter = 100, n_samples = 500, n_features = 20, n_mediators = 1, beta_X = 0.1, alpha_M = 0.5, beta_M = 0.3, conf_str = 0.8, w_signal = 0.7, mo_confounding = 0.8, phi = 0, rho_G1 = 0, rho_G2 = 0, rho_pop = 0, lambda_XM = NULL, lambda_MY = NULL, omega_1 = NULL, omega_2 = NULL, feat_cor = 0, base_seed = 100, n_cores = 1 )run_mediation_sim( n_iter = 100, n_samples = 500, n_features = 20, n_mediators = 1, beta_X = 0.1, alpha_M = 0.5, beta_M = 0.3, conf_str = 0.8, w_signal = 0.7, mo_confounding = 0.8, phi = 0, rho_G1 = 0, rho_G2 = 0, rho_pop = 0, lambda_XM = NULL, lambda_MY = NULL, omega_1 = NULL, omega_2 = NULL, feat_cor = 0, base_seed = 100, n_cores = 1 )
n_iter |
Number of simulation replicates. Default 100. |
n_samples |
Observations per replicate. Default 500. |
n_features |
Number of outcome and negative-control features. Default 20. |
n_mediators |
Number of independent mediators. When > 1, each mediator has its own genetic instrument Gm and contributes additively to Y. Default 1 (single mediator, backward compatible). |
beta_X |
Direct effect of X on Y (true NDE). Default 0.10. |
alpha_M |
Effect of X on mediator. Default 0.50. |
beta_M |
Effect of mediator on Y (per-mediator true NIE = alpha_M * beta_M). Default 0.30. |
conf_str |
Confounding strength delta. Default 0.80. |
w_signal |
Proxy quality omega. Default 0.70. |
mo_confounding |
Strength of U1 -> M (mediator-outcome confounding). Default 0.80. |
phi |
Strength of the mediator instrument Gm -> M. 0 = no mediator instrument (five estimators, backward compatible). > 0 = generates Gm and includes the 2-stage MR estimator (IV2SLS2). Default 0. |
rho_G1 |
Correlation of G1 with conf_XM. Default 0. |
rho_G2 |
Correlation of G2 with conf_MY. Default 0. |
rho_pop |
Shared population structure. Default 0. |
lambda_XM |
Optional per-path confounder loading vector (X->M path). |
lambda_MY |
Optional per-path confounder loading vector (M->Y path). |
omega_1 |
Coverage of conf_XM by W1. NULL = use w_signal. |
omega_2 |
Coverage of conf_MY by W2. NULL = use w_signal. |
feat_cor |
Within-module correlation for block-diagonal co-expression modules in Y and W. 0 = independent features. Default 0. |
base_seed |
Starting seed; replicate i uses base_seed + i. Default 100. |
n_cores |
Number of parallel workers. Default 1. |
When are non-default (rho_G1, rho_G2, rho_pop, lambda_XM, lambda_MY, omega_1, omega_2), the two-stage proximal estimators (PGC2, PGC2Gm) are also included.
A list with summary (one row per method with NDE/NIE bias,
RMSE, Type I), raw (full per-feature results), true_NDE,
true_NIE, and params.
res <- run_mediation_sim(n_iter = 3, n_samples = 100, mo_confounding = 0.8) res$summaryres <- run_mediation_sim(n_iter = 3, n_samples = 100, mo_confounding = 0.8) res$summary
Sets alpha_M = 0 and beta_M = 0 (no true NIE) while
keeping beta_X active (NDE still present). Reports Type I
error for both NIE (should be 0 under the null) and NDE (should
reflect power for the direct effect).
run_null_mediation_sim( n_iter = 200, n_samples = 500, n_features = 20, beta_X = 0.1, conf_str = 0.8, w_signal = 0.7, mo_confounding = 0.8, phi = 0, rho_G1 = 0, rho_G2 = 0, rho_pop = 0, lambda_XM = NULL, lambda_MY = NULL, omega_1 = NULL, omega_2 = NULL, feat_cor = 0, base_seed = 300, n_cores = 1, alpha = 0.05 )run_null_mediation_sim( n_iter = 200, n_samples = 500, n_features = 20, beta_X = 0.1, conf_str = 0.8, w_signal = 0.7, mo_confounding = 0.8, phi = 0, rho_G1 = 0, rho_G2 = 0, rho_pop = 0, lambda_XM = NULL, lambda_MY = NULL, omega_1 = NULL, omega_2 = NULL, feat_cor = 0, base_seed = 300, n_cores = 1, alpha = 0.05 )
n_iter |
Number of replicates. Default 200. |
n_samples |
Observations per replicate. Default 500. |
n_features |
Features per replicate. Default 20. |
beta_X |
Direct effect (NDE, still active under null NIE). Default 0.10. |
conf_str |
Confounding strength delta. Default 0.80. |
w_signal |
Proxy quality omega. Default 0.70. |
mo_confounding |
Strength of U1 -> M. Default 0.80. |
phi |
Strength of the mediator instrument Gm -> M. 0 = no mediator instrument. Default 0. |
rho_G1 |
Correlation of G1 with conf_XM. Default 0. |
rho_G2 |
Correlation of G2 with conf_MY. Default 0. |
rho_pop |
Shared population structure. Default 0. |
lambda_XM |
Optional per-path confounder loading vector (X->M path). |
lambda_MY |
Optional per-path confounder loading vector (M->Y path). |
omega_1 |
Coverage of conf_XM by W1. NULL = use w_signal. |
omega_2 |
Coverage of conf_MY by W2. NULL = use w_signal. |
feat_cor |
Within-module correlation for block-diagonal co-expression modules in Y and W. 0 = independent features. Default 0. |
base_seed |
Seed offset. Default 300. |
n_cores |
Parallel workers. Default 1. |
alpha |
Significance threshold. Default 0.05. |
A list with rates (data frame: method, NIE_type1, NDE_type1)
and raw (full results).
null <- run_null_mediation_sim(n_iter = 2, n_samples = 100, n_features = 5, mo_confounding = 0.8, phi = 0.8) null$ratesnull <- run_null_mediation_sim(n_iter = 2, n_samples = 100, n_features = 5, mo_confounding = 0.8, phi = 0.8) null$rates
Run null simulations to estimate Type I error rates
run_null_sim( n_iter = 200, n_samples = 500, n_features = 20, conf_str = 0.8, w_signal = 0.7, feat_cor = 0, base_seed = 300, n_cores = 1, alpha = 0.05 )run_null_sim( n_iter = 200, n_samples = 500, n_features = 20, conf_str = 0.8, w_signal = 0.7, feat_cor = 0, base_seed = 300, n_cores = 1, alpha = 0.05 )
n_iter |
Number of replicates. Default 200. |
n_samples |
Observations per replicate. Default 500. |
n_features |
Features per replicate. Default 20. |
conf_str |
Confounding strength delta. Default 0.80. |
w_signal |
Proxy quality omega. Default 0.70. |
feat_cor |
Within-module feature correlation. Default 0. |
base_seed |
Seed offset. Default 300. |
n_cores |
Parallel workers. Default 1. |
alpha |
Significance threshold. Default 0.05. |
A list with rates (data frame) and raw (full results).
null <- run_null_sim(n_iter = 2, n_samples = 100, n_features = 5) null$ratesnull <- run_null_sim(n_iter = 2, n_samples = 100, n_features = 5) null$rates
Run repeated simulations for a single parameter configuration
run_simulation( n_iter = 100, n_samples = 500, n_features = 20, beta_X = 0.1, alpha_M = 0.5, beta_M = 0.3, conf_str = 0.8, w_signal = 0.7, feat_cor = 0, base_seed = 100, n_cores = 1 )run_simulation( n_iter = 100, n_samples = 500, n_features = 20, beta_X = 0.1, alpha_M = 0.5, beta_M = 0.3, conf_str = 0.8, w_signal = 0.7, feat_cor = 0, base_seed = 100, n_cores = 1 )
n_iter |
Number of simulation replicates. Default 100. |
n_samples |
Observations per replicate. Default 500. |
n_features |
Number of outcome and negative-control features. Default 20. |
beta_X |
Direct effect of X on Y. Default 0.10. |
alpha_M |
Effect of X on mediator. Default 0.50. |
beta_M |
Effect of mediator on Y. Default 0.30. |
conf_str |
Confounding strength delta. Default 0.80. |
w_signal |
Proxy quality omega. Default 0.70. |
feat_cor |
Within-module correlation for block-diagonal co-expression modules in Y and W. 0 = independent features. Default 0. |
base_seed |
Starting seed; replicate i uses base_seed + i. Default 100. |
n_cores |
Number of parallel workers. Default 1. |
A list with raw, summary, iter_bias, true_total, params.
res <- run_simulation(n_iter = 3, n_samples = 100, beta_X = 0.1, conf_str = 0.8) res$summaryres <- run_simulation(n_iter = 3, n_samples = 100, beta_X = 0.1, conf_str = 0.8) res$summary
Generate one synthetic dataset under the generalised SCM
run_single_iteration( trained_gan = NULL, n_synthetic_samples = 500, n_features = 20, n_confounders = 1, n_mediators = 1, beta_X = 0.1, alpha_M = 0.5, beta_M = 0.3, effect_size = NULL, conf_strength = 0.8, coverage = 1, captured = NULL, nc_model = "proxy", nc_params = list(), mo_confounding = 0, pleio = 0, phi = 0, gamma_G = 0.6, rho_G1 = 0, rho_G2 = 0, rho_pop = 0, lambda_XM = NULL, lambda_MY = NULL, omega_1 = NULL, omega_2 = NULL, feat_cor = 0, separate_U = NULL, u_strength = NULL, w_coverage_profile = NULL, MMExp = 1, MMOut = 1, MMCon = 1, MMCpG = 1, outcome_type = c("continuous", "survival", "binary"), surv_h0 = 0.1, surv_event_frac = 0.6, surv_censor_rate = NULL, bin_prev = 0.5, seed = NULL )run_single_iteration( trained_gan = NULL, n_synthetic_samples = 500, n_features = 20, n_confounders = 1, n_mediators = 1, beta_X = 0.1, alpha_M = 0.5, beta_M = 0.3, effect_size = NULL, conf_strength = 0.8, coverage = 1, captured = NULL, nc_model = "proxy", nc_params = list(), mo_confounding = 0, pleio = 0, phi = 0, gamma_G = 0.6, rho_G1 = 0, rho_G2 = 0, rho_pop = 0, lambda_XM = NULL, lambda_MY = NULL, omega_1 = NULL, omega_2 = NULL, feat_cor = 0, separate_U = NULL, u_strength = NULL, w_coverage_profile = NULL, MMExp = 1, MMOut = 1, MMCon = 1, MMCpG = 1, outcome_type = c("continuous", "survival", "binary"), surv_h0 = 0.1, surv_event_frac = 0.6, surv_censor_rate = NULL, bin_prev = 0.5, seed = NULL )
trained_gan |
Optional |
n_synthetic_samples |
Sample size. Default 500. |
n_features |
Number of outcome (and control) features. Default 20. |
n_confounders |
Number of latent confounders |
n_mediators |
Number of independent mediators. When > 1, each mediator has its own genetic instrument Gm and contributes additively to Y. Default 1 (single mediator, backward compatible). |
beta_X |
Direct effect of X on Y (true NDE). Default 0.10. |
alpha_M |
Effect of X on the mediator M. Default 0.50. |
beta_M |
Effect of M on Y. Default 0.30. |
effect_size |
Optional shortcut: if non- |
conf_strength |
Overall confounding strength (analogue of |
coverage |
How well the negative controls span the confounder
subspace, in |
captured |
Integer indices of the confounders the negative controls
see (passed to |
nc_model |
Negative-control model: a function |
nc_params |
Extra named parameters forwarded to |
mo_confounding |
Strength of U1 -> M (mediator-outcome confounding). 0 = no M-O confounding (original DGP). Default 0. When > 0, the first confounder U[,1] also affects M, creating the M-O confounding that the mediation estimators are benchmarked against. |
pleio |
Strength of a direct G -> Y path (horizontal pleiotropy), violating the exclusion restriction. 0 = no pleiotropy (valid instrument, original DGP). Default 0. When > 0, G affects Y directly in addition to through X, allowing benchmarking of IV/2SLS under instrument invalidity. |
phi |
Strength of the mediator instrument Gm -> M.
0 = no mediator instrument (original DGP, no Gm generated). Default 0.
When > 0, a valid instrument for M is generated (independent of U and G,
no direct path to Y), enabling point identification of NDE/NIE via
|
gamma_G |
Strength of the exposure instrument G -> X. Default 0.6. |
rho_G1 |
Correlation of G1 with conf_XM (instrument exogeneity violation). Default 0. |
rho_G2 |
Correlation of G2 with conf_MY (instrument exogeneity violation). Default 0. |
rho_pop |
Shared population structure inducing G1-G2 correlation Default 0. |
lambda_XM |
Optional length-k loading vector giving each confounder's weight on the X->M backdoor path. NULL (default) = shared loadings. |
lambda_MY |
Optional length-k loading vector giving each confounder's weight on the M->Y backdoor path. NULL (default) = shared loadings. |
omega_1 |
Coverage of conf_XM by W1. NULL = use |
omega_2 |
Coverage of conf_MY by W2. NULL = use |
feat_cor |
Within-module feature correlation. When > 0 and the GAN does not provide feature_correlations, a block-diagonal correlation matrix is used for the NC and outcome noise. GAN-learned correlations take precedence. Default 0. |
separate_U |
Defunct. Passing a value errors with a
message pointing to the replacement per-path loading vectors
|
u_strength |
Numeric vector: per-confounder strength profile (length k). Default NULL → rep(1, k) (equal strength, backward compatible). Recycled to length k and normalized so the total confounding budget is unchanged. |
w_coverage_profile |
A list with optional |
MMExp, MMOut, MMCon, MMCpG
|
Per-pathway confounding multipliers (exposure, outcome, controls, methylation). Default 1. |
outcome_type |
|
surv_h0 |
Baseline hazard for the survival DGP. Default 0.1. |
surv_event_frac |
Target fraction of observed events. Default 0.6. |
surv_censor_rate |
Explicit censoring rate. Default NULL. |
bin_prev |
Target prevalence (marginal event probability) for the binary DGP. Default 0.5. |
seed |
Optional RNG seed. |
A named list matching generate_toy_data() — X, G (n x
n_features), Y, W, U1, M, synthetic_data, true_total,
true_NDE, true_NIE — plus U (full n x k confounder matrix),
genetic_instrument, successful_features, failed_features, and
params. When phi > 0, also includes Gm (numeric vector, length n,
or n_mediators x n matrix when n_mediators > 1).
When any parameter is non-default, also includes G1, G2,
W1, W2, conf_XM, conf_MY, and (when rho_pop > 0) P.
When n_mediators > 1, M is an n_mediators x n matrix.
dat <- run_single_iteration(NULL, n_synthetic_samples = 100, n_features = 5, n_confounders = 1, seed = 1) analyze_methods_robust(dat)dat <- run_single_iteration(NULL, n_synthetic_samples = 100, n_features = 5, n_confounders = 1, seed = 1) analyze_methods_robust(dat)
Samples from the Gaussian copula: draws n_samples from
MVN(0, copula_cor), then transforms each feature through its inverse
marginal CDF. The result is centered and scaled to zero mean and unit
variance per feature, so the texture acts as noise that the structural
signal in run_single_iteration() provides the mean for.
sample_feature_texture(feature_texture, n_samples, n_features = NULL)sample_feature_texture(feature_texture, n_samples, n_features = NULL)
feature_texture |
An |
n_samples |
Number of synthetic samples to draw. |
n_features |
Target number of features. If NULL, uses the number of features in the training data. If larger, additional features are drawn by sampling existing columns with replacement and adding independent noise. If smaller, the first n_features are used. |
A n_features x n_samples matrix of synthetic texture
values, centered and scaled per feature.
M <- matrix(rnorm(30 * 200), 30, 200) # 30 transcripts, 200 samples ft <- train_feature_texture(M) draws <- sample_feature_texture(ft, 500, n_features = 20)M <- matrix(rnorm(30 * 200), 30, 200) # 30 transcripts, 200 samples ft <- train_feature_texture(M) draws <- sample_feature_texture(ft, 500, n_features = 20)
Binary columns (detected at training time) are rounded to after
de-normalisation, and one-hot dummy groups are made mutually exclusive
(the column with the highest pre-rounding value wins per row).
sample_texture(trained_gan, n)sample_texture(trained_gan, n)
trained_gan |
An |
n |
Number of rows to draw. |
A data frame of n rows with the trained columns, on the original
(de-normalised) scale. Binary columns contain only 0/1 values.
if (check_torch_setup()) { dat <- load_real_input_data(example = TRUE) gan <- train_gan_on_real_data(dat$gan_training_data, epochs = 5, verbose = FALSE) head(sample_texture(gan, 5)) }if (check_torch_setup()) { dat <- load_real_input_data(example = TRUE) gan <- train_gan_on_real_data(dat$gan_training_data, epochs = 5, verbose = FALSE) head(sample_texture(gan, 5)) }
Returns a reader-orienting summary of a simulation scenario: the ground-truth estimands (NDE, NIE, total effect), the modifiable parameters with their swept ranges, and the fixed parameters with their values. Intended to be rendered as a table preceding simulation results in the manuscript (, #582: "state the truth and the parameter ranges up front to orient the reader").
scenario_manifest( dat_or_params, conf_grid = NULL, coverage_grid = NULL, mo_confounding_grid = NULL, phi_grid = NULL, rho_G1_grid = NULL, rho_G2_grid = NULL, rho_pop_grid = NULL, omega_1_grid = NULL, omega_2_grid = NULL )scenario_manifest( dat_or_params, conf_grid = NULL, coverage_grid = NULL, mo_confounding_grid = NULL, phi_grid = NULL, rho_G1_grid = NULL, rho_G2_grid = NULL, rho_pop_grid = NULL, omega_1_grid = NULL, omega_2_grid = NULL )
dat_or_params |
A |
conf_grid |
Swept values of |
coverage_grid |
Swept values of NC coverage ( |
mo_confounding_grid |
Swept values of mediator-outcome confounding strength. Default NULL. |
phi_grid |
Swept values of the mediator-instrument strength
|
rho_G1_grid |
Swept values of the G1-conf_XM correlation. Default NULL. |
rho_G2_grid |
Swept values of the G2-conf_MY correlation. Default NULL. |
rho_pop_grid |
Swept values of the population-stratification correlation. Default NULL. |
omega_1_grid |
Swept values of |
omega_2_grid |
Swept values of |
dat_or_params may be either:
a list returned by generate_toy_data(), in which case the
estimands and fixed parameters are read from the object
(true_NDE, true_NIE, true_total, n, n_features,
n_mediators, lambda_XM, lambda_MY, feat_cor); or
a bare named list of DGP parameters
(beta_X, alpha_M, beta_M, n_mediators, n,
n_features, lambda_XM, lambda_MY, feat_cor, ...), in which case
the estimands are recomputed as
NDE = beta_X, NIE = n_mediators * alpha_M * beta_M,
total = NDE + NIE.
The *_grid arguments record the swept ranges for the modifiable
parameters. When a grid argument is omitted or NULL, the
corresponding modifiable parameter is reported with its scalar value
only (no range).
A named list with elements:
estimandsNamed numeric vector: NDE, NIE, total.
modifiable_parametersData frame with columns
parameter, value, swept_range. value is the scalar
used (or NA when only a grid was supplied);
swept_range is a comma-separated string of grid values,
or NA when the parameter was held fixed.
fixed_parametersData frame with columns parameter,
value.
dat <- generate_toy_data(n = 200, phi = 0.8, lambda_XM = c(1, 0), lambda_MY = c(0, 1), omega_1 = 0.7, omega_2 = 0.7, seed = 42) scenario_manifest(dat, conf_grid = c(0.3, 0.8), coverage_grid = c(0.3, 0.7, 1))dat <- generate_toy_data(n = 200, phi = 0.8, lambda_XM = c(1, 0), lambda_MY = c(0, 1), omega_1 = 0.7, omega_2 = 0.7, seed = 42) scenario_manifest(dat, conf_grid = c(0.3, 0.8), coverage_grid = c(0.3, 0.7, 1))
Applies a published set of per-variant weights (e.g. a PGS Catalog
scoring file, which is already LD-aware) directly to a genotype
dosage matrix. Matching is by chr:pos:ref:alt key (with automatic
allele-flip handling: when the genotype stores the opposite
orientation, the weight is negated) or by rsID when present.
score_pgs_panel(weights, genotypes, impute_mean = TRUE, scale_score = TRUE)score_pgs_panel(weights, genotypes, impute_mean = TRUE, scale_score = TRUE)
weights |
A data.frame of variant weights. Recognized columns:
|
genotypes |
A numeric dosage matrix (variants x samples, dosages
in 0-2) whose rownames are either |
impute_mean |
Logical: mean-impute missing dosages per variant.
Default |
scale_score |
Logical: scale the resulting score to mean 0 /
sd 1. Default |
A list with:
named numeric vector of per-sample scores.
data.frame of matched variants with the weights used (after any flip correction).
number of weight-panel variants.
# Toy dosage matrix: 4 variants x 50 samples dos <- matrix(rbinom(4 * 50, 2, 0.3), nrow = 4, dimnames = list(c("1:100:A:G", "1:200:C:T", "1:300:G:A", "2:400:T:C"), paste0("S", 1:50))) wts <- data.frame(chr = c(1, 1, 2), pos = c(100, 300, 400), a1 = c("A", "G", "T"), a0 = c("G", "A", "C"), weight = c(0.1, -0.2, 0.05)) out <- score_pgs_panel(wts, dos) plot(out$score)# Toy dosage matrix: 4 variants x 50 samples dos <- matrix(rbinom(4 * 50, 2, 0.3), nrow = 4, dimnames = list(c("1:100:A:G", "1:200:C:T", "1:300:G:A", "2:400:T:C"), paste0("S", 1:50))) wts <- data.frame(chr = c(1, 1, 2), pos = c(100, 300, 400), a1 = c("A", "G", "T"), a0 = c("G", "A", "C"), weight = c(0.1, -0.2, 0.05)) out <- score_pgs_panel(wts, dos) plot(out$score)
Draws one standardised genetic instrument for the exposure, as an additive
score over n_snps biallelic SNPs (dosages Binomial(2, maf)). Mirrors the
single-instrument (PRS) design used throughout the SCENIC framework.
simulate_single_genetic_instrument(n, n_snps = 20, maf = 0.3, seed = NULL)simulate_single_genetic_instrument(n, n_snps = 20, maf = 0.3, seed = NULL)
n |
Number of samples. |
n_snps |
SNPs contributing to the score. Default 20. |
maf |
Minor-allele frequency. Default 0.3. |
seed |
Optional RNG seed. |
List with G (standardised instrument, length n), dosages
(n x n_snps), and maf.
g <- simulate_single_genetic_instrument(100, n_snps = 10, seed = 1) length(g$G)g <- simulate_single_genetic_instrument(100, n_snps = 10, seed = 1) length(g$G)
Prints the full diagnosis summary (same as print()).
## S3 method for class 'iconic_diagnosis' summary(object, ...)## S3 method for class 'iconic_diagnosis' summary(object, ...)
object |
An |
... |
Unused. |
Invisibly returns object.
set.seed(1) data <- iconic_data(X = rnorm(100), Y = matrix(rnorm(100 * 10), 10, 100), G = rnorm(100), W = matrix(rnorm(100 * 10), 10, 100)) diag <- iconic_diagnose(data) summary(diag)set.seed(1) data <- iconic_data(X = rnorm(100), Y = matrix(rnorm(100 * 10), 10, 100), G = rnorm(100), W = matrix(rnorm(100 * 10), 10, 100)) diag <- iconic_diagnose(data) summary(diag)
Prints the full prospective summary (same as print()).
## S3 method for class 'iconic_prospect' summary(object, ...)## S3 method for class 'iconic_prospect' summary(object, ...)
object |
An |
... |
Unused. |
Invisibly returns object.
if (check_torch_setup()) { data <- iconic_data(X = rnorm(100), Y = matrix(rnorm(100 * 10), 10, 100), M = rnorm(100)) result <- iconic_prospect(data, n_iter = 2, gan_epochs = 5, gamma_G_grid = c(0.4, 0.8), run_rho_sweep = FALSE) summary(result) }if (check_torch_setup()) { data <- iconic_data(X = rnorm(100), Y = matrix(rnorm(100 * 10), 10, 100), M = rnorm(100)) result <- iconic_prospect(data, n_iter = 2, gan_epochs = 5, gamma_G_grid = c(0.4, 0.8), run_rho_sweep = FALSE) summary(result) }
Prints the full recommendation summary (same as print()).
## S3 method for class 'iconic_recommendation' summary(object, ...)## S3 method for class 'iconic_recommendation' summary(object, ...)
object |
An |
... |
Unused. |
Invisibly returns object.
data <- iconic_data(X = rnorm(100), Y = matrix(rnorm(100 * 10), 10, 100), G = rnorm(100), W = matrix(rnorm(100 * 10), 10, 100)) diag <- iconic_diagnose(data) est <- iconic_estimate(data, diagnosis = diag) rec <- iconic_recommend(data, diagnosis = diag, estimate = est, auto_sensitivity = FALSE) summary(rec)data <- iconic_data(X = rnorm(100), Y = matrix(rnorm(100 * 10), 10, 100), G = rnorm(100), W = matrix(rnorm(100 * 10), 10, 100)) diag <- iconic_diagnose(data) est <- iconic_estimate(data, diagnosis = diag) rec <- iconic_recommend(data, diagnosis = diag, estimate = est, auto_sensitivity = FALSE) summary(rec)
Prints the full sensitivity summary (same as print()).
## S3 method for class 'iconic_sensitivity' summary(object, ...)## S3 method for class 'iconic_sensitivity' summary(object, ...)
object |
An |
... |
Unused. |
Invisibly returns object.
if (check_torch_setup()) { data <- iconic_data(X = rnorm(100), Y = matrix(rnorm(100 * 10), 10, 100), M = rnorm(100), G = rnorm(100), Gm = rnorm(100), W = matrix(rnorm(100 * 10), 10, 100)) sens <- iconic_sensitivity(data, n_iter = 2, gan_epochs = 5, rho_G1_grid = c(0, 0.2), rho_G2_grid = c(0, 0.2)) summary(sens) }if (check_torch_setup()) { data <- iconic_data(X = rnorm(100), Y = matrix(rnorm(100 * 10), 10, 100), M = rnorm(100), G = rnorm(100), Gm = rnorm(100), W = matrix(rnorm(100 * 10), 10, 100)) sens <- iconic_sensitivity(data, n_iter = 2, gan_epochs = 5, rho_G1_grid = c(0, 0.2), rho_G2_grid = c(0, 0.2)) summary(sens) }
Runs a custom DGP that varies the G->X coefficient (pi_GX) and
records IV2SLS performance binned by first-stage partial F.
Uses fit_iv2sls() with min_f = 0 (no weak-IV guard)
so bias is visible across all instrument strengths.
sweep_instrument_strength( pi_GX_grid = c(0.02, 0.05, 0.1, 0.15, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8), n_iter = 50, n = 200, k = 1, conf_str = 0.8, tau = 0.25, coverage = 0.7, n_cores = 1 )sweep_instrument_strength( pi_GX_grid = c(0.02, 0.05, 0.1, 0.15, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8), n_iter = 50, n = 200, k = 1, conf_str = 0.8, tau = 0.25, coverage = 0.7, n_cores = 1 )
pi_GX_grid |
Numeric vector of G->X coefficients to sweep. |
n_iter |
Number of replications per grid point. |
n |
Sample size. |
k |
Number of confounders. |
conf_str |
Confounding strength. |
tau |
True total effect. |
coverage |
Negative-control coverage. |
n_cores |
Number of parallel workers for the replicate loops.
Default 1 (sequential). Uses |
A data frame with columns pi_GX, arm,
iter, partial_F, beta, se,
pvalue, rejected.
res <- sweep_instrument_strength(pi_GX_grid = c(0.05, 0.2), n_iter = 2) head(res)res <- sweep_instrument_strength(pi_GX_grid = c(0.05, 0.2), n_iter = 2) head(res)
Sweep mediation Type I error across confounding strength levels
sweep_mediation_null_by_conf( conf_grid = c(0.2, 0.4, 0.6, 0.8, 1), n_iter = 100, n_samples = 500, n_features = 20, w_signal = 0.7, mo_confounding = 0.8, phi = 0, rho_G1 = 0, rho_G2 = 0, rho_pop = 0, lambda_XM = NULL, lambda_MY = NULL, omega_1 = NULL, omega_2 = NULL, feat_cor = 0, base_seed = 900, n_cores = 1, alpha = 0.05 )sweep_mediation_null_by_conf( conf_grid = c(0.2, 0.4, 0.6, 0.8, 1), n_iter = 100, n_samples = 500, n_features = 20, w_signal = 0.7, mo_confounding = 0.8, phi = 0, rho_G1 = 0, rho_G2 = 0, rho_pop = 0, lambda_XM = NULL, lambda_MY = NULL, omega_1 = NULL, omega_2 = NULL, feat_cor = 0, base_seed = 900, n_cores = 1, alpha = 0.05 )
conf_grid |
Numeric vector of confounding strength values. Default c(0.2, 0.4, 0.6, 0.8, 1.0). |
n_iter |
Replicates per conf_str value. Default 100. |
n_samples |
Observations per replicate. Default 500. |
n_features |
Features per replicate. Default 20. |
w_signal |
Proxy quality omega. Default 0.70. |
mo_confounding |
Strength of U1 -> M. Default 0.80. |
phi |
Strength of the mediator instrument Gm -> M. 0 = no mediator instrument. Default 0. |
rho_G1 |
Correlation of G1 with conf_XM. Default 0. |
rho_G2 |
Correlation of G2 with conf_MY. Default 0. |
rho_pop |
Shared population structure. Default 0. |
lambda_XM |
Optional per-path confounder loading vector (X->M path). |
lambda_MY |
Optional per-path confounder loading vector (M->Y path). |
omega_1 |
Coverage of conf_XM by W1. NULL = use w_signal. |
omega_2 |
Coverage of conf_MY by W2. NULL = use w_signal. |
feat_cor |
Within-module feature correlation. Default 0. |
base_seed |
Seed offset. Default 900. |
n_cores |
Parallel workers. Default 1. |
alpha |
Significance threshold. Default 0.05. |
A data frame with columns: conf_str, method, NIE_type1, NDE_type1.
t1e <- sweep_mediation_null_by_conf(c(0.2, 0.8), n_iter = 3, n_samples = 100) head(t1e)t1e <- sweep_mediation_null_by_conf(c(0.2, 0.8), n_iter = 3, n_samples = 100) head(t1e)
Sweep a single mediation simulation parameter across a grid
sweep_mediation_param( param, param_grid, n_iter = 100, n_samples = 500, n_features = 20, n_mediators = 1, beta_X = 0.1, alpha_M = 0.5, beta_M = 0.3, conf_str = 0.8, w_signal = 0.7, mo_confounding = 0.8, phi = 0, rho_G1 = 0, rho_G2 = 0, rho_pop = 0, lambda_XM = NULL, lambda_MY = NULL, omega_1 = NULL, omega_2 = NULL, feat_cor = 0, u_strength = NULL, w_coverage_profile = NULL, base_seed = 0, n_cores = 1 )sweep_mediation_param( param, param_grid, n_iter = 100, n_samples = 500, n_features = 20, n_mediators = 1, beta_X = 0.1, alpha_M = 0.5, beta_M = 0.3, conf_str = 0.8, w_signal = 0.7, mo_confounding = 0.8, phi = 0, rho_G1 = 0, rho_G2 = 0, rho_pop = 0, lambda_XM = NULL, lambda_MY = NULL, omega_1 = NULL, omega_2 = NULL, feat_cor = 0, u_strength = NULL, w_coverage_profile = NULL, base_seed = 0, n_cores = 1 )
param |
Parameter to vary: one of "beta_X", "conf_str", "w_signal", "alpha_M", "beta_M", "n_samples", "mo_confounding", "phi", "rho_G1", "rho_G2", "rho_pop", "omega_1", "omega_2", "feat_cor". |
param_grid |
Numeric vector of values to sweep. |
n_iter |
Replicates per grid point. Default 100. |
n_samples |
Observations per replicate. Default 500. |
n_features |
Features per replicate. Default 20. |
n_mediators |
Number of independent mediators. Default 1. |
beta_X |
Baseline direct effect. Default 0.10. |
alpha_M |
Baseline mediator path. Default 0.50. |
beta_M |
Baseline mediator effect. Default 0.30. |
conf_str |
Baseline confounding strength. Default 0.80. |
w_signal |
Baseline proxy quality. Default 0.70. |
mo_confounding |
Baseline M-O confounding. Default 0.80. |
phi |
Baseline mediator-instrument strength. 0 = no mediator instrument. Default 0. |
rho_G1 |
Baseline G1-conf_XM correlation. Default 0. |
rho_G2 |
Baseline G2-conf_MY correlation. Default 0. |
rho_pop |
Baseline population structure. Default 0. |
lambda_XM |
Optional per-path confounder loading vector (X->M path). |
lambda_MY |
Optional per-path confounder loading vector (M->Y path). |
omega_1 |
Baseline W1 coverage. NULL = use w_signal. |
omega_2 |
Baseline W2 coverage. NULL = use w_signal. |
feat_cor |
Baseline within-module feature correlation. Default 0. |
u_strength |
Numeric vector: per-confounder
strength scaling. |
w_coverage_profile |
A list with |
base_seed |
Seed offset. Default 0. |
n_cores |
Parallel workers. Default 1. |
A list with summary (data frame) and iter_bias
(per-iteration NDE/NIE bias).
res <- sweep_mediation_param("conf_str", c(0.2, 0.8), n_iter = 3, n_samples = 100) res$summaryres <- sweep_mediation_param("conf_str", c(0.2, 0.8), n_iter = 3, n_samples = 100) res$summary
Runs simulation sweeps for the empirical NC validity diagnostics
and returns data frames ready for plotting with
plot_nc_validity_diagnostics(). Panels: (A) A1 W perp X | C,
(B) A2 W perp G | C, (C) A2' W perp Gm | C, (D) A3
covariance-capture versus true coverage omega, (E) A3 support
R2(Utilde | W) versus omega.
sweep_nc_validity( n_samples = 500, n_iter = 50, phi_val = 0.8, contam_grid = c(0, 0.05, 0.1, 0.15, 0.2, 0.3, 0.5), meqtl_grid = c(0, 0.05, 0.1, 0.15, 0.2, 0.3, 0.5), eqtl_grid = c(0, 0.05, 0.1, 0.15, 0.2, 0.3, 0.5), omega_grid = c(0, 0.1, 0.2, 0.3, 0.5, 0.7, 0.9, 1), n_perm = 200, cs_confounders = 2, include_a3_grid = FALSE, k_grid = c(1, 2, 3), n_valid_grid = c(1, 2, 3), n_cores = 1 )sweep_nc_validity( n_samples = 500, n_iter = 50, phi_val = 0.8, contam_grid = c(0, 0.05, 0.1, 0.15, 0.2, 0.3, 0.5), meqtl_grid = c(0, 0.05, 0.1, 0.15, 0.2, 0.3, 0.5), eqtl_grid = c(0, 0.05, 0.1, 0.15, 0.2, 0.3, 0.5), omega_grid = c(0, 0.1, 0.2, 0.3, 0.5, 0.7, 0.9, 1), n_perm = 200, cs_confounders = 2, include_a3_grid = FALSE, k_grid = c(1, 2, 3), n_valid_grid = c(1, 2, 3), n_cores = 1 )
n_samples |
Number of synthetic samples per iteration. |
n_iter |
Number of replications per sweep point. |
phi_val |
Mediator-instrument strength (for the A2' panel and the capture/support sweeps). |
contam_grid |
X->W contamination strength grid (A1). |
meqtl_grid |
G->W (meQTL) strength grid (A2). |
eqtl_grid |
Gm->W (eQTL) strength grid (A2'). |
omega_grid |
True negative-control coverage grid for the capture (D) and support (E) sweeps. Includes 0 by default so the permutation-null calibration is visible. |
n_perm |
Permutations per replicate for the capture test.
Default 200, matching |
cs_confounders |
Number of latent confounders for the capture/support sweeps (distinct loadings). Default 2. |
include_a3_grid |
Logical; also run the legacy A3 dimensional grid (PGC bias over n_valid x k). Default FALSE (no longer plotted). |
k_grid, n_valid_grid
|
Grids for the legacy A3 dimensional sweep. |
n_cores |
Number of parallel workers for the replicate loops.
Default 1 (sequential). Uses |
A list with elements panel_a, panel_b,
panel_d, panel_capture_support (per-replicate raw),
and optionally panel_c when include_a3_grid = TRUE.
panels <- sweep_nc_validity(n_samples = 100, n_iter = 2, contam_grid = c(0, 0.1), meqtl_grid = c(0, 0.1), eqtl_grid = c(0, 0.1), omega_grid = c(0, 0.5), n_perm = 20) panels$panel_a panels$panel_capture_supportpanels <- sweep_nc_validity(n_samples = 100, n_iter = 2, contam_grid = c(0, 0.1), meqtl_grid = c(0, 0.1), eqtl_grid = c(0, 0.1), omega_grid = c(0, 0.5), n_perm = 20) panels$panel_a panels$panel_capture_support
Sweep Type I error rate across confounding strength levels
sweep_null_by_conf( conf_grid = c(0.2, 0.4, 0.6, 0.8, 1), n_iter = 100, n_samples = 500, n_features = 20, w_signal = 0.7, feat_cor = 0, base_seed = 900, n_cores = 1, alpha = 0.05 )sweep_null_by_conf( conf_grid = c(0.2, 0.4, 0.6, 0.8, 1), n_iter = 100, n_samples = 500, n_features = 20, w_signal = 0.7, feat_cor = 0, base_seed = 900, n_cores = 1, alpha = 0.05 )
conf_grid |
Numeric vector of confounding strength values. Default c(0.2, 0.4, 0.6, 0.8, 1.0). |
n_iter |
Replicates per conf_str value. Default 100. |
n_samples |
Observations per replicate. Default 500. |
n_features |
Features per replicate. Default 20. |
w_signal |
Proxy quality omega. Default 0.70. |
feat_cor |
Within-module feature correlation. Default 0. |
base_seed |
Seed offset. Default 900. |
n_cores |
Parallel workers. Default 1. |
alpha |
Significance threshold. Default 0.05. |
A data frame with columns: conf_str, method, type1_error.
t1e <- sweep_null_by_conf(c(0.2, 0.8), n_iter = 3, n_samples = 100) plot_type1_vs_conf(t1e)t1e <- sweep_null_by_conf(c(0.2, 0.8), n_iter = 3, n_samples = 100) plot_type1_vs_conf(t1e)
Sweep a single simulation parameter across a grid
sweep_param( param, param_grid, n_iter = 100, n_samples = 500, n_features = 20, beta_X = 0.1, alpha_M = 0.5, beta_M = 0.3, conf_str = 0.8, w_signal = 0.7, feat_cor = 0, base_seed = 0, n_cores = 1 )sweep_param( param, param_grid, n_iter = 100, n_samples = 500, n_features = 20, beta_X = 0.1, alpha_M = 0.5, beta_M = 0.3, conf_str = 0.8, w_signal = 0.7, feat_cor = 0, base_seed = 0, n_cores = 1 )
param |
Parameter to vary: one of "beta_X", "conf_str", "w_signal", "alpha_M", "beta_M", "n_samples", "feat_cor". |
param_grid |
Numeric vector of values to sweep. |
n_iter |
Replicates per grid point. Default 100. |
n_samples |
Observations per replicate. Default 500. |
n_features |
Features per replicate. Default 20. |
beta_X |
Baseline direct effect. Default 0.10. |
alpha_M |
Baseline mediator path. Default 0.50. |
beta_M |
Baseline mediator effect. Default 0.30. |
conf_str |
Baseline confounding strength. Default 0.80. |
w_signal |
Baseline proxy quality. Default 0.70. |
feat_cor |
Baseline within-module feature correlation. Default 0. |
base_seed |
Seed offset. Default 0. |
n_cores |
Parallel workers. Default 1. |
A list with summary (data frame) and iter_bias (data frame).
res <- sweep_param("conf_str", c(0.2, 0.8), n_iter = 3, n_samples = 100) res$summaryres <- sweep_param("conf_str", c(0.2, 0.8), n_iter = 3, n_samples = 100) res$summary
Learns the joint distribution of a features x samples mediator matrix using a Gaussian copula with flexible marginal distributions. For each feature, the marginal is fitted as either an empirical CDF (default) or the best parametric family (normal, log-normal, gamma, beta) selected by AIC with a Kolmogorov-Smirnov goodness-of-fit check. Cross-feature dependence is captured by the Gaussian copula correlation matrix.
train_feature_texture(M_matrix, marginal_method = "auto")train_feature_texture(M_matrix, marginal_method = "auto")
M_matrix |
Mediator panel, |
marginal_method |
Marginal fitting method: |
The resulting model can be sampled via sample_feature_texture() to
produce realistic synthetic feature vectors that preserve the marginal
shapes and correlation structure of the user's mediator panel.
An iconic_feature_texture S3 object: a named list with
marginals (list of per-feature marginal specs), copula_cor
(p x p correlation matrix), n_features, n_samples,
marginal_method, and marginal_types (summary of how many
features used each method).
M <- matrix(rnorm(30 * 200), 30, 200) # 30 transcripts, 200 samples ft <- train_feature_texture(M) draws <- sample_feature_texture(ft, 100) # 30 x 100 synthetic drawsM <- matrix(rnorm(30 * 200), 30, 200) # 30 transcripts, 200 samples ft <- train_feature_texture(M) draws <- sample_feature_texture(ft, 100) # 30 x 100 synthetic draws
Fits an adversarial network (torch GAN) to the tidy training frame. Requires the torch package; if torch is not available, an error is raised with installation instructions.
train_gan_on_real_data( real_data, feature_correlations = NULL, feature_texture = NULL, epochs = 300, batch_size = 32, lr = 2e-04, seed = NULL, verbose = TRUE )train_gan_on_real_data( real_data, feature_correlations = NULL, feature_texture = NULL, epochs = 300, batch_size = 32, lr = 2e-04, seed = NULL, verbose = TRUE )
real_data |
Data frame of numeric variables (e.g. the
|
feature_correlations |
Optional list with elements |
feature_texture |
Optional |
epochs |
GAN training epochs. Default 300. |
batch_size |
Mini-batch size. Default 32. |
lr |
Adam learning rate. Default 2e-4. |
seed |
Optional RNG seed (sets both R and torch seeds). |
verbose |
Print progress. Default TRUE. |
Binary columns (values in , e.g. encoded sex or one-hot ethnicity
dummies) are detected automatically and stored in the returned object.
sample_texture() rounds them back to 0/1 and enforces one-hot mutual
exclusivity, so synthetic draws respect the discrete structure.
Feature-level residual correlation matrices (for the Y and W panels)
can be attached via the feature_correlations argument. When present,
run_single_iteration() uses them to inject correlated noise into the
simulated outcome and negative-control panels.
A feature-level copula texture model for the mediator (M) panel can be
attached via the feature_texture argument (an
iconic_feature_texture object from train_feature_texture()). When
present, run_single_iteration() uses it to draw realistic mediator
feature vectors that preserve the marginal distributions and
cross-feature correlation structure of the user's mediator panel.
An iconic_gan object: a list with model_type ("gan"),
columns, norm (per-column centre/scale), binary_cols (names of
0/1 columns), onehot_groups (list of one-hot dummy groups),
feature_correlations (list or NULL), feature_texture (object or
NULL), training statistics, and the trained networks + loss history.
if (check_torch_setup()) { dat <- load_real_input_data(example = TRUE) gan <- train_gan_on_real_data(dat$gan_training_data, feature_correlations = dat$feature_correlations, feature_texture = dat$feature_texture, epochs = 5, verbose = FALSE) head(sample_texture(gan, 5)) }if (check_torch_setup()) { dat <- load_real_input_data(example = TRUE) gan <- train_gan_on_real_data(dat$gan_training_data, feature_correlations = dat$feature_correlations, feature_texture = dat$feature_texture, epochs = 5, verbose = FALSE) head(sample_texture(gan, 5)) }