simPIC: simulating single-cell ATAC-seq data

Introduction to simPIC

simPIC is an R package for the simple simulation of single-cell RNA sequencing data. This vignette gives an overview and introduction to simPIC’s functionality.

Installation

simPIC can be installed from Bioconductor:

BiocManager::install("simPIC")

Quick start

Assuming you already have a matrix of count data similar to that you wish to simulate there are two simple steps to creating a simulated data set with simPIC.

# Load package
suppressPackageStartupMessages({
    library(simPIC)
})
#> Warning: multiple methods tables found for 'setequal'
#> Warning: replacing previous import 'BiocGenerics::setequal' by
#> 'S4Vectors::setequal' when loading 'IRanges'
#> Warning: replacing previous import 'BiocGenerics::setequal' by
#> 'S4Vectors::setequal' when loading 'GenomeInfoDb'
#> Warning: replacing previous import 'BiocGenerics::setequal' by
#> 'S4Vectors::setequal' when loading 'GenomicRanges'
#> Warning: replacing previous import 'BiocGenerics::setequal' by
#> 'S4Vectors::setequal' when loading 'XVector'
#> Warning: replacing previous import 'BiocGenerics::setequal' by
#> 'S4Vectors::setequal' when loading 'SummarizedExperiment'
#> Warning: replacing previous import 'BiocGenerics::setequal' by
#> 'S4Vectors::setequal' when loading 'S4Arrays'
#> Warning: replacing previous import 'BiocGenerics::setequal' by
#> 'S4Vectors::setequal' when loading 'DelayedArray'
#> Warning: replacing previous import 'BiocGenerics::setequal' by
#> 'S4Vectors::setequal' when loading 'SparseArray'
#> Warning: replacing previous import 'S4Arrays::read_block' by
#> 'DelayedArray::read_block' when loading 'SummarizedExperiment'

# Load test data
set.seed(12)
counts <- readRDS(system.file("extdata", "test.rds", package = "simPIC"))

# Estimate parameters
est <- simPICestimate(counts)
#> simPIC is:
#> estimating library size parameters...
#> estimating sparsity...
#> estimating peak mean parameters...
#> using weibull distribution for estimating peak mean

# Simulate data using estimated parameters
sim <- simPICsimulate(est)
#> simPIC is:
#> updating parameters...
#> setting up SingleCellExperiment object...
#> Simulating library size...
#> Simulating peak mean...
#> using weibull distribution for simulating peak mean
#> Simulating true counts...
#> Done!!

These steps will be explained in detail in the following sections but briefly the first step takes a dataset and estimates simulation parameters from it and the second step takes those parameters and simulates a new dataset.

Input data

simPIC recommends to use a Paired-Insertion Count (PIC) matrix for optimal use of the quantitative information present in scATAC-seq data. You can convert your own matrix to PIC by following the PICsnATAC vignette. Briefly, you need three input files for PIC -

  1. cell barcodes with metadata (singlecell.csv)
  2. list of peak regions (peaks.bed)
  3. fragment files (fragment.tsv.gz)
pic_mat <- PIC_counting(
    cells = cells,
    fragment_tsv_gz_file_location = fragment_tsv_gz_file_location,
    peak_sets = peak_sets
)

The simPIC simulation

The core of the simPIC model is a gamma-Poisson distribution. This model is used to generate a uniform quantification based peak-by-cell count matrix. Mean chromatin accessibility signals for each peak are simulated from a weibull distribution with default settings. Users have the flexibility to choose from gamma, lognormal or pareto distribution as well. Each cell is given an expected library size, simulated from a log-normal distribution to match to a given dataset. Sparsity is imposed on counts simulated from a Poisson distribution.

The simPICcount class

All the parameters for the simPIC simulation are stored in a simPICcount object. A class specifically desgined for storing simPIC scATAC-seq simulation parameters. Let’s create a new one and see what it looks like.

sim.params <- newsimPICcount()

we can see the default values for the simPICcount object parameters. These values are based on provided test data.

