Introduction to RBPSpecificity

Overview

RBPSpecificity provides tools to analyze RNA-Binding Protein (RBP) binding specificities from high-throughput sequencing data. This package provides tools for:

  • Calculating Specificity from k-mer enrichment data
  • Calculating Sensitivity for top motifs
  • Visualizing specificity distributions and sensitivity profiles
  • Computing enrichment scores from CLIP peak data

Introduction

Background

The package implements two key metrics for characterizing RBP binding behavior:

  1. Specificity: Quantifies how specifically an RBP binds its top motif relative to all other motifs. Higher specificity indicates more specific binding.
  2. Sensitivity: Measures how much the binding affinity changes upon single nucleotide variations in the motif. Higher sensitivity indicates greater sensitivity to sequence changes.

These metrics enable quantitative comparison of RBP binding properties across different experimental conditions or between different RBPs.

Reference

For a detailed description of these metrics and their 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

Workflow Integration

RBPSpecificity is designed to seamlessly integrate into standard Bioconductor sequence and genomic analysis workflows:

  • Input Integration: Raw peak files from CLIP-seq or similar experiments can be imported as GRanges objects using rtracklayer::import() and standard annotation packages (e.g. GenomicFeatures).
  • Sequence Retrieval: The motifEnrichment() function interfaces directly with BSgenome genome packages (e.g., BSgenome.Hsapiens.UCSC.hg38) to extract genomic sequences for enrichment calculations.
  • Downstream Analysis: The Specificity and Sensitivity scores and matrices returned by the package can be integrated with other downstream analyses such as differential binding studies (e.g., DiffBind, DESeq2), regulatory network modeling, or motif annotation pipelines.

Sample Data

The sample data included in this package were obtained from the ENCODE portal.

RBNS Data

RNA Bind-n-Seq (RBNS) datasets provide in vitro RNA-RBP affinity measurements. For each K-mer enrichment dataset, the RBP concentration generating the highest R-value was selected as the representative sample. The collected RBNS R scores were feature-scaled to [1, e] followed by natural log transformation to normalize to a 0-to-1 scale.

eCLIP Data

eCLIP datasets provide in vivo RBP binding site information. Peak coordinates were extended 25 nucleotides upstream from the 5’-end. Motif enrichment was calculated by counting K-mer appearances across extended peaks and subtracting background counts from randomly shifted genomic regions (up to 500 nt away). Background generation was repeated 100 times for robust averaging.

Setup

if (!requireNamespace("BiocManager", quietly = TRUE)) {
    install.packages("BiocManager")
}

BiocManager::install("RBPSpecificity")
library(RBPSpecificity)
library(ggplot2)

In Vitro Specificity from Pre-computed Enrichment Data (RBNS)

This workflow demonstrates how to calculate specificity and sensitivity from pre-computed enrichment scores, such as those from RBNS (RNA Bind-n-Seq) experiments.

Loading RBNS Data

The sample data contains normalized 5-mer enrichment scores for four RBPs: HNRNPC, PCBP2 (structural context independent), RBFOX2, and EIF4G2 (structural context dependent).

# Load sample RBNS data from package
rbns_file <- system.file("extdata", "RBNS_normalized_5mer.csv",
    package = "RBPSpecificity"
)
rbns_data <- read.csv(rbns_file, stringsAsFactors = FALSE)

# Preview the data
head(rbns_data)
#>   Motif    EIF4G2     HNRNPC     RBFOX2      PCBP2
#> 1 AAAAA 0.4667673 0.02115767 0.05019945 0.02791232
#> 2 AAAAC 0.3879612 0.02118148 0.02586520 0.05477091
#> 3 AAAAG 0.4692119 0.01456301 0.05103704 0.01947033
#> 4 AAAAU 0.4591129 0.02351263 0.04702782 0.02892464
#> 5 AAACA 0.3666514 0.02098727 0.02119075 0.02849734
#> 6 AAACC 0.3428551 0.03361433 0.01435467 0.18305568

Calculating Specificity and Sensitivity for All Four RBPs

The package expects a data.frame with MOTIF and Score columns:

rbps <- c("HNRNPC", "PCBP2", "RBFOX2", "EIF4G2")

rbns_metrics <- lapply(rbps, function(rbp) {
    rbns_enrichment <- data.frame(
        MOTIF = rbns_data$Motif,
        Score = rbns_data[[rbp]]
    )
    is_val <- returnSpecificity(rbns_enrichment)
    vs_val <- returnSensitivity(rbns_enrichment, output_type = "number")
    list(
        enrichment = rbns_enrichment,
        Specificity = is_val,
        Sensitivity = vs_val
    )
})
names(rbns_metrics) <- rbps

