R/getZ_marg_score.R
fit_null_model.RdPre-computes the null model (without genetic effects) for efficient repeated
calls to getZ_marg_score() or getZ_marg_score_binary_SPA() with
different SNP sets but the same phenotype and covariates. This optimization is
critical for genome-wide analysis where the same null model is used across
thousands of gene/region tests.
fit_null_model(X, Y, trait = "binary", sample_id = NULL)A numeric matrix of covariates (n x k), where k is the number of covariates. If the first column is not all 1s (intercept), it will be added automatically.
A numeric vector of response variable (length n). For binary traits, must be coded as 0/1. For continuous traits, any numeric values are allowed.
Character string indicating trait type. Must be either "binary" (logistic regression) or "continuous" (linear regression). Default is "binary".
Optional character vector of sample IDs (length n). Stored in the
null model for downstream sample alignment in marginal_scan().
Default is NULL.
A null_model object (list) with class "glow_null_model" containing:
Character, "binary" or "continuous"
Integer, sample size (number of observations)
Numeric matrix, covariate matrix with intercept included
(Binary only) Numeric vector of fitted probabilities P(Y=1|X)
Numeric vector, response (needed for SPA and use_lm_t)
(Continuous only) Numeric vector of residuals
(Continuous only) Numeric scalar, residual standard deviation
Numeric matrix (n x k+1), precomputed projection matrix for efficient marginal scan. For binary: X_tilde (X_tilde' X_tilde)^-1/2; for continuous: X (X' X)^-1/2.
(Binary only) Numeric vector of length n, sqrt(mu_0 * (1 - mu_0))
Character vector of sample IDs (if provided)
In genome-wide association studies, researchers typically test many SNP sets (genes, pathways, regions) against the same phenotype and covariates. The standard workflow repeatedly fits the null model (expensive GLM fitting), even though it's identical across all tests.
This function enables a two-step optimized workflow:
Fit null model once: null_obj <- fit_null_model(X, Y, trait)
Reuse for all SNP sets: getZ_marg_score(G_i, null_model = null_obj)
For Binary Traits:
Fits logistic regression \(\text{logit}(P(Y=1)) = X\beta\) and stores:
Fitted probabilities \(Y_0 = P(Y=1|X)\) (for weighting)
Covariate matrix with intercept (for projection)
Response vector (needed for SPA calculations)
Sample size (for validation)
For Continuous Traits:
Fits linear regression \(Y = X\beta + \epsilon\) and stores:
Residuals \(r = Y - X\hat{\beta}\)
Residual standard deviation \(s_0 = \text{SD}(r)\)
Covariate matrix with intercept (for projection)
Sample size (for validation)
Performance Impact:
For testing 1000 genes with 500 samples:
Standard mode: ~50 seconds (fit null model 1000 times)
Optimized mode: ~5 seconds (fit null model once, reuse 1000 times)
Speedup: 10x faster
Speedup increases with more tests and larger samples.
Zhang, H., Liu, M., Landers, J. E., and Wu, Z. (2026). Integrated weighted association test with application to genetic association studies. The Annals of Applied Statistics, 20(3), 2074-2097. doi:10.1214/26-AOAS2223
getZ_marg_score for standard score calculation
getZ_marg_score_binary_SPA for SPA-adjusted score calculation
# Example 1: Binary trait - standard vs. optimized workflow
set.seed(123)
n <- 500
X <- matrix(rnorm(n * 2), n, 2)
Y <- sample(c(0, 1), n, replace = TRUE)
# Create 10 different SNP sets
gene_sets <- lapply(1:10, function(i) {
matrix(rbinom(n * 5, 2, 0.1), n, 5)
})
# Standard workflow (slow - fits null model 10 times)
if (FALSE) { # \dontrun{
results_slow <- lapply(gene_sets, function(G) {
getZ_marg_score(G, X, Y, trait = "binary")
})
} # }
# Optimized workflow (fast - fits null model once)
null_obj <- fit_null_model(X, Y, trait = "binary")
results_fast <- lapply(gene_sets, function(G) {
getZ_marg_score(G, null_model = null_obj)
})
# Example 2: Continuous trait
Y_cont <- rnorm(n)
null_obj_cont <- fit_null_model(X, Y_cont, trait = "continuous")
result <- getZ_marg_score(gene_sets[[1]], null_model = null_obj_cont)
# Example 3: Using with SPA-adjusted method
null_obj_spa <- fit_null_model(X, Y, trait = "binary")
result_spa <- getZ_marg_score_binary_SPA(gene_sets[[1]], null_model = null_obj_spa)