sim.params
#> An object of class "simPICcount"
#> Slot "nPeaks":
#> [1] 5000
#> 
#> Slot "nCells":
#> [1] 700
#> 
#> Slot "seed":
#> [1] 53238
#> 
#> Slot "default":
#> [1] TRUE
#> 
#> Slot "pm.distr":
#> [1] "weibull"
#> 
#> Slot "lib.size.meanlog":
#> [1] 6.687082
#> 
#> Slot "lib.size.sdlog":
#> [1] 0.344361
#> 
#> Slot "peak.mean.shape":
#> [1] 0.7909301
#> 
#> Slot "peak.mean.rate":
#> [1] 7.100648
#> 
#> Slot "peak.mean.scale":
#> [1] 0.09522228
#> 
#> Slot "peak.mean.pi":
#> [1] -17.17441
#> 
#> Slot "peak.mean.meanlog":
#> [1] -2.825233
#> 
#> Slot "peak.mean.sdlog":
#> [1] -1.366378
#> 
#> Slot "sparsity":
#>  [1] 0.84925808 0.84475826 0.38794330 0.62483433 0.97191761 0.62420859
#>  [7] 0.66181149 0.32323586 0.37953361 0.14664756 0.44963537 0.07493546
#> [13] 0.98509483 0.76025096 0.22400472 0.58124180 0.21459479 0.81719401
#> [19] 0.90687172 0.49426057 0.15138319 0.59815102 0.71652090 0.17266414
#> [25] 0.24371900 0.41602956 0.83637913 0.10737761 0.73584226 0.88013537
#>  [ reached getOption("max.print") -- omitted 199970 entries ]

Getting and setting parameters

To get a particular parameter, for e.g., number of peaks, we can use simPICget function:

simPICget(sim.params, "nPeaks")
#> [1] 5000

Alternatively, to give a parameter a new value we can use the setsimPICparameters function:

sim.params <- setsimPICparameters(sim.params, nPeaks = 2000)
simPICget(sim.params, "nPeaks")
#> [1] 2000

To get or set multiple parameters use simPICget or setsimPICparameters functions:

# Set multiple parameters at once (using a list)
sim.params <- setsimPICparameters(sim.params,
    update = list(nPeaks = 8000, nCells = 500)
)
# Extract multiple parameters as a list
params <- simPICgetparameters(
    sim.params,
    c("nPeaks", "nCells", "peak.mean.shape")
)
# Set multiple parameters at once (using additional arguments)
params <- setsimPICparameters(sim.params,
    lib.size.sdlog = 3.5, lib.size.meanlog = 9.07
)
params
#> An object of class "simPICcount"
#> Slot "nPeaks":
#> [1] 8000
#> 
#> Slot "nCells":
#> [1] 500
#> 
#> Slot "seed":
#> [1] 53238
#> 
#> Slot "default":
#> [1] TRUE
#> 
#> Slot "pm.distr":
#> [1] "weibull"
#> 
#> Slot "lib.size.meanlog":
#> [1] 9.07
#> 
#> Slot "lib.size.sdlog":
#> [1] 3.5
#> 
#> Slot "peak.mean.shape":
#> [1] 0.7909301
#> 
#> Slot "peak.mean.rate":
#> [1] 7.100648
#> 
#> Slot "peak.mean.scale":
#> [1] 0.09522228
#> 
#> Slot "peak.mean.pi":
#> [1] -17.17441
#> 
#> Slot "peak.mean.meanlog":
#> [1] -2.825233
#> 
#> Slot "peak.mean.sdlog":
#> [1] -1.366378
#> 
#> Slot "sparsity":
#>  [1] 0.84925808 0.84475826 0.38794330 0.62483433 0.97191761 0.62420859
#>  [7] 0.66181149 0.32323586 0.37953361 0.14664756 0.44963537 0.07493546
#> [13] 0.98509483 0.76025096 0.22400472 0.58124180 0.21459479 0.81719401
#> [19] 0.90687172 0.49426057 0.15138319 0.59815102 0.71652090 0.17266414
#> [25] 0.24371900 0.41602956 0.83637913 0.10737761 0.73584226 0.88013537
#>  [ reached getOption("max.print") -- omitted 199970 entries ]

Estimating parameters

simPIC allows you to estimate many of it’s parameters from a SingleCellExperiment object containing counts or a counts matrix using the simPICestimate function.

# Get the counts from test data
count <- readRDS(system.file("extdata", "test.rds", package = "simPIC"))

# Check that counts is a dgCMatrix
class(count)
#> [1] "dgCMatrix"
#> attr(,"package")
#> [1] "Matrix"
typeof(count)
#> [1] "S4"

# Check the dimensions, each row is a peak, each column is a cell
dim(count)
#> [1] 5000  700