# Display in vitro metrics
invitro_summary <- data.frame(
    RBP = rbps,
    Specificity = sapply(rbns_metrics, function(x) round(x$Specificity, 2)),
    Sensitivity = sapply(rbns_metrics, function(x) round(x$Sensitivity, 4))
)
print(invitro_summary)
#>           RBP Specificity Sensitivity
#> HNRNPC HNRNPC       50.84      0.8580
#> PCBP2   PCBP2       21.57      0.4932
#> RBFOX2 RBFOX2       31.51      0.9027
#> EIF4G2 EIF4G2        2.04      0.4359

Visualization

Specificity Distribution Plots

The plotSpecificity() function displays the distribution of k-mer scores, highlighting the top motif and its specificity value:

plotSpecificity(rbns_metrics[["HNRNPC"]]$enrichment)
Distribution of k-mer scores from RBNS data for four RBPs.

Distribution of k-mer scores from RBNS data for four RBPs.

plotSpecificity(rbns_metrics[["PCBP2"]]$enrichment)
Distribution of k-mer scores from RBNS data for four RBPs.

Distribution of k-mer scores from RBNS data for four RBPs.

plotSpecificity(rbns_metrics[["RBFOX2"]]$enrichment)
Distribution of k-mer scores from RBNS data for four RBPs.

Distribution of k-mer scores from RBNS data for four RBPs.

plotSpecificity(rbns_metrics[["EIF4G2"]]$enrichment)
Distribution of k-mer scores from RBNS data for four RBPs.

Distribution of k-mer scores from RBNS data for four RBPs.

Sensitivity Profile Plots

The plotSensitivity() function visualizes how sensitive binding is to mutations at each position:

plotSensitivity(rbns_metrics[["HNRNPC"]]$enrichment)
Sensitivity profiles from RBNS data for four RBPs.

Sensitivity profiles from RBNS data for four RBPs.

plotSensitivity(rbns_metrics[["PCBP2"]]$enrichment)
Sensitivity profiles from RBNS data for four RBPs.

Sensitivity profiles from RBNS data for four RBPs.

plotSensitivity(rbns_metrics[["RBFOX2"]]$enrichment)
Sensitivity profiles from RBNS data for four RBPs.

Sensitivity profiles from RBNS data for four RBPs.

plotSensitivity(rbns_metrics[["EIF4G2"]]$enrichment)
Sensitivity profiles from RBNS data for four RBPs.

Sensitivity profiles from RBNS data for four RBPs.

Cellular Specificity from De Novo Enrichment (eCLIP)

This workflow demonstrates how to calculate enrichment scores directly from peak data using the motifEnrichment() function.

Note: While this example uses eCLIP-derived peaks, motifEnrichment() works with any peak data in BED format (e.g., iCLIP, PAR-CLIP, or other ChIP-seq derived peaks).

Requirement: This workflow requires BSgenome.Hsapiens.UCSC.hg38 to be installed.

When calculating enrichment from cellular peak data, the resulting metrics are referred to as Cellular Specificity and Cellular Sensitivity, as they reflect in vivo binding behavior rather than intrinsic biochemical affinity.

Loading Peak Data

# Load eCLIP peaks (10-column narrowPeak format) from package
bed_cols <- c(
    "chr", "start", "end", "name", "score", "strand",
    "signalValue", "pValue", "qValue", "peak"
)

rbp_names <- c("HNRNPC", "PCBP2", "RBFOX2", "EIF4G2")
cell_lines <- c("K562", "HepG2", "K562", "K562")

peak_data <- lapply(seq_along(rbp_names), function(i) {
    bed_file <- system.file("extdata",
        sprintf("ENCODE_eCLIP_%s_%s_narrowPeak.bed", rbp_names[i], cell_lines[i]),
        package = "RBPSpecificity"
    )
    peaks <- read.table(bed_file, header = FALSE, sep = "\t")
    colnames(peaks) <- bed_cols
    message("Loaded ", nrow(peaks), " ", rbp_names[i], " peaks.")
    peaks
})
names(peak_data) <- rbp_names

Running motifEnrichment Pipeline

In addition to standard input variables, motifEnrichment() provides several parameters for customization:

Parameter Description
extension Numeric vector of length 2: c(five_prime, three_prime) indicating shifting of 5’ and 3’ ends. Positive extends, negative trims.
method Enrichment model: "anr" (default, any number of repetitions, conditional Binomial test) or "zoops" (zero-or-one occurrence per sequence, Hypergeometric test)
bkg_iter Number of background iterations for averaging (higher = more robust)
bkg_min_dist Minimum distance (bp) to shift peaks when generating background regions
bkg_max_dist Maximum distance (bp) for shifting peaks in background generation
scramble_bkg Logical, scramble background sequences to control for nucleotide composition (default: FALSE)
nucleic_acid_type Character string, "DNA" or "RNA" (default: "DNA")

ZOOPS method can also be used instead. Note that ANR and ZOOPS calculation can result in different motif enrichment values and thus different specificity and sensitivity calculations. For more information on ANR vs. ZOOPS, please check the GitHub README and Further Reading section. In addition, the scramble background option can be turned on (scramble_bkg = TRUE) to control for nucleotide composition.

# NOTE: This requires BSgenome.Hsapiens.UCSC.hg38

cellular_results <- lapply(rbp_names, function(rbp) {
    message("Running motifEnrichment for ", rbp, "...")
    enrichment <- motifEnrichment(
        coordinates = peak_data[[rbp]],
        species_or_build = "hg38",
        K = 5,
        extension = c(25, 0),
        method = "anr",
        bkg_iter = 100,
        scramble_bkg = FALSE
    )

    cs_val <- returnSpecificity(enrichment)
    cvs_val <- returnSensitivity(enrichment, output_type = "number")

    list(
        enrichment = enrichment,
        Specificity = cs_val,
        Sensitivity = cvs_val
    )
})
#>   |                                                                              |                                                                      |   0%  |                                                                              |======================================================================| 100%
#>   |                                                                              |                                                                      |   0%  |                                                                              |====================                                                  |  28%  |                                                                              |=======================================                               |  56%  |                                                                              |===========================================================           |  84%  |                                                                              |======================================================================| 100%
#>   |                                                                              |                                                                      |   0%  |                                                                              |================================================                      |  69%  |                                                                              |======================================================================| 100%
#>   |                                                                              |                                                                      |   0%  |                                                                              |====================================                                  |  52%  |                                                                              |======================================================================| 100%
names(cellular_results) <- rbp_names

# Display cellular metrics
cellular_summary <- data.frame(
    RBP = rbp_names,
    Specificity = sapply(cellular_results, function(x) round(x$Specificity, 2)),
    Sensitivity = sapply(cellular_results, function(x) round(x$Sensitivity, 4))
)
print(cellular_summary)
#>           RBP Specificity Sensitivity
#> HNRNPC HNRNPC       35.28      0.9420
#> PCBP2   PCBP2        6.89      0.6570
#> RBFOX2 RBFOX2        3.00      0.4825
#> EIF4G2 EIF4G2       16.50      0.8937

Visualization

The same plotSpecificity() and plotSensitivity() functions used for RBNS data can be applied to visualize cellular specificity and cellular sensitivity from eCLIP enrichment results:

# Specificity Distribution plots
plotSpecificity(cellular_results[["HNRNPC"]]$enrichment)

plotSpecificity(cellular_results[["PCBP2"]]$enrichment)

plotSpecificity(cellular_results[["RBFOX2"]]$enrichment)

plotSpecificity(cellular_results[["EIF4G2"]]$enrichment)


# Sensitivity Profile plots
plotSensitivity(cellular_results[["HNRNPC"]]$enrichment)

plotSensitivity(cellular_results[["PCBP2"]]$enrichment)

plotSensitivity(cellular_results[["RBFOX2"]]$enrichment)

plotSensitivity(cellular_results[["EIF4G2"]]$enrichment)

Comparing In Vitro (RBNS) vs. Cellular (eCLIP) Specificity

A key application is comparing specificity and sensitivity across in vitro and cellular contexts. RBPs that bind directly to single-stranded sequence motifs (e.g., HNRNPC, PCBP2) are expected to show correlated specificity and sensitivity values. RBPs whose binding depends on structural context (e.g., RBFOX2, EIF4G2) may show divergence between in vitro and cellular metrics.

