Skip to contents

Overview

TWAS weights are per-variant coefficients that predict a molecular phenotype (gene expression, splicing, protein abundance) from genotype. Once learned, the weights are combined with GWAS summary statistics to compute per-gene TWAS Z-scores (covered in the TWAS Z-score + causal inference vignette).

twasWeightsPipeline() is a single that accepts multiple input types. The output is always a TwasWeights collection (a GRangesList subclass keyed by (study, context, trait, method), one element per tuple).

Input class Methods supported
QtlDataset Univariate: LASSO, elastic net, ridge, mr.ash, MCP, SCAD, L0Learn, Bayesian alphabet, DPR. Multivariate: mr.mash, mvsusie
MultiStudyQtlDataset Iterates over embedded studies; same methods as QtlDataset
QtlSumStats RSS-only: LASSO, elastic net, ridge, mr.ash, MCP, SCAD, L0Learn, Bayesian alphabet, DPR, PRS-CS, LDpred2, P+T. Multivariate: mr.mash, mvsusie

Bundled inputs

data(qtlDatasetExample, qtlSumStatsExample, multiStudyQtlDatasetExample)

Individual-level weights

The simplest individual-level call learns a single regression-based weight set from a QtlDataset. Cross-validation is off by default (CrossValidationParam(folds = 0)). Like fineMappingPipeline(), you need to tell it which genotype window to extract — cisWindow (per trait) is the most common choice.

Fine-mapping methods (susie, susieInf, susieAsh, mvsusie, fsusie) are not re-fit by twasWeightsPipeline() - instead run fine-mapping models with fineMappingPipeline() first and pass the result via fineMappingResult = … (see the next section).

tw <- twasWeightsPipeline(qtlDatasetExample, methods = "lasso", cisWindow = 1e6)
tw
## TwasWeights: 1 genes, 1 entries
##   1 studies, 1 contexts, 1 traits, 1 methods
##   LD reference: NULL (individual-level fit)

Extract the per-variant weights for one tuple:

entry <- twasWeights(
    tw,
    studyName = "study1",
    context = "brain",
    trait = "ENSG_example",
    method = "lasso"
)
head(weights(entry))
## chr22:14850625:T:G chr22:14870204:T:C chr22:14878387:G:A chr22:14880040:A:G 
##                  0                  0                  0                  0 
## chr22:14884399:T:C chr22:15063831:A:G 
##                  0                  0

CV performance:

cvResult(entry)
## NULL

The entry’s variantIds slot lists the LD-aligned variant set the weights are defined on:

## [1] 198

Including a fine-mapping susie fit in the TWAS ensemble

The posterior effect estimates of SuSiE fine-mapping models are themselves a set of per-variant weight estimates. When you pass an existing QtlFineMappingResult to twasWeightsPipeline(), the trimmed susie fit for each matching (study, context, trait) tuple is added to the ensemble as an additional weights row instead of being re-fit.

The same holds under cross-validation: each fold needs a fit trained on that fold’s rows, and the weights pipeline will not produce one. Run the fine-mapping with crossValidationParam so those per-fold fits travel with the result — requesting a fine-mapping method under cross-validation without them is an error. That fold partition then governs the whole run: every other weight method is cross-validated on the same folds, which is what makes their scores comparable. The fold samples must be the samples of the dataset being scored, or the run stops rather than scoring a fit against rows it was never separated from.

fmr <- fineMappingPipeline(
    qtlDatasetExample,
    methods = "susie",
    cisWindow = 1e6,
    crossValidationParam = CrossValidationParam(folds = 3)
)
tw2 <- twasWeightsPipeline(
    qtlDatasetExample,
    methods = c("susie", "lasso"),
    cisWindow = 1e6,
    fineMappingResult = fmr
)

Multi-study weights

msTw <- twasWeightsPipeline(
    multiStudyQtlDatasetExample,
    methods = "lasso",
    cisWindow = 1e6
)
table(msTw$study)
## 
## study1 study2 
##      1      1

