RBPEqBind simulates competitive binding of multiple RNA-binding proteins (RBPs) to RNA sequences using an equilibrium binding model. This package provides tools for:
RBPEqBind implements a mathematical simulation framework for RNA-RBP interactions based on equilibrium binding kinetics. The simulation follows two key principles:
Competitive binding among multiple RNA-binding proteins (RBPs) is a fundamental regulator of post-transcriptional gene expression. Understanding how multiple RBPs compete for overlapping binding sites is critical for interpreting transcriptome-wide binding data (such as CLIP-seq) and functional genomics studies. RBPEqBind provides a mathematical simulation framework to model competitive RBP binding kinetics on transcript sequences, allowing researchers to explore how RBP binding landscapes are altered by relative protein abundances, RNA concentration, and binding affinity distributions.
For an RNA sequence of length \(l\) interacting with \(N\) RBPs, there exist \(l - (k-1)\) possible k-mer binding sites. For each binding site \(R\) on the RNA, the kinetic reaction scheme is:
\[R + P_i \rightleftharpoons P_iR\]
At equilibrium, the dissociation constant is defined as:
\[K_{D,i} = \frac{[R_{eq}][P_{i,eq}]}{[P_iR]}\]
Given the initial concentrations of RBPs (\([P_{0,i}]\)) and RNA (\([R_0]\)), the equilibrium concentration of each RNA-RBP complex can be determined by solving the system of equations with the constraint:
\[[R_0] \geq [R_{eq}] \geq 0\]
From the equilibrium concentrations, binding probability at each unique motif site (\(p_{site}\)) is calculated as \([P_iR] / [R_{0,e}]\), where \([R_{0,e}] = n \times [R_0]\) is the effective initial RNA concentration accounting for the motif multiplicity \(n\) in the transcript sequence. Per-position probability (\(p_{pos}\)) is then obtained by averaging overlapping \(p_{site}\) values across all \(k\)-mer windows covering each position.
For RBP enrichment analysis:
- With a single active RBP, fold-change (_occupancy_fc and _density_fc) represents enrichment relative to a uniform distribution across the transcript.
- With multiple active RBPs, fold-change represents the competitive ratio of self-occupancy relative to the sum of all other active competing RBPs at that position.
For a detailed description of the mathematical model and its biological applications, please refer to:
Yi S, Singh SS, Ye X, Krishna R, Kothwela V, Jankowsky E, Luna JM. (2025). Inherent Specificity and Mutational Sensitivity as Quantitative Metrics for RBP Binding. bioRxiv. https://www.biorxiv.org/content/10.1101/2025.03.28.646018v4
To install RBPEqBind, start R and use the BiocManager package:
if (!requireNamespace("BiocManager", quietly = TRUE))
install.packages("BiocManager")
BiocManager::install("RBPEqBind")
For the development version from GitHub, you can use devtools or pak:
# install.packages("devtools")
devtools::install_github("S00NYI/RBPEqBind")
library(RBPEqBind)
library(data.table)
library(ggplot2)
RBP affinity models are loaded from CSV files containing k-mer motifs and scores (enrichment or affinity values). The CSV file requires a column of sequence motifs and score columns for each RBP.
The RBP-RNA affinity scores can be derived from:
For generating motif enrichment scores from CLIP experiments, please refer to the companion R package RBPSpecificity (in development).
# Load raw models from package sample data
model_file <- system.file("extdata", "model_RBP.csv", package = "RBPEqBind")
raw_models <- loadModel(model_file)
# View available RBPs
names(raw_models)
#> [1] "HH" "HL" "LH" "LL"
The included four model RBPs (HH, HL, LH, LL) are designed to bind the 5-mer poly-U motif (UUUUU) with highest affinity, but with varying binding specificity and affinity distributions. For details on how these model RBP scores were generated, please refer to the manuscript referenced above.
The viewModel() function can be used to inspect the score distributions of
raw models before affinity conversion:
viewModel(raw_models, rbp = c("HH", "HL", "LH", "LL"), bins = 100, alpha = 0.4)
Figure 1: Raw score distributions for model RBPs
The included RBP models have enrichment scores normalized to a 0-to-1 scale.
For binding simulation, each RBP may have different minimum and maximum affinity
ranges, which are set using the setModel() function:
# High affinity (H) = high Ka -> low Kd
# Low affinity (L) = low Ka -> high Kd
max_affinities <- c(
"HH" = 100, "HL" = 100, # Ka_max = 100 -> Kd_min = 0.01 nM
"LH" = 10, "LL" = 10 # Ka_max = 10 -> Kd_min = 0.1 nM
)
min_affinities <- c(
"HH" = 0.001, "HL" = 0.001, # Ka_min = 0.001 -> Kd_max = 1000 nM
"LH" = 0.001, "LL" = 0.001
)
rbp_models <- setModel(raw_models,
max_affinity = max_affinities,
min_affinity = min_affinities)
The viewModel() function can be used again to inspect the converted affinity
(Ka) or dissociation constant (Kd) distributions:
viewModel(rbp_models, rbp = c("HH", "HL", "LH", "LL"), metric = "Ka",
bins = 100, alpha = 0.4)
Figure 2: Ka distributions for all RBPs
To compare specific RBPs, pass only the desired subset:
viewModel(rbp_models, rbp = c("HH", "LL"), metric = "Ka", bins = 100, alpha = 0.5)
Figure 3: Comparing HH vs LL affinity distributions
Given the initial concentrations of RBPs and RNA, along with association constants (or dissociation constants) per motif per RBP, RBPEqBind calculates several metrics for each nucleotide position:
| Metric | Description |
|---|---|
occupancy |
Binding probability (0-1) |
density |
Normalized binding (sum = 1) |
density_fc |
Fold-change over expectation |
occupancy_fc |
Fold-change over mean occupancy |
For single RBP simulations (only one RBP with concentration > 0):
density_fc = density × L (enrichment over uniform distribution)occupancy_fc = occupancy / mean(occupancy)For multiple active RBPs:
density_fc = self_density / sum(other_densities)occupancy_fc = self_occupancy / sum(other_occupancies)This vignette covers four simulation scenarios of increasing complexity:
Users can provide their own sequence or use the built-in generateRNA() function
to create randomized RNA sequences:
# Generate a random 100-nt RNA sequence
random_seq <- generateRNA(100)
cat("Generated sequence:", substr(random_seq, 1, 50), "...\n")
#> Generated sequence: UUUUUUCCGACGCCCACUAGACCGAUCAAGCGUACCGACCAAAGAAAAUC ...
# Or use a specific reference sequence
seq <- "UAGCGGUGCGAUUGGCCCGUGGACCGCGUUUUUGCACUCAUCGUUUCGCACUAAGUACAUAUAGUUGCGACAAAGCCGCUUAUGAGUUGGGGGUAUAUUC"
Set initial concentrations of RBPs and target RNA, then run the simulation:
# Define concentrations (nM) - competitive binding of all 4 RBPs
prot_concs <- c("HH" = 100, "HL" = 100, "LH" = 100, "LL" = 100)
rna_conc <- 10.0 # nM
# Detect k-mer size from models
k_size <- nchar(rbp_models$HH$motif[1])
# Run simulation
res <- simulateBinding(
sequence = seq,
rbp_models = rbp_models,
protein_concs = prot_concs,
rna_conc = rna_conc,
k = k_size
)
# Add transcript column for visualization
res$transcript <- "Custom RNA"
head(res)
#> pos nt HH HL LH LL HH_density
#> <int> <char> <num> <num> <num> <num> <num>
#> 1: 1 U 0.07129708 0.1167119 0.3740217 0.4372868 0.006983791
#> 2: 2 A 0.05867352 0.1329368 0.3441924 0.4633364 0.005747270
#> 3: 3 G 0.04830287 0.1269083 0.3580165 0.4659452 0.004731430
#> 4: 4 C 0.04433490 0.1227539 0.3663078 0.4657982 0.004342753
#> 5: 5 G 0.04896784 0.1274139 0.3609860 0.4618369 0.004796566
#> 6: 6 G 0.04653250 0.1242896 0.3641747 0.4642060 0.004558016
#> HL_density LH_density LL_density HH_density_fc HH_occupancy_fc
#> <num> <num> <num> <num> <num>
#> 1: 0.007527902 0.010734056 0.01110460 0.2378145 0.07682706
#> 2: 0.008574407 0.009877986 0.01176611 0.1901904 0.06238773
#> 3: 0.008185566 0.010274724 0.01183236 0.1561907 0.05079860
#> 4: 0.007917610 0.010512674 0.01182862 0.1435198 0.04643079
#> 5: 0.008218178 0.010359946 0.01172803 0.1582704 0.05153225
#> 6: 0.008016664 0.010451457 0.01178819 0.1506468 0.04884428
#> HL_density_fc HL_occupancy_fc LH_density_fc LH_occupancy_fc LL_density_fc
#> <num> <num> <num> <num> <num>
#> 1: 0.2611820 0.1322356 0.4190324 0.5981516 0.4398600
#> 2: 0.3130332 0.1534709 0.3786441 0.5255273 0.4862096
#> 3: 0.3049933 0.1454929 0.4151512 0.5583919 0.5101975
#> 4: 0.2967170 0.1400596 0.4364100 0.5787885 0.5194135
#> 5: 0.3056842 0.1461519 0.4187059 0.5656150 0.5017405
#> 6: 0.2991553 0.1420594 0.4289912 0.5734781 0.5119482
#> LL_occupancy_fc transcript
#> <num> <char>
#> 1: 0.7780479 Custom RNA
#> 2: 0.8647518 Custom RNA
#> 3: 0.8738204 Custom RNA
#> 4: 0.8732681 Custom RNA
#> 5: 0.8594428 Custom RNA
#> 6: 0.8676799 Custom RNA
Each metric calculated using simulateBinding() can be visualized with built-in
plotting functions.
The plotBinding() function displays binding metrics across positions:
Note: The visualizations below show a zoomed-in view from positions 20-60. The
xlimparameter can be removed to show the entire sequence, or adjusted to view a specific segment of the target RNA.
plotBinding(res, rbp = c("HH", "HL", "LH", "LL"), metric = "density_fc",
transcript = "Custom RNA", xlim = c(20, 60))
Figure 4: Raw density_fc (positions 20-60)
For smoother visualization, apply a k-mer sliding window average:
plotBinding(res, rbp = c("HH", "HL", "LH", "LL"), metric = "density_fc",
transcript = "Custom RNA", window = 5, xlim = c(20, 60))
Figure 5: Density FC with 5-mer smoothing window (positions 20-60)
The plotHeatmap() function displays binding for multiple RBPs simultaneously:
plotHeatmap(res, transcript = "Custom RNA", xlim = c(20, 60),
xaxis_type = "both", metric = "occupancy_fc")
Figure 6: Heatmap of occupancy_fc (zoomed region)
Simulation results can be exported using the built-in exportResults() function
in JSON or CSV format:
# Export to JSON
tmp_json <- tempfile(fileext = ".json")
exportResults(res, output_file = tmp_json, format = "json")
# Export to CSV
tmp_csv <- tempfile(fileext = ".csv")
exportResults(res, output_file = tmp_csv, format = "csv")
# Clean up
unlink(tmp_json)
unlink(tmp_csv)
Results can also be converted to SummarizedExperiment format:
se <- makeSE(res, rbp_models = rbp_models)
se
#> class: SummarizedExperiment
#> dim: 100 4
#> metadata(0):
#> assays(4): occupancy density density_fc occupancy_fc
#> rownames: NULL
#> rowData names(3): pos nt transcript
#> colnames(4): HH HL LH LL
#> colData names(3): RBP Kd_min Kd_max
To run simulations across multiple combinations of RBP and RNA concentrations,
use the simulateGrid() function. RBPEqBind uses the future.apply framework
for parallel processing, running sequentially by default and automatically
utilizing parallel workers when the user configures a future::plan:
# Optional: enable parallel processing across 2 workers
library(future)
plan(multisession, workers = 2)
# Define concentration grids
prot_grid <- list(
HH = c(10, 100),
HL = c(10, 100),
LH = c(10, 100),
LL = c(10, 100)
)
rna_grid <- c(10)
res_grid <- simulateGrid(
sequence = seq,
rbp_models = rbp_models,
protein_conc_grid = prot_grid,
rna_conc_grid = rna_grid,
k = k_size
)
Grid sweep results can be visualized for specific concentration combinations using the same plotting functions:
# Filter to specific concentration combination
plotBinding(
results = res_grid,
rbp = c("HH", "HL", "LH", "LL"),
rna_conc = 10,
protein_conc = c(HH=10, HL=10, LH=100, LL=100),
metric = "density"
)
The plotGrid() function focuses on changes in binding for one RBP across
different target RNA and competitor RBP concentrations:
# First run a 2-RBP simulation for cleaner visualization
rbp_models_2rbp <- rbp_models[c("HH", "LL")]
res_grid_2rbp <- simulateGrid(
sequence = seq,
rbp_models = rbp_models_2rbp,
protein_conc_grid = list(HH = c(10, 50), LL = c(10, 50)),
rna_conc_grid = c(10),
k = k_size
)
plotGrid(
results = res_grid_2rbp,
rbp1 = "HH",
rbp2 = "LL",
rbp1_concs = c(10, 50),
roi_range = c(29, 33),
metric = "density"
)
Grid sweep results can also be exported in JSON/CSV or SummarizedExperiment format.
To simulate RBP binding to multiple RNA sequences (e.g., transcript sequences from bioMart), RBPEqBind provides functions to load FASTA files directly. Note that binding simulation is performed on a sequence-by-sequence basis, not simultaneously on all RNA sequences in the FASTA file.
# Load sample FASTA from package
fasta_file <- system.file("extdata", "test_transcripts.fa", package = "RBPEqBind")
res_fasta <- simulateBindingFasta(
fasta_file = fasta_file,
rbp_models = rbp_models,
protein_concs = c(HH = 100, HL = 100, LH = 100, LL = 100),
rna_conc = 10,
k = 5
)
head(res_fasta)
#> pos nt HH HL LH LL HH_density
#> <int> <char> <num> <num> <num> <num> <num>
#> 1: 1 U 0.1319377 0.2172656 0.07975064 0.5701071 0.0003151770
#> 2: 2 C 0.1482320 0.2344256 0.15800529 0.4585148 0.0003541014
#> 3: 3 A 0.1550461 0.2172857 0.18999212 0.4368384 0.0003703789
#> 4: 4 A 0.1428079 0.2068544 0.21404929 0.4354432 0.0003411440
#> 5: 5 C 0.1281987 0.2137216 0.22752862 0.4297183 0.0003062450
#> 6: 6 C 0.1152156 0.2109461 0.27011203 0.4028967 0.0002752306
#> HL_density LH_density LL_density HH_density_fc HH_occupancy_fc
#> <num> <num> <num> <num> <num>
#> 1: 0.0004226189 9.702996e-05 0.0005887760 0.2843468 0.1521557
#> 2: 0.0004559980 1.922398e-04 0.0004735295 0.3156638 0.1741968
#> 3: 0.0004226581 2.311571e-04 0.0004511433 0.3351971 0.1836785
#> 4: 0.0004023672 2.604267e-04 0.0004497024 0.3066473 0.1667641
#> 5: 0.0004157252 2.768266e-04 0.0004437900 0.2695008 0.1471909
#> 6: 0.0004103265 3.286364e-04 0.0004160901 0.2382839 0.1303410
#> HL_density_fc HL_occupancy_fc LH_density_fc LH_occupancy_fc LL_density_fc
#> <num> <num> <num> <num> <num>
#> 1: 0.4222039 0.2779059 0.07314338 0.08675049 0.7052680
#> 2: 0.4471135 0.3065380 0.14976277 0.18783935 0.4724244
#> 3: 0.4015070 0.2779028 0.18579071 0.23479871 0.4404861
#> 4: 0.3827428 0.2610807 0.21825655 0.27263762 0.4479384
#> 5: 0.4048503 0.2721024 0.23746442 0.29486427 0.4443246
#> 6: 0.4022978 0.2676220 0.29831365 0.37049436 0.4102670
#> LL_occupancy_fc
#> <num>
#> 1: 1.3290636
#> 2: 0.8480604
#> 3: 0.7768448
#> 4: 0.7724574
#> 5: 0.7546213
#> 6: 0.6756909
#> transcript
#> <char>
#> 1: PTBP2_hg19 range=chr1:97269727-97272451 5'pad=0 3'pad=0 strand=+ repeatMasking=none
#> 2: PTBP2_hg19 range=chr1:97269727-97272451 5'pad=0 3'pad=0 strand=+ repeatMasking=none
#> 3: PTBP2_hg19 range=chr1:97269727-97272451 5'pad=0 3'pad=0 strand=+ repeatMasking=none
#> 4: PTBP2_hg19 range=chr1:97269727-97272451 5'pad=0 3'pad=0 strand=+ repeatMasking=none
#> 5: PTBP2_hg19 range=chr1:97269727-97272451 5'pad=0 3'pad=0 strand=+ repeatMasking=none
#> 6: PTBP2_hg19 range=chr1:97269727-97272451 5'pad=0 3'pad=0 strand=+ repeatMasking=none
Visualization and result export are performed the same way as in Scenario 1.
RBPEqBind also supports concentration grid sweeps across sequences in a FASTA file:
fasta_file <- system.file("extdata", "test_transcripts.fa", package = "RBPEqBind")
res_grid_fasta <- simulateGridFasta(
fasta_file = fasta_file,
rbp_models = rbp_models,
protein_conc_grid = list(
HH = c(10, 100),
HL = c(10, 100),
LH = c(10, 100),
LL = c(10, 100)
),
rna_conc_grid = c(10),
k = 5
)
head(res_grid_fasta)
#> pos nt HH HL LH LL HH_density
#> <int> <char> <num> <num> <num> <num> <num>
#> 1: 1 U 0.1641753 0.2460096 0.1056294 0.4703919 0.0004374202
#> 2: 2 C 0.1806942 0.2488883 0.1745693 0.3821979 0.0004814321
#> 3: 3 A 0.1879711 0.2368540 0.2014866 0.3589431 0.0005008203
#> 4: 4 A 0.1727699 0.2264808 0.2225873 0.3641544 0.0004603191
#> 5: 5 C 0.1557077 0.2316158 0.2347091 0.3646240 0.0004148594
#> 6: 6 C 0.1457214 0.2304463 0.2711244 0.3381027 0.0003882525
#> HL_density LH_density LL_density HH_density_fc HH_occupancy_fc
#> <num> <num> <num> <num> <num>
#> 1: 0.0005991885 0.0002123307 0.0009017772 0.2553091 0.1997192
#> 2: 0.0006061998 0.0003509102 0.0007327026 0.2849026 0.2242822
#> 3: 0.0005768887 0.0004050178 0.0006881213 0.2998874 0.2357644
#> 4: 0.0005516236 0.0004474334 0.0006981117 0.2712276 0.2124509
#> 5: 0.0005641304 0.0004718000 0.0006990120 0.2391200 0.1873854
#> 6: 0.0005612820 0.0005450001 0.0006481687 0.2212957 0.1735453
#> HL_density_fc HL_occupancy_fc LH_density_fc LH_occupancy_fc LL_density_fc
#> <num> <num> <num> <num> <num>
#> 1: 0.3861925 0.3323571 0.1095399 0.1199547 0.7220344
#> 2: 0.3873370 0.3374933 0.1927724 0.2150450 0.5093369
#> 3: 0.3619218 0.3164801 0.2293640 0.2570742 0.4640918
#> 4: 0.3435058 0.2981927 0.2616486 0.2915717 0.4783631
#> 5: 0.3557676 0.3067593 0.2811677 0.3121350 0.4818148
#> 6: 0.3549225 0.3052477 0.3411148 0.3795823 0.4336926
#> LL_occupancy_fc rna_conc Conc_HH Conc_HL Conc_LH Conc_LL
#> <num> <num> <num> <num> <num> <num>
#> 1: 0.9119404 10 10 10 10 10
#> 2: 0.6326191 10 10 10 10 10
#> 3: 0.5731062 10 10 10 10 10
#> 4: 0.5856097 10 10 10 10 10
#> 5: 0.5861815 10 10 10 10 10
#> 6: 0.5223341 10 10 10 10 10
#> transcript
#> <char>
#> 1: PTBP2_hg19 range=chr1:97269727-97272451 5'pad=0 3'pad=0 strand=+ repeatMasking=none
#> 2: PTBP2_hg19 range=chr1:97269727-97272451 5'pad=0 3'pad=0 strand=+ repeatMasking=none
#> 3: PTBP2_hg19 range=chr1:97269727-97272451 5'pad=0 3'pad=0 strand=+ repeatMasking=none
#> 4: PTBP2_hg19 range=chr1:97269727-97272451 5'pad=0 3'pad=0 strand=+ repeatMasking=none
#> 5: PTBP2_hg19 range=chr1:97269727-97272451 5'pad=0 3'pad=0 strand=+ repeatMasking=none
#> 6: PTBP2_hg19 range=chr1:97269727-97272451 5'pad=0 3'pad=0 strand=+ repeatMasking=none
Visualization and export functions work the same as in Scenario 2.
sessionInfo()
#> R version 4.6.1 (2026-06-24)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Ubuntu 24.04.5 LTS
#>
#> Matrix products: default
#> BLAS: /home/biocbuild/bbs-3.24-bioc/R/lib/libRblas.so
#> LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.12.0 LAPACK version 3.12.0
#>
#> locale:
#> [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
#> [3] LC_TIME=en_GB LC_COLLATE=C
#> [5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
#> [7] LC_PAPER=en_US.UTF-8 LC_NAME=C
#> [9] LC_ADDRESS=C LC_TELEPHONE=C
#> [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
#>
#> time zone: America/New_York
#> tzcode source: system (glibc)
#>
#> attached base packages:
#> [1] stats4 stats graphics grDevices utils datasets methods
#> [8] base
#>
#> other attached packages:
#> [1] Biostrings_2.81.9 Seqinfo_1.3.2 XVector_0.53.0
#> [4] IRanges_2.47.5 S4Vectors_0.51.10 BiocGenerics_0.59.12
#> [7] generics_0.1.4 future_1.75.0 ggplot2_4.0.3
#> [10] data.table_1.18.6.1 RBPEqBind_0.99.4 BiocStyle_2.41.0
#>
#> loaded via a namespace (and not attached):
#> [1] SummarizedExperiment_1.43.0 gtable_0.3.6
#> [3] rjson_0.2.23 xfun_0.61
#> [5] bslib_0.12.0 Biobase_2.73.2
#> [7] lattice_0.23-1 vctrs_0.7.3
#> [9] tools_4.6.1 bitops_1.1-0
#> [11] curl_8.0.0 parallel_4.6.1
#> [13] tibble_3.3.1 pkgconfig_2.0.3
#> [15] BiocBaseUtils_1.15.1 Matrix_1.7-6
#> [17] RColorBrewer_1.1-3 S7_0.2.2
#> [19] cigarillo_1.3.1 lifecycle_1.0.5
#> [21] compiler_4.6.1 farver_2.1.2
#> [23] Rsamtools_2.29.0 tinytex_0.61
#> [25] codetools_0.2-20 htmltools_0.5.9
#> [27] sass_0.4.10 RCurl_1.98-1.20
#> [29] yaml_2.3.12 pillar_1.11.1
#> [31] crayon_1.5.3 jquerylib_0.1.4
#> [33] BiocParallel_1.47.0 DelayedArray_0.39.6
#> [35] cachem_1.1.0 magick_2.9.1
#> [37] abind_1.4-8 parallelly_1.48.0
#> [39] tidyselect_1.2.1 digest_0.6.39
#> [41] dplyr_1.2.1 restfulr_0.0.17
#> [43] bookdown_0.48 listenv_1.0.0
#> [45] labeling_0.4.3 fastmap_1.2.0
#> [47] grid_4.6.1 cli_3.6.6
#> [49] SparseArray_1.13.2 magrittr_2.0.5
#> [51] S4Arrays_1.13.0 dichromat_2.0-1
#> [53] XML_3.99-0.24 future.apply_1.20.2
#> [55] withr_3.0.3 scales_1.4.0
#> [57] rmarkdown_2.32 httr_1.4.9
#> [59] matrixStats_1.5.0 globals_0.19.1
#> [61] otel_0.2.0 evaluate_1.0.5
#> [63] knitr_1.52 GenomicRanges_1.65.4
#> [65] BiocIO_1.23.3 viridisLite_0.4.3
#> [67] rtracklayer_1.73.0 rlang_1.3.0
#> [69] Rcpp_1.1.2 glue_1.8.1
#> [71] BiocManager_1.30.27 jsonlite_2.0.0
#> [73] R6_2.6.1 MatrixGenerics_1.25.0
#> [75] GenomicAlignments_1.49.2