Learning TWAS weights with pecotmr
pecotmr authors
2026-11-02
Source:vignettes/twas-weights.Rmd
twas-weights.RmdOverview
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:
length(variantIds(entry))## [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:
-
twasWeightsCv()runs the cross-validation that scores each method. -
learnTwasWeights()runs the requested weight methods on the full data. -
combineTwasWeights()forms the ensemble from per-method weights. -
estimateSparsity()reports how sparse a fitted weight vector is.
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"
## [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 additionalensembleWeightsrow to the collection — a weighted combination of the individual methods’ CV predictions, filtered by that bundle’sr2Threshold. -
phenotypeFilterParamis 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)
colnames(tw)## [1] "study" "context" "trait" "methods" "weightTensor"
## [6] "fits" "foldFits" "cvResult" "cvPartition" "standardized"
## [11] "dataType" "traitPos" "region" "ldReference"
methodNames(tw)## [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
## [1] TRUE
## 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, MAEFine-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
- Combine the learned weights with a GWAS via
causalInferencePipelineto compute per-gene TWAS Z-scores.
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