The call for iterating over the studies of a MultiStudyQtlDataset is identical to the call for a single QtlDataset.

Multiple weight methods at once

Pass a character vector to fit several methods on the same fold split and return them as the methods of each gene’s row in the collection. Fine-mapping methods (susie, susieInf, susieAsh, mvsusie) can only be included if their results are present in the object passed to the fineMappingResult argument:

# TWAS-regression only — no fineMappingResult needed.
twasWeightsPipeline(
    qtlDatasetExample,
    methods = c("lasso", "enet", "ridge", "mrash")
)

# Add a fine-mapping susie row by providing the QtlFineMappingResult.
fmr <- fineMappingPipeline(
    qtlDatasetExample,
    methods = "susie",
    cisWindow = 1e6
)
twasWeightsPipeline(
    qtlDatasetExample,
    methods = c("susie", "lasso", "enet", "ridge", "mrash"),
    cisWindow = 1e6,
    fineMappingResult = fmr
)

RSS / sumstats weights

twSs <- twasWeightsPipeline(qtlSumStatsExample, methods = "lasso")
twSs
## TwasWeights: 1 genes, 1 entries
##   1 studies, 1 contexts, 1 traits, 1 methods
##   LD reference: plink1 @ pecotmr://extdata/toy_canonical

The RSS path computes LD as needed from each row’s LD reference in the QtlSumStats collection. The same rule holds for fine-mapping methods here: they are not re-fit, so run fineMappingPipeline() on the QtlSumStats first and pass its result as fineMappingResult.

Multivariate (multi-context) weights

When you have multiple contexts for the same trait (e.g. multi-tissue expression), pass methods = "mrmash" to do a joint multivariate fit:

twasWeightsPipeline(
    qtlDatasetExample,
    methods = "mrmash",
    contexts = c("brain", "liver")
)

To choose the axes yourself, pass a jointSpecification: a character vector of axes, or list(axes = , scope = ) to limit the fit to listed studies, contexts or traits. The jointSpecification argument in ?twasWeightsPipeline gives the full grammar. On a QtlSumStats the contexts are fitted against one LD matrix, so their rows must share an LD reference.

# `qd`: a QtlDataset whose traits are measured in brain, liver and more.
twasWeightsPipeline(
    qd,
    methods = "mrmash",
    jointSpecification = list(list(
        axes = "context",
        scope = list(context = c("brain", "liver"))
    ))
)

Calling a weight method directly

Each methods = value dispatches to an exported function, and those functions are usable on their own. Reach for one when you want to fit a single model to a matrix you already have — tuning a penalty, comparing methods outside the pipeline, or debugging a method that behaves oddly on your data.

The individual-level methods take X (genotypes) and y (phenotype) and return a weight vector:

set.seed(1)
n <- 200
p <- 30
X <- matrix(rnorm(n * p), n, p, dimnames = list(NULL, paste0("v", 1:p)))
y <- as.vector(X %*% c(rep(0.4, 3), rep(0, p - 3)) + rnorm(n))

w <- lassoWeights(X = X, y = y)
c(length = length(w), nonzero = sum(w != 0))
##  length nonzero 
##      30      15

The families, and the methods = name each answers to:

Family methods = Function
Penalized regression lasso, enet, ridge, mcp, scad, l0learn lassoWeights(), enetWeights(), ridgeWeights(), …
Bayesian alphabet bayesA … bayesR bayesAWeights(), bayesARssWeights(), …
Dirichlet process regression dprVb, dprGibbs, dprAdaptiveGibbs dprVbWeights(), dprGibbsWeights(), …
PRS-CS prsCs (sumstats input only) prsCsWeights()
LDpred2 ldpred2Auto, ldpred2Grid (sumstats input only) ldpred2AutoWeights(), ldpred2GridWeights()
mr.mash / mr.ash mrmash, mrash mrmashWeights(), mrashWeights()
SuSiE family susie, susieInf, susieAsh, mvsusie, fsusie susieWeights(), mvsusieWeights(), …

