Skip to contents

This vignette demonstrates the enrichment analysis part of the pecotmr package, which largely follows from fastENLOC (https://github.com/xqwen/fastenloc) but uses susieR fitted objects as input to estimate priors for use with the coloc package (coloc v5, aka SuSiE-coloc). The main differences are:

  1. enrichment is based on all QTL variants whether or not they are inside signal clusters;
  2. causal QTL are sampled from SuSiE single effects, not signal clusters;
  3. a variant may be a QTL for not only multiple conditions (e.g. cell types) but also multiple regions (e.g. genes).

The example uses de-identified data shipped with the package. All sample names, variant positions, and identifiers are synthetic.

Set up environment and load data

The enrichment pipeline consumes fine-mapping results as pecotmr S4 objects: a GwasFineMappingResult for the GWAS and a QtlFineMappingResult for the molecular QTLs. Both are shipped as example datasets.

data(gwasFineMappingExample)
data(qtlFineMappingExample)
gwasFineMappingExample
## GwasFineMappingResult: 1 entries
##   1 studies, 1 methods
##   LD reference: plink1 @ pecotmr://extdata/toy_canonical
qtlFineMappingExample
## QtlFineMappingResult: 1 entries
##   1 studies, 1 contexts, 1 traits, 1 methods
##   LD reference: NULL (individual-level fit)

Input data

Each object is a GRangesList-backed collection, one element per tuple; use the accessors rather than reaching into slots. topLoci() returns the per-variant posterior summary as a GRanges, with the identity columns, pip, and credible-set membership in mcols():

head(topLoci(gwasFineMappingExample))
## GRanges object with 6 ranges and 31 metadata columns:
##       seqnames    ranges strand |       study     context       trait
##          <Rle> <IRanges>  <Rle> | <character> <character> <character>
##   [1]    chr22  32337604      * |     study_1        <NA>        <NA>
##   [2]    chr22  32350609      * |     study_1        <NA>        <NA>
##   [3]    chr22  32354393      * |     study_1        <NA>        <NA>
##   [4]    chr22  32362470      * |     study_1        <NA>        <NA>
##   [5]    chr22  32362793      * |     study_1        <NA>        <NA>
##   [6]    chr22  32369273      * |     study_1        <NA>        <NA>
##           blockId      method    dataType  dataSource standardized
##       <character> <character> <character> <character>  <character>
##   [1]    region_1       susie        <NA>        <NA>         <NA>
##   [2]    region_1       susie        <NA>        <NA>         <NA>
##   [3]    region_1       susie        <NA>        <NA>         <NA>
##   [4]    region_1       susie        <NA>        <NA>         <NA>
##   [5]    region_1       susie        <NA>        <NA>         <NA>
##   [6]    region_1       susie        <NA>        <NA>         <NA>
##               variant_id       chrom       pos          A1          A2
##              <character> <character> <integer> <character> <character>
##   [1] chr22:32337604:C:T       chr22  32337604           T           C
##   [2] chr22:32350609:A:G       chr22  32350609           G           A
##   [3] chr22:32354393:A:G       chr22  32354393           G           A
##   [4] chr22:32362470:T:C       chr22  32362470           C           T
##   [5] chr22:32362793:A:G       chr22  32362793           G           A
##   [6] chr22:32369273:C:A       chr22  32369273           A           C
##               N        af posterior_effect posterior_se     theta       pip
##       <numeric> <numeric>        <numeric>    <numeric> <numeric> <numeric>
##   [1]        NA        NA      0.002119882   0.00433735         0 0.1968901
##   [2]        NA        NA      0.000792956   0.00281382         0 0.0751927
##   [3]        NA        NA      0.001375308   0.00361922         0 0.1288942
##   [4]        NA        NA      0.004781037   0.00552692         0 0.4367631
##   [5]        NA        NA      0.001452388   0.00370652         0 0.1359622
##   [6]        NA        NA      0.000270653   0.00166687         0 0.0262804
##           logBF       cs_95       cs_70       cs_50 cs_95_purity cs_70_purity
##       <numeric> <character> <character> <character>    <numeric>    <numeric>
##   [1]   16782.2 susie_rss_3 susie_rss_3 susie_rss_3     0.984661     0.984661
##   [2]   16755.2 susie_rss_3 susie_rss_0 susie_rss_0     0.984661     0.000000
##   [3]   16919.3 susie_rss_3 susie_rss_0 susie_rss_0     0.984661     0.000000
##   [4]   16950.6 susie_rss_3 susie_rss_3 susie_rss_3     0.984661     0.984661
##   [5]   16766.3 susie_rss_3 susie_rss_3 susie_rss_0     0.984661     0.984661
##   [6]   21268.5 susie_rss_0 susie_rss_0 susie_rss_0     0.000000     0.000000
##       cs_50_purity within_cs_pip        gene       event grange_start
##          <numeric>     <numeric> <character> <character>    <integer>
##   [1]     0.987238     0.1968901        <NA>        <NA>         <NA>
##   [2]     0.000000     0.0751927        <NA>        <NA>         <NA>
##   [3]     0.000000     0.1288942        <NA>        <NA>         <NA>
##   [4]     0.987238     0.4367631        <NA>        <NA>         <NA>
##   [5]     0.000000     0.1359622        <NA>        <NA>         <NA>
##   [6]     0.000000            NA        <NA>        <NA>         <NA>
##       grange_end
##        <integer>
##   [1]       <NA>
##   [2]       <NA>
##   [3]       <NA>
##   [4]       <NA>
##   [5]       <NA>
##   [6]       <NA>
##   -------
##   seqinfo: 1 sequence from an unspecified genome; no seqlengths
head(topLoci(qtlFineMappingExample))
## GRanges object with 6 ranges and 31 metadata columns:
##       seqnames    ranges strand |       study     context       trait
##          <Rle> <IRanges>  <Rle> | <character> <character> <character>
##   [1]    chr22  32596246      * |     study_1   context_1      gene_1
##   [2]    chr22  32724254      * |     study_1   context_1      gene_1
##   [3]    chr22  32724508      * |     study_1   context_1      gene_1
##   [4]    chr22  32728910      * |     study_1   context_1      gene_1
##   [5]    chr22  32729588      * |     study_1   context_1      gene_1
##   [6]    chr22  32732510      * |     study_1   context_1      gene_1
##           blockId      method    dataType  dataSource standardized
##       <character> <character> <character> <character>  <character>
##   [1]        <NA>       susie        <NA>        <NA>         <NA>
##   [2]        <NA>       susie        <NA>        <NA>         <NA>
##   [3]        <NA>       susie        <NA>        <NA>         <NA>
##   [4]        <NA>       susie        <NA>        <NA>         <NA>
##   [5]        <NA>       susie        <NA>        <NA>         <NA>
##   [6]        <NA>       susie        <NA>        <NA>         <NA>
##               variant_id       chrom       pos          A1          A2
##              <character> <character> <integer> <character> <character>
##   [1] chr22:32596246:T:C       chr22  32596246           C           T
##   [2] chr22:32724254:T:C       chr22  32724254           C           T
##   [3] chr22:32724508:C:A       chr22  32724508           A           C
##   [4] chr22:32728910:G:A       chr22  32728910           A           G
##   [5] chr22:32729588:T:C       chr22  32729588           C           T
##   [6] chr22:32732510:G:A       chr22  32732510           A           G
##               N        af posterior_effect posterior_se     theta       pip
##       <numeric> <numeric>        <numeric>    <numeric> <numeric> <numeric>
##   [1]       415        NA      -0.01284643    0.0324407         0 0.1444970
##   [2]       415        NA       0.00601338    0.0283820         0 0.0442663
##   [3]       415        NA       0.00601338    0.0283820         0 0.0442663
##   [4]       415        NA       0.00534990    0.0268018         0 0.0394963
##   [5]       415        NA       0.00464131    0.0249437         0 0.0345102
##   [6]       415        NA       0.00937127    0.0351058         0 0.0684514
##           logBF       cs_95       cs_70       cs_50 cs_95_purity cs_70_purity
##       <numeric> <character> <character> <character>    <numeric>    <numeric>
##   [1]   6.53015     susie_0     susie_0     susie_0     0.000000     0.000000
##   [2]  15.67566     susie_1     susie_1     susie_1     0.625503     0.938229
##   [3]  15.67566     susie_1     susie_1     susie_1     0.625503     0.938229
##   [4]  15.56140     susie_1     susie_1     susie_0     0.625503     0.938229
##   [5]  15.42615     susie_1     susie_1     susie_0     0.625503     0.938229
##   [6]  16.11229     susie_1     susie_1     susie_1     0.625503     0.938229
##       cs_50_purity within_cs_pip        gene       event grange_start
##          <numeric>     <numeric> <character> <character>    <integer>
##   [1]     0.000000            NA        <NA>        <NA>         <NA>
##   [2]     0.940595     0.0441714        <NA>        <NA>         <NA>
##   [3]     0.940595     0.0441714        <NA>        <NA>         <NA>
##   [4]     0.000000     0.0394021        <NA>        <NA>         <NA>
##   [5]     0.000000     0.0344176        <NA>        <NA>         <NA>
##   [6]     0.940595     0.0683547        <NA>        <NA>         <NA>
##       grange_end
##        <integer>
##   [1]       <NA>
##   [2]       <NA>
##   [3]       <NA>
##   [4]       <NA>
##   [5]       <NA>
##   [6]       <NA>
##   -------
##   seqinfo: 1 sequence from an unspecified genome; no seqlengths

Posterior inclusion probabilities across all variants in each region, taken from the underlying SuSiE fit:

gwas_pip <- susieFit(gwasFineMappingExample)$pip
qtl_pip <- susieFit(qtlFineMappingExample)$pip
op <- par(mfrow = c(1, 2))
plot(
    gwas_pip,
    ylab = "PIP",
    xlab = "Variant index",
    main = "GWAS PIPs",
    pch = 16,
    cex = 0.6
)
plot(
    qtl_pip,
    ylab = "PIP",
    xlab = "Variant index",
    main = "QTL PIPs",
    pch = 16,
    cex = 0.6
)

par(op)

Perform enrichment analysis

qtlEnrichmentPipeline() takes the two fine-mapping collections directly and estimates, for every (gwasStudy, qtlStudy, qtlContext) combination, how enriched the molecular QTLs are among the GWAS causal variants. The GWAS collection must be RSS-derived (it carries an ldReference); gwasFineMappingExample already does.

enrich_res <- qtlEnrichmentPipeline(
    gwasFineMappingResult = gwasFineMappingExample,
    qtlFineMappingResult = qtlFineMappingExample,
    verbose = FALSE
)
## Warning: numGwas is not provided. Estimating piGwas from the data. Note that
## this estimate may be biased if the input gwasPip does not contain genome-wide
## variants.
## Warning: piQtl is not provided. Estimating piQtl from the data. Note that this
## estimate may be biased if either 1) the input susieQtlRegions does not have
## enough data, or 2) the single effects only include variables inside of credible
## sets or signal clusters.
## Fine-mapped GWAS and QTL data loaded successfully for enrichment analysis!
## Proportion of xQTL missing from GWAS variants: 0 in MI round 0
## Proportion of xQTL missing from GWAS variants: 0 in MI round 1
## Proportion of xQTL missing from GWAS variants: 0 in MI round 2
## Proportion of xQTL missing from GWAS variants: 0 in MI round 3
## Proportion of xQTL missing from GWAS variants: 0 in MI round 4
## Proportion of xQTL missing from GWAS variants: 0 in MI round 5
## Proportion of xQTL missing from GWAS variants: 0 in MI round 6
## Proportion of xQTL missing from GWAS variants: 0 in MI round 7
## Proportion of xQTL missing from GWAS variants: 0 in MI round 8
## Proportion of xQTL missing from GWAS variants: 0 in MI round 9
## Proportion of xQTL missing from GWAS variants: 0 in MI round 10
## Proportion of xQTL missing from GWAS variants: 0 in MI round 11
## Proportion of xQTL missing from GWAS variants: 0 in MI round 12
## Proportion of xQTL missing from GWAS variants: 0 in MI round 13
## Proportion of xQTL missing from GWAS variants: 0 in MI round 14
## Proportion of xQTL missing from GWAS variants: 0 in MI round 15
## Proportion of xQTL missing from GWAS variants: 0 in MI round 16
## Proportion of xQTL missing from GWAS variants: 0 in MI round 17
## Proportion of xQTL missing from GWAS variants: 0 in MI round 18
## Proportion of xQTL missing from GWAS variants: 0 in MI round 19
## Proportion of xQTL missing from GWAS variants: 0 in MI round 20
## Proportion of xQTL missing from GWAS variants: 0 in MI round 21
## Proportion of xQTL missing from GWAS variants: 0 in MI round 22
## Proportion of xQTL missing from GWAS variants: 0 in MI round 23
## Proportion of xQTL missing from GWAS variants: 0 in MI round 24
## EM updates completed!
## Outlier filtering removed 1 MI round(s)
enrich_res
## # A tibble: 1 × 16
##   gwasStudy gwasContext gwasTrait qtlStudy qtlContext  enrichment enrichmentSe
##   <chr>     <chr>       <chr>     <chr>    <chr>            <dbl>        <dbl>
## 1 study_1   NA          NA        study_1  context_1  -0.00000795        1.000
## # ℹ 9 more variables: enrichmentLogOdds <dbl>,
## #   enrichmentLogOddsNoShrinkage <dbl>, enrichmentSeNoShrinkage <dbl>,
## #   intercept <dbl>, interceptSe <dbl>, colocP1 <dbl>, colocP2 <dbl>,
## #   colocP12 <dbl>, effectiveMiRounds <dbl>

The result is a data frame with one row per (gwasStudy, qtlStudy, qtlContext) combination, giving the enrichment estimate (enrichment), its standard error (enrichmentSe), and the log-odds (enrichmentLogOdds). These enrichment priors can then be supplied to a colocalization analysis. This synthetic single-region example is for illustration only; stable enrichment estimates require many fine-mapped regions.

Session information

Here are some details about the computing environment, including the versions of R and the R packages used to generate these results.

## 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.3
## 
## loaded via a namespace (and not attached):
##   [1] tidyselect_1.2.1            dplyr_1.2.1                
##   [3] farver_2.1.2                Biostrings_2.78.0          
##   [5] S7_0.2.2                    bitops_1.1-0               
##   [7] fastmap_1.2.0               reshape_0.8.10             
##   [9] mathjaxr_2.0-0              digest_0.6.39              
##  [11] lifecycle_1.0.5             magrittr_2.0.5             
##  [13] compiler_4.5.3              rlang_1.3.0                
##  [15] sass_0.4.10                 tools_4.5.3                
##  [17] utf8_1.2.6                  yaml_2.3.12                
##  [19] knitr_1.52                  S4Arrays_1.10.1            
##  [21] htmlwidgets_1.6.4           bit_4.6.0                  
##  [23] DelayedArray_0.36.1         plyr_1.8.9                 
##  [25] RColorBrewer_1.1-3          abind_1.4-8                
##  [27] BiocParallel_1.44.0         withr_3.0.3                
##  [29] purrr_1.2.2                 numDeriv_2016.8-1.1        
##  [31] BiocGenerics_0.56.0         desc_1.4.3                 
##  [33] grid_4.5.3                  stats4_4.5.3               
##  [35] susieR_0.16.6               ggplot2_4.0.3              
##  [37] scales_1.4.0                MASS_7.3-66                
##  [39] MultiAssayExperiment_1.36.2 SummarizedExperiment_1.40.0
##  [41] cli_3.6.6                   rmarkdown_2.32             
##  [43] metafor_5.0-1               crayon_1.5.3               
##  [45] ragg_1.5.2                  generics_0.1.4             
##  [47] otel_0.2.0                  RcppParallel_5.1.11-2      
##  [49] tzdb_0.5.0                  BiocBaseUtils_1.12.0       
##  [51] cachem_1.1.0                stringr_1.6.0              
##  [53] metadat_1.6-0               parallel_4.5.3             
##  [55] XVector_0.50.0              matrixStats_1.5.0          
##  [57] vctrs_0.7.3                 Matrix_1.7-6               
##  [59] jsonlite_2.0.0              IRanges_2.44.0             
##  [61] hms_1.1.4                   S4Vectors_0.48.1           
##  [63] bit64_4.8.6                 mixsqp_0.3-54              
##  [65] irlba_2.4.1                 archive_1.1.13             
##  [67] systemfonts_1.3.2           tidyr_1.3.2                
##  [69] jquerylib_0.1.4             glue_1.8.1                 
##  [71] pkgdown_2.2.1               codetools_0.2-20           
##  [73] stringi_1.8.9               gtable_0.3.6               
##  [75] GenomicRanges_1.62.1        quadprog_1.5-8             
##  [77] tibble_3.3.1                pillar_1.11.1              
##  [79] htmltools_0.5.9             Seqinfo_1.0.0              
##  [81] R6_2.6.1                    zigg_0.0.2                 
##  [83] textshaping_1.0.5           vroom_1.7.1                
##  [85] evaluate_1.0.5              lattice_0.23-1             
##  [87] Biobase_2.70.0              readr_2.2.0                
##  [89] tictoc_1.2.1                backports_1.5.1            
##  [91] Rsamtools_2.26.0            Rfast_2.1.5.2              
##  [93] bslib_0.12.0                Rcpp_1.1.2                 
##  [95] SparseArray_1.10.10         nlme_3.1-170               
##  [97] checkmate_2.3.3             xfun_0.61                  
##  [99] fs_2.1.0                    MatrixGenerics_1.22.0      
## [101] pkgconfig_2.0.3