comparison <- data.frame(
    RBP = rbp_names,
    Structural_Context = c("Independent", "Independent", "Dependent", "Dependent"),
    In_Vitro_Specificity = sapply(rbns_metrics[rbp_names], function(x) round(x$Specificity, 2)),
    Cellular_Specificity = sapply(cellular_results, function(x) round(x$Specificity, 2)),
    In_Vitro_Sensitivity = sapply(rbns_metrics[rbp_names], function(x) round(x$Sensitivity, 4)),
    Cellular_Sensitivity = sapply(cellular_results, function(x) round(x$Sensitivity, 4))
)
print(comparison)
#>           RBP Structural_Context In_Vitro_Specificity Cellular_Specificity
#> HNRNPC HNRNPC        Independent                50.84                35.28
#> PCBP2   PCBP2        Independent                21.57                 6.89
#> RBFOX2 RBFOX2          Dependent                31.51                 3.00
#> EIF4G2 EIF4G2          Dependent                 2.04                16.50
#>        In_Vitro_Sensitivity Cellular_Sensitivity
#> HNRNPC               0.8580               0.9420
#> PCBP2                0.4932               0.6570
#> RBFOX2               0.9027               0.4825
#> EIF4G2               0.4359               0.8937

Session Info

sessionInfo()
#> R version 4.6.1 (2026-06-24)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Ubuntu 26.04 LTS
#> 
#> Matrix products: default
#> BLAS:   /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3 
#> LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.32.so;  LAPACK version 3.12.0
#> 
#> locale:
#>  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
#>  [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
#>  [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: Etc/UTC
#> tzcode source: system (glibc)
#> 
#> attached base packages:
#> [1] stats4    stats     graphics  grDevices utils     datasets  methods  
#> [8] base     
#> 
#> other attached packages:
#>  [1] BSgenome.Hsapiens.UCSC.hg38_1.4.5 BSgenome_1.81.0                  
#>  [3] rtracklayer_1.73.0                BiocIO_1.23.3                    
#>  [5] Biostrings_2.81.5                 XVector_0.53.0                   
#>  [7] GenomicRanges_1.65.1              GenomeInfoDb_1.49.1              
#>  [9] Seqinfo_1.3.0                     IRanges_2.47.2                   
#> [11] S4Vectors_0.51.5                  BiocGenerics_0.59.10             
#> [13] generics_0.1.4                    ggplot2_4.0.3                    
#> [15] RBPSpecificity_0.99.6             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.60                  
#>  [5] bslib_0.11.0                Biobase_2.73.1             
#>  [7] lattice_0.22-9              vctrs_0.7.3                
#>  [9] tools_4.6.1                 bitops_1.0-9               
#> [11] curl_7.1.0                  parallel_4.6.1             
#> [13] BiocBaseUtils_1.15.1        Matrix_1.7-5               
#> [15] RColorBrewer_1.1-3          S7_0.2.2                   
#> [17] cigarillo_1.3.1             lifecycle_1.0.5            
#> [19] stringr_1.6.0               compiler_4.6.1             
#> [21] farver_2.1.2                Rsamtools_2.29.0           
#> [23] codetools_0.2-20            htmltools_0.5.9            
#> [25] sys_3.4.3                   buildtools_1.0.0           
#> [27] sass_0.4.10                 RCurl_1.98-1.19            
#> [29] yaml_2.3.12                 crayon_1.5.3               
#> [31] jquerylib_0.1.4             BiocParallel_1.47.0        
#> [33] cachem_1.1.0                DelayedArray_0.39.3        
#> [35] abind_1.4-8                 digest_0.6.39              
#> [37] stringi_1.8.7               reshape2_1.4.5             
#> [39] restfulr_0.0.17             labeling_0.4.3             
#> [41] maketools_1.3.2             fastmap_1.2.0              
#> [43] grid_4.6.1                  cli_3.6.6                  
#> [45] SparseArray_1.13.2          magrittr_2.0.5             
#> [47] S4Arrays_1.13.0             XML_3.99-0.23              
#> [49] withr_3.0.3                 UCSC.utils_1.9.0           
#> [51] scales_1.4.0                rmarkdown_2.31             
#> [53] httr_1.4.8                  matrixStats_1.5.0          
#> [55] otel_0.2.0                  evaluate_1.0.5             
#> [57] knitr_1.51                  rlang_1.3.0                
#> [59] Rcpp_1.1.2                  glue_1.8.1                 
#> [61] BiocManager_1.30.27         jsonlite_2.0.0             
#> [63] plyr_1.8.9                  R6_2.6.1                   
#> [65] MatrixGenerics_1.25.0       GenomicAlignments_1.49.1