Every token is camelCase, matching the function names. Snake_case spellings are not accepted: bayes_r, dpr_vb and susie_inf all error with the list of known tokens.

Most families have an RSS counterpart taking summary statistics and an LD reference instead of X/y — lassosumRssWeights() for lasso, enetRssWeights() and ridgeRssWeights() for enet and ridge, mcpRssWeights(), sdprWeights() (SDPR) for dprGibbs, susieRssWeights(), bayesARssWeights() through bayesRRssWeights() for the Bayesian alphabet, and so on; dprVb and dprAdaptiveGibbs have none. enet mixes the lasso and ridge penalties at alpha = 0.5 on either input; methods = list(enet = list(alpha = 0.3)) sets another mixing. Those are what twasWeightsPipeline() calls when the input is a QtlSumStats. The LD reference is the same ldReference the rest of the package takes: a genotype panel from readGenotypes(), a GenotypeMixture or an LdMatrix, matched to the statistics by their variantNames. lassosum, the penalized methods, PRS-CS, SDPR and pecotmr’s own Bayesian alphabet sampler use a genotype reference through its standardized genotypes without forming the LD matrix; the mr.ash and mr.mash engines, qgg and hibayes take the matrix, which is formed over the statistics’ variants.

Each Bayesian alphabet method runs on several engines, chosen with backend. On individual-level data, bayesB defaults to BGLR and the others to qgg, with hibayes available for all. On summary statistics, a genotype reference defaults to pecotmr’s sampler, and an LdMatrix to qgg (bayesA, bayesC, bayesN, bayesR) or hibayes (bayesB, bayesL). The pipeline takes the choice per method: twasWeightsPipeline(..., backend = c(bayesR = "hibayes")). LDpred2 defaults the same way, to pecotmr’s sampler on a genotype reference and to bigsnpr on an LdMatrix. lasso also runs on bigsnpr’s lassosum2 (backend = "bigsnpr"), which takes its own settings in BigsnprOptions().

Every alphabet engine reads the LD shrunk toward the identity, shrinkCorr R + (1 - shrinkCorr) I, with shrinkCorr = 0.95 by default, the LDpred2 vignette’s: a reference with fewer people than the window has variants lacks directions the statistics have, and without shrinkage the alphabet’s effects can grow without bound along them. A small or mismatched reference does better with less, down to 0.4; the study’s own genotypes as the reference need none. pecotmr’s sampler also reads the statistics through SDPR’s likelihood by default: the LD as R (R + aI)-1 R and the marginal effects as R (R + aI)-1 b, with a = 0.1, which drops the part of the effects the reference LD cannot explain, the part those effects grow along. BayesAlphabetRssOptions(ldProjection = NULL) turns it off.

PRS-CS, SDPR, pecotmr’s Bayesian alphabet sampler and LDpred2 share LDpred2’s sampler options. nChains runs several chains and averages those that LDpred2’s own filter keeps; raoBlackwell = TRUE averages each effect’s conditional posterior mean rather than its draws; shrinkCorr shrinks the LD toward the identity (PRS-CS, the alphabet and LDpred2); and allowJumpSign = FALSE refuses a draw whose sign differs from the effect’s (the alphabet and LDpred2). Each method takes them in its options, as in prsCsWeights(stat, ldReference, methodArgs = PrsCsOptions(nChains = 4)); the alphabet takes shrinkCorr as an argument of its own, since it applies to every engine.