# Show the first few entries
count[1:5, 1:5]
#> 5 x 5 sparse Matrix of class "dgCMatrix"
#>                          TTGCCCACACTCAAGT-1 TATGTGGGTCCAAGAG-1
#> chr22-29837913-29838812                   1                  2
#> chr12-90076794-90077665                   .                  .
#> chr11-63574191-63575102                   .                  .
#> chr12-54977628-54978479                   .                  .
#> chr1-221715272-221716182                  .                  .
#>                          CGATGATCAAGTAACA-1 GTCGTAATCAGGAAGC-1
#> chr22-29837913-29838812                   .                  .
#> chr12-90076794-90077665                   .                  .
#> chr11-63574191-63575102                   .                  .
#> chr12-54977628-54978479                   .                  .
#> chr1-221715272-221716182                  .                  .
#>                          TACTAGGCACAAACAA-1
#> chr22-29837913-29838812                   .
#> chr12-90076794-90077665                   .
#> chr11-63574191-63575102                   .
#> chr12-54977628-54978479                   .
#> chr1-221715272-221716182                  1

new <- newsimPICcount()
new <- simPICestimate(count)
#> simPIC is:
#> estimating library size parameters...
#> estimating sparsity...
#> estimating peak mean parameters...
#> using weibull distribution for estimating peak mean

## estimating using gamma distribution
## new <- simPICestimate(count, pm.distr = "gamma")

Here we estimated parameters from a counts matrix using default parameters. The estimation process has the following steps:

  1. Mean parameters are estimated by fitting a weibull distribution (default) to the peak means.
  2. Library size parameters are estimated by fitting a log-normal distribution to the library sizes.
  3. Sparsity parameter is estimated by fitting a Bernoulli distribution.

For more details of the estimation procedures see ?simPICestimate.

Simulating counts

Once we have a set of parameters we are happy with we can use simPICsimulate to simulate counts. To make adjustments to the parameters provide them as additional arguments. Alternatively if we don’t supply any parameters defaults will be used:

sim <- simPICsimulate(new, nCells = 1000)
#> simPIC is:
#> updating parameters...
#> setting up SingleCellExperiment object...
#> Simulating library size...
#> Simulating peak mean...
#> using weibull distribution for simulating peak mean
#> Simulating true counts...
#> Done!!
sim
#> class: SingleCellExperiment 
#> dim: 5000 1000 
#> metadata(1): Params
#> assays(1): counts
#> rownames(5000): Peak1 Peak2 ... Peak4999 Peak5000
#> rowData names(2): Peak exp.peakmean
#> colnames(1000): Cell1 Cell2 ... Cell999 Cell1000
#> colData names(2): Cell exp.libsize
#> reducedDimNames(0):
#> mainExpName: NULL
#> altExpNames(0):

## simulating using gamma distribution
## sim <- simPICsimulate(new, nCells =1000, pm.distr = "gamma")

Looking at the output of simPICsimulate we can see that sim is SingleCellExperiment object with 5000 features (peaks) and 1000 samples (cells). The main part of this object is a features by samples matrix containing the simulated counts (accessed using counts). Additionally a SingleCellExperiment contains information about each cell (accessed using colData) and each peak (accessed using rowData). simPIC uses these slots, as well as assays, to store information about the intermediate values of the simulation.

# Access the counts
counts(sim)[1:5, 1:5]
#>       Cell1 Cell2 Cell3 Cell4 Cell5
#> Peak1     0     0     0     0     0
#> Peak2     0     0     1     1     0
#> Peak3     1     0     1     1     0
#> Peak4     0     0     0     0     0
#> Peak5     0     0     0     0     0
# Information about peaks
head(rowData(sim))
#> DataFrame with 6 rows and 2 columns
#>              Peak exp.peakmean
#>       <character>    <numeric>
#> Peak1       Peak1  6.75102e-06
#> Peak2       Peak2  2.68961e-04
#> Peak3       Peak3  1.02505e-04
#> Peak4       Peak4  3.80896e-06
#> Peak5       Peak5  4.83026e-05
#> Peak6       Peak6  4.76012e-05
# Information about cells
head(colData(sim))
#> DataFrame with 6 rows and 2 columns
#>              Cell exp.libsize
#>       <character>   <numeric>
#> Cell1       Cell1        1467
#> Cell2       Cell2         575
#> Cell3       Cell3        1122
#> Cell4       Cell4        1105
#> Cell5       Cell5         925
#> Cell6       Cell6         887
# Peak by cell matrices
names(assays(sim))
#> [1] "counts"

For more details about the SingleCellExperiment object refer to thevignette.

The simPICsimulate function provides additional simulation details:

* Cell information (colData)

* Cell - Unique cell identifier.

* exp.libsize - The expected library size for that cell. (not obtained from the final simulated counts)

* Peak information (rowData)

* Peak - Unique peak identifier.