Several of these engines estimate more than the weights, and fitRetention = "slim" keeps it as the "fit" attribute of the weights they return: SDPR’s heritability estimate (h2), PRS-CS’s posterior shrinkage parameters (psiEst, sigmaEst, phiEst), LDpred2-auto’s chains (each one’s starting p, its estimates of p and h2, and whether it was kept) with the inclusion probabilities, LDpred2-grid’s grid of p, h2 and sparse, and for lassosum and the penalized methods the search itself — path has one row per LD shrinkage s and penalty lambda, with the objective (fbeta), loss, convergence flag and number of non-zero weights, and marks the candidate returned. fitRetention = "full" adds each candidate’s coefficients.

These methods, and LDpred2-grid, pick the returned candidate on held-out summary statistics: the statistics are split into training and held-out parts, the path is refit on each training part, and every candidate is scored on the held-out part. The scoring shrinks the LD toward the identity by an amount set by the reference’s sample size, because a reference smaller than the number of variants misses directions of the true LD, and the least-penalized fits put weight exactly there. An LdMatrix therefore needs nSamples, and the splits are random, so set.seed() makes the weights reproducible. selection = "ldQuadratic" is lassosum’s own pseudovalidation instead.

# An LdMatrix is keyed by chrom:pos:A2:A1 variant ids.
ids <- sprintf("chr1:%d:A:G", 100L * seq_len(p))
R <- cor(X)
dimnames(R) <- list(ids, ids)
stat <- list(
    z = as.vector(cor(X, y)) * sqrt(n),
    n = rep(n, p),
    variantNames = ids
)
set.seed(1)
w <- lassosumRssWeights(
    stat,
    LdMatrix(R, nSamples = n),
    fitRetention = "slim"
)
head(attr(w, "fit")$path)
## # A tibble: 6 × 7
##       s   lambda  fbeta   loss  conv nparams selected
##   <dbl>    <dbl>  <dbl>  <dbl> <int>   <int> <lgl>   
## 1   0.2 0.000100 -0.353 -0.353     1      30 FALSE   
## 2   0.2 0.000144 -0.353 -0.353     1      30 FALSE   
## 3   0.2 0.000207 -0.352 -0.353     1      30 FALSE   
## 4   0.2 0.000298 -0.352 -0.353     1      30 FALSE   
## 5   0.2 0.000428 -0.351 -0.353     1      30 FALSE   
## 6   0.2 0.000616 -0.350 -0.353     1      30 FALSE

The mr.mash methods need prior covariance matrices, built by buildMrmashPriorMatrices(). Pass dataDrivenPriorMatrices to add data-driven components alongside the canonical mixture — computeCovDiag() gives the per-condition diagonal, and mashr::cov_flash() returns a list of FLASH-derived components in the shape that argument expects.

Cross-validation, ensembling and prediction

The pipeline’s ensemble method is not a model of its own — it is a weighted combination of the others, with the weights chosen by cross-validation. Those steps are exported too:

Once you have weights, twasPredict() applies them to a genotype matrix. It takes a named list of weight vectors, with names ending in _weights, and returns one predicted column per entry:

pred <- twasPredict(X, list(gene1_weights = w))
names(pred)
## [1] "gene1_predicted"
round(cor(as.vector(pred[[1]]), y), 3)
## [1] 0.513

That correlation is the in-sample prediction accuracy — on real data you would want the cross-validated version from twasWeightsCv() instead, since in-sample accuracy is optimistic by construction.

Common parameters

twasWeightsPipeline(
    qtlDatasetExample,
    methods = "susie",
    contexts = "brain",
    traitId = "ENSG_example",
    cisWindow = 5e5,
    # or, in place of traitId + cisWindow, a literal window:
    # region = GenomicRanges::GRanges("chr22:25000000-26000000"),
    crossValidationParam = CrossValidationParam(
        folds = 5,
        BPPARAM = BiocParallel::SerialParam(),
        samplePartition = NULL,
        maxVariants = -1
    ),
    # required for a fine-mapping method, and under folds > 1 it must carry
    # the per-fold fits (fineMappingPipeline(..., crossValidationParam = ...))
    fineMappingResult = fmr,
    ensembleParam = EnsembleParam(
        enabled = TRUE,
        r2Threshold = 0.01,
        solver = "quadprog"
    ),
    estimatePi = TRUE,
    residualizationParam = ResidualizationParam(
        phenotypeCovariates = NULL,
        genotypeCovariates = NULL
    ),
    weightsDataType = NULL,
    phenotypeFilterParam = PhenotypeFilterParam(naAction = "drop")
)
  • EnsembleParam(enabled = TRUE) adds an additional ensembleWeights row to the collection — a weighted combination of the individual methods’ CV predictions, filtered by that bundle’s r2Threshold.
  • phenotypeFilterParam is the run’s phenotype QC (see the fine-mapping vignette for semantics).

Reading the result

TwasWeights is a GRangesList subclass with one element per gene — its (study, context, trait), plus the group of a joint fit — holding the variants its methods’ weights are aligned to. A gene’s methods are an axis of its row, listed by methodNames(); the identity is in mcols() columns, reached with $:

tw
## TwasWeights: 1 genes, 1 entries
##   1 studies, 1 contexts, 1 traits, 1 methods
##   LD reference: NULL (individual-level fit)
##  [1] "study"        "context"      "trait"        "methods"      "weightTensor"
##  [6] "fits"         "foldFits"     "cvResult"     "cvPartition"  "standardized"
## [11] "dataType"     "traitPos"     "region"       "ldReference"
## [1] "lasso"

as(tw, "GRanges") gives the long per-variant view across every method at once — one range per variant and method, carrying its weight and .method — which is usually what you want for inspection:

head(as.data.frame(as(tw, "GRanges")), 3)
##   seqnames    start      end width strand A1 A2 weight .study .context
## 1    chr22 14850625 14850625     1      *  G  T      0 study1    brain
## 2    chr22 14870204 14870204     1      *  C  T      0 study1    brain
## 3    chr22 14878387 14878387     1      *  A  G      0 study1    brain
##         .trait .ldReference .method .weightColumn
## 1 ENSG_example         <NA>   lasso          <NA>
## 2 ENSG_example         <NA>   lasso          <NA>
## 3 ENSG_example         <NA>   lasso          <NA>

To pull one method’s weights, pass the gene and the method to the accessor:

key <- list(
    studyName = tw$study[[1L]],
    context = tw$context[[1L]],
    trait = tw$trait[[1L]],
    method = methodNames(tw)[[1L]]
)
w <- do.call(weights, c(list(tw), key))
length(w)
## [1] 198
do.call(weightStandardized, c(list(tw), key)) # TRUE from the pipeline
## [1] TRUE
do.call(weightsDataType, c(list(tw), key)) # tag carried through for reporting
## NULL

weightStandardized() is TRUE for anything the pipeline fitted: genotypes are centred and scaled to unit variance before every method runs, so all the weights are on one scale. That scaling is part of residualization — ResidualizationParam(scaleResiduals = FALSE) opts out, and it is on by default — so the recorded flag reports what was done rather than what you claimed. That is not bookkeeping. Every method here returns coefficients on the scale of the X it was handed — exactly so for glmnet, ncvreg, L0Learn, mr.ash, the Bayesian alphabet and susieWeights() — while the DPR samplers are not even scale-equivariant, so rescaling X changes their fit rather than just its units. Standardizing up front removes the question, and lands on the scale the LD correlations and z-scores behind a TWAS Z already use. learnTwasWeights() still takes weightStandardized, because there you supply X yourself.

weights() returns a named numeric vector for univariate methods and a matrix for multivariate ones. The remaining payloads are keyed the same way: variantIds() for the aligned variant set, cvResult() for the cross-validation record (rsq, r, predicted values), and methodFits() for the full fit object when the pipeline kept fits (fitRetention != "none"). resolveWeights() returns the weights and their variant IDs together, which is the form the downstream TWAS and MR steps consume.

Publishing the weights