* exp.peakmean - The expected peak means for that peak. (not obtained from the final simulated counts)

* Peak by cell information (assays)

* counts - The final simulated counts.

For more information on the simulation see ?simPICsimulate.

Comparing simulations and real data

simPIC provides a function simPICcompare that aims to make these comparisons easier. This function takes a list of SingleCellExperiment objects, combines the datasets and produces comparison plots. Let’s make two small simulations and see how they compare.

sim1 <- simPICsimulate(nPeaks = 20000, nCells = 1000)
#> simPIC is:
#> updating parameters...
#> setting up SingleCellExperiment object...
#> Simulating library size...
#> Simulating peak mean...
#> using weibull distribution for simulating peak mean
#> Simulating true counts...
#> Done!!
sim2 <- simPICsimulate(nPeaks = 20000, nCells = 1000)
#> simPIC is:
#> updating parameters...
#> setting up SingleCellExperiment object...
#> Simulating library size...
#> Simulating peak mean...
#> using weibull distribution for simulating peak mean
#> Simulating true counts...
#> Done!!
comparison <- simPICcompare(list(real = sim1, simPIC = sim2))

names(comparison)
#> [1] "RowData" "ColData" "Plots"
names(comparison$Plots)
#> [1] "Means"        "Variances"    "MeanVar"      "LibrarySizes" "ZerosPeak"   
#> [6] "ZerosCell"    "MeanZeros"    "PeakMeanNzp"

The returned list has three items. The first two are the combined datasets by peak (RowData) and by cell (ColData) and the third contains some comparison plots (produced using ggplot2), for example a plot of the distribution of means:

comparison$Plots$Means

These are only a few of the plots you might want to consider but it should be easy to make more using the returned data.

Citing simPIC

If you use simPIC in your work please cite our paper:

citation("simPIC")
#> To cite package 'simPIC' in publications use:
#> 
#>   Chugh S, McCarthy D, Shim H (2023). _simPIC: simPIC: flexible
#>   simulation of paired-insertion counts for single-cell ATAC-sequencing
#>   data_. R package version 1.3.0,
#>   <https://github.com/sagrikachugh/simPIC>.
#> 
#> A BibTeX entry for LaTeX users is
#> 
#>   @Manual{,
#>     title = {simPIC: simPIC: flexible simulation of paired-insertion counts for
#> single-cell ATAC-sequencing data},
#>     author = {Sagrika Chugh and Davis McCarthy and Heejung Shim},
#>     year = {2023},
#>     note = {R package version 1.3.0},
#>     url = {https://github.com/sagrikachugh/simPIC},
#>   }

Session information

sessionInfo()
#> R version 4.4.1 (2024-06-14)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Ubuntu 24.04.1 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.26.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=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: Etc/UTC
#> tzcode source: system (glibc)
#> 
#> attached base packages:
#> [1] stats4    stats     graphics  grDevices utils     datasets  methods  
#> [8] base     
#> 
#> other attached packages:
#>  [1] simPIC_1.3.0                SingleCellExperiment_1.29.0
#>  [3] SummarizedExperiment_1.37.0 Biobase_2.67.0             
#>  [5] GenomicRanges_1.59.0        GenomeInfoDb_1.43.0        
#>  [7] IRanges_2.41.0              S4Vectors_0.45.0           
#>  [9] BiocGenerics_0.53.1         generics_0.1.3             
#> [11] MatrixGenerics_1.19.0       matrixStats_1.4.1          
#> [13] BiocStyle_2.35.0           
#> 
#> loaded via a namespace (and not attached):
#>  [1] gtable_0.3.6            xfun_0.49               bslib_0.8.0            
#>  [4] ggplot2_3.5.1           lattice_0.22-6          vctrs_0.6.5            
#>  [7] tools_4.4.1             parallel_4.4.1          fansi_1.0.6            
#> [10] tibble_3.2.1            highr_0.11              pkgconfig_2.0.3        
#> [13] Matrix_1.7-1            checkmate_2.3.2         actuar_3.3-4           
#> [16] lifecycle_1.0.4         GenomeInfoDbData_1.2.13 farver_2.1.2           
#> [19] compiler_4.4.1          munsell_0.5.1           codetools_0.2-20       
#> [22] htmltools_0.5.8.1       sys_3.4.3               buildtools_1.0.0       
#> [25] sass_0.4.9              yaml_2.3.10             pillar_1.9.0           
#> [28] crayon_1.5.3            jquerylib_0.1.4         MASS_7.3-61            
#>  [ reached getOption("max.print") -- omitted 37 entries ]