The same two writers that write fine-mapping results write weights. writeGwasVcf() writes a GWAS-VCF-style file: one record per variant, one sample per fitted model, the weight in the WGT FORMAT field, and each model’s identity in its own ##SAMPLE header line. writeBed() writes the same thing as a long table, one row per (variant, model).

writeGwasVcf(tw, "weights.vcf.gz")
writeGwasVcf(tw, "lasso_weights.vcf.gz", method = "lasso") # one model
writeBed(tw, "weights.bed.gz")

Both export the full-data fit only, and refuse an object that has none — crossValidationParam = CrossValidationParam(fitFullData = FALSE) produces entries holding nothing but cross-validation results, and those cannot be published: they describe held-out folds, and fold membership is sample-level information. For the same reason neither exporter writes CV metrics. Those stay on the object:

cv <- cvResult(
    tw,
    studyName = "s1",
    context = "brain",
    trait = "g1",
    method = "susie"
)
cv$performance # per-method metric matrix: corr, rsq, adj_rsq, pval, RMSE, MAE

Fine-mapping-derived entries (susie, susieInf, susieAsh, mvsusie, fsusie) are skipped with a message by both exporters, because their “weights” are the fine-mapping posterior re-expressed — export the fine-mapping result instead, which carries the posterior, its standard error and the credible sets. Selecting only such a method, as in method = "susie", is therefore an error rather than an empty file.

readTwasWeights() reads either format back into a TwasWeights:

tw <- readTwasWeights("weights.bed.gz")
tw <- readTwasWeights(
    "weights.bed.gz",
    which = GenomicRanges::GRanges("chr22:10000000-19000000")
)

A weight is all it requires, so a table converted from another tool reads with nothing but variant ids and a WGT column — plus the identity axes the collection is keyed by, which studyName / context / trait / method supply when the file does not record them. weightStandardized is required when the file does not say which genotype scale the weights are on: that is not recoverable from the numbers, since converting between the two rescales every variant by its own allele-frequency-dependent factor.

Next steps

Session info

## R version 4.5.3 (2026-03-11)
## Platform: x86_64-conda-linux-gnu
## Running under: Ubuntu 24.04.5 LTS
## 
## Matrix products: default
## BLAS/LAPACK: /home/runner/work/pecotmr/pecotmr/.pixi/envs/default/lib/libopenblasp-r0.3.34.so;  LAPACK version 3.12.0
## 
## locale:
##  [1] LC_CTYPE=C.UTF-8       LC_NUMERIC=C           LC_TIME=C.UTF-8       
##  [4] LC_COLLATE=C.UTF-8     LC_MONETARY=C.UTF-8    LC_MESSAGES=C.UTF-8   
##  [7] LC_PAPER=C.UTF-8       LC_NAME=C              LC_ADDRESS=C          
## [10] LC_TELEPHONE=C         LC_MEASUREMENT=C.UTF-8 LC_IDENTIFICATION=C   
## 
## time zone: Etc/UTC
## tzcode source: system (glibc)
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] pecotmr_0.9.4
## 
## loaded via a namespace (and not attached):
##   [1] mathjaxr_2.0-0              RColorBrewer_1.1-3         
##   [3] jsonlite_2.0.0              shape_1.4.6.1              
##   [5] MultiAssayExperiment_1.36.2 magrittr_2.0.5             
##   [7] farver_2.1.2                rmarkdown_2.32             
##   [9] fs_2.1.0                    ragg_1.5.2                 
##  [11] vctrs_0.7.3                 memoise_2.0.1              
##  [13] Rsamtools_2.26.0            mixsqp_0.3-54              
##  [15] htmltools_0.5.9             S4Arrays_1.10.1            
##  [17] BiocBaseUtils_1.12.0        curl_8.0.0                 
##  [19] tictoc_1.2.1                SparseArray_1.10.10        
##  [21] sass_0.4.10                 bslib_0.12.0               
##  [23] htmlwidgets_1.6.4           desc_1.4.3                 
##  [25] bigsnpr_1.12.21             plyr_1.8.9                 
##  [27] httr2_1.3.0                 cachem_1.1.0               
##  [29] lifecycle_1.0.5             iterators_1.0.14           
##  [31] pkgconfig_2.0.3             Matrix_1.7-6               
##  [33] R6_2.6.1                    fastmap_1.2.0              
##  [35] MatrixGenerics_1.22.0       digest_0.6.39              
##  [37] numDeriv_2016.8-1.1         reshape_0.8.10             
##  [39] S4Vectors_0.48.1            irlba_2.4.1                
##  [41] textshaping_1.0.5           GenomicRanges_1.62.1       
##  [43] rmio_0.4.0                  RSQLite_3.53.3             
##  [45] filelock_1.0.3              metadat_1.6-0              
##  [47] httr_1.4.9                  abind_1.4-8                
##  [49] compiler_4.5.3              rngtools_1.5.2             
##  [51] bit64_4.8.6                 withr_3.0.3                
##  [53] doParallel_1.0.17           S7_0.2.2                   
##  [55] backports_1.5.1             metafor_5.0-1              
##  [57] BiocParallel_1.44.0         DBI_1.3.0                  
##  [59] bigassertr_0.2.0            MASS_7.3-66                
##  [61] DelayedArray_0.36.1         tools_4.5.3                
##  [63] bigstatsr_1.6.2             otel_0.2.0                 
##  [65] glue_1.8.1                  quadprog_1.5-8             
##  [67] nlme_3.1-170                bigparallelr_0.3.2         
##  [69] grid_4.5.3                  checkmate_2.3.3            
##  [71] generics_0.1.4              gtable_0.3.6               
##  [73] bigsparser_0.7.3            tzdb_0.5.0                 
##  [75] susieR_0.16.6               flock_0.7                  
##  [77] tidyr_1.3.2                 data.table_1.18.6.1        
##  [79] hms_1.1.4                   XVector_0.50.0             
##  [81] BiocGenerics_0.56.0         foreach_1.5.2              
##  [83] pillar_1.11.1               stringr_1.6.0              
##  [85] vroom_1.7.1                 robustbase_0.99-7          
##  [87] splines_4.5.3               dplyr_1.2.1                
##  [89] BiocFileCache_3.0.0         lattice_0.23-1             
##  [91] survival_3.8-12             bit_4.6.0                  
##  [93] tidyselect_1.2.1            Biostrings_2.78.0          
##  [95] PlinkMatrix_1.0.0           knitr_1.52                 
##  [97] IRanges_2.44.0              Seqinfo_1.0.0              
##  [99] SummarizedExperiment_1.40.0 stats4_4.5.3               
## [101] xfun_0.61                   Biobase_2.70.0             
## [103] matrixStats_1.5.0           DEoptimR_1.2-1             
## [105] stringi_1.8.9               UCSC.utils_1.6.1           
## [107] yaml_2.3.12                 evaluate_1.0.5             
## [109] codetools_0.2-20            archive_1.1.13             
## [111] tibble_3.3.1                cli_3.6.6                  
## [113] RcppParallel_5.1.11-2       systemfonts_1.3.2          
## [115] jquerylib_0.1.4             Rcpp_1.1.2                 
## [117] GenomeInfoDb_1.46.2         zigg_0.0.2                 
## [119] dbplyr_2.6.0                parallel_4.5.3             
## [121] Rfast_2.1.5.2               pkgdown_2.2.1              
## [123] ggplot2_4.0.3               readr_2.2.0                
## [125] blob_1.3.0                  doRNG_1.8.6.3              
## [127] bitops_1.1-0                glmnet_5.0                 
## [129] scales_1.4.0                purrr_1.2.2                
## [131] crayon_1.5.3                rlang_1.3.0                
## [133] cowplot_1.2.0