# RaggedExperiment

Martin Morgan1 and Marcel Ramos1

1Roswell Park Comprehensive Cancer Center, Buffalo, NY

#### 24 October 2023

# 1 Introduction

The RaggedExperiment package provides a flexible data representation for copy number, mutation and other ragged array schema for genomic location data. It aims to provide a framework for a set of samples that have differing numbers of genomic ranges.

The `RaggedExperiment` class derives from a `GRangesList` representation and provides a semblance of a rectangular dataset. The row and column dimensions of the `RaggedExperiment` correspond to the number of ranges in the entire dataset and the number of samples represented in the data, respectively.

# 2 Installation

    if (!require("BiocManager"))
        install.packages("BiocManager")
    BiocManager::install("RaggedExperiment")

Loading the package:

    library(RaggedExperiment)
    library(GenomicRanges)

# 3 `RaggedExperiment` class overview

A schematic showing the class geometry and supported transformations of the `RaggedExperiment` class is show below. There are three main operations for transforming the `RaggedExperiment` representation:

1. `sparseAssay`
2. `compactAssay`
3. `qreduceAssay`

Figure 1: RaggedExperiment object schematic
Rows and columns represent genomic ranges and samples, respectively. Assay operations can be performed with (from left to right) compactAssay, qreduceAssay, and sparseAssay.

# 4 Constructing a `RaggedExperiment` object

We start with a couple of `GRanges` objects, each representing an individual sample:

    sample1 <- GRanges(
        c(A = "chr1:1-10:-", B = "chr1:8-14:+", C = "chr2:15-18:+"),
        score = 3:5)
    sample2 <- GRanges(
        c(D = "chr1:1-10:-", E = "chr2:11-18:+"),
        score = 1:2)

Include column data `colData` to describe the samples:

    colDat <- DataFrame(id = 1:2)

## 4.1 Using `GRanges` objects

    ragexp <- RaggedExperiment(
        sample1 = sample1,
        sample2 = sample2,
        colData = colDat
    )
    ragexp

    ## class: RaggedExperiment
    ## dim: 5 2
    ## assays(1): score
    ## rownames(5): A B C D E
    ## colnames(2): sample1 sample2
    ## colData names(1): id

## 4.2 Using a `GRangesList` instance

    grl <- GRangesList(sample1 = sample1, sample2 = sample2)
    RaggedExperiment(grl, colData = colDat)

    ## class: RaggedExperiment
    ## dim: 5 2
    ## assays(1): score
    ## rownames(5): A B C D E
    ## colnames(2): sample1 sample2
    ## colData names(1): id

## 4.3 Using a `list` of `GRanges`

    rangeList <- list(sample1 = sample1, sample2 = sample2)
    RaggedExperiment(rangeList, colData = colDat)

    ## class: RaggedExperiment
    ## dim: 5 2
    ## assays(1): score
    ## rownames(5): A B C D E
    ## colnames(2): sample1 sample2
    ## colData names(1): id

## 4.4 Using a `List` of `GRanges` with metadata

Note: In cases where a `SimpleGenomicRangesList` is provided along with accompanying metadata (accessed by `mcols`), the metadata is used as the `colData` for the `RaggedExperiment`.

    grList <- List(sample1 = sample1, sample2 = sample2)
    mcols(grList) <- colDat
    RaggedExperiment(grList)

    ## class: RaggedExperiment
    ## dim: 5 2
    ## assays(1): score
    ## rownames(5): A B C D E
    ## colnames(2): sample1 sample2
    ## colData names(1): id

# 5 Accessors

## 5.1 Range data

    rowRanges(ragexp)

    ## GRanges object with 5 ranges and 0 metadata columns:
    ##     seqnames    ranges strand
    ##        <Rle> <IRanges>  <Rle>
    ##   A     chr1      1-10      -
    ##   B     chr1      8-14      +
    ##   C     chr2     15-18      +
    ##   D     chr1      1-10      -
    ##   E     chr2     11-18      +
    ##   -------
    ##   seqinfo: 2 sequences from an unspecified genome; no seqlengths

## 5.2 Dimension names

    dimnames(ragexp)

    ## [[1]]
    ## [1] "A" "B" "C" "D" "E"
    ##
    ## [[2]]
    ## [1] "sample1" "sample2"

## 5.3 `colData`

    colData(ragexp)

    ## DataFrame with 2 rows and 1 column
    ##                id
    ##         <integer>
    ## sample1         1
    ## sample2         2

# 6 Subsetting

## 6.1 by dimension

Subsetting a `RaggedExperiment` is akin to subsetting a `matrix` object. Rows correspond to genomic ranges and columns to samples or specimen. It is possible to subset using `integer`, `character`, and `logical` indices.

## 6.2 by genomic ranges

The `overlapsAny` and `subsetByOverlaps` functionalities are available for use for `RaggedExperiment`. Please see the corresponding documentation in `RaggedExperiment` and `GenomicRanges`.

# 7 *Assay functions

`RaggedExperiment` package provides several different functions for representing ranged data in a rectangular matrix via the `*Assay` methods.

## 7.1 sparseAssay

The most straightforward matrix representation of a `RaggedExperiment` will return a matrix of dimensions equal to the product of the number of ranges and samples.

    dim(ragexp)

    ## [1] 5 2

    Reduce(`*`, dim(ragexp))

    ## [1] 10

    sparseAssay(ragexp)

    ##   sample1 sample2
    ## A       3      NA
    ## B       4      NA
    ## C       5      NA
    ## D      NA       1
    ## E      NA       2

    length(sparseAssay(ragexp))

    ## [1] 10

### 7.1.1 Support for sparse matrix output

We provide sparse matrix representations with the help of the `Matrix` package. To obtain a sparse representation, the user can use the `sparse = TRUE` argument.

    sparseAssay(ragexp, sparse = TRUE)

    ## 5 x 2 sparse Matrix of class "dgCMatrix"
    ##   sample1 sample2
    ## A       3       .
    ## B       4       .
    ## C       5       .
    ## D       .       1
    ## E       .       2

This representation is of class `dgCMatrix` see the `Matrix` documentation for more details.

## 7.2 compactAssay

Samples with identical ranges are placed in the same row. Non-disjoint ranges are not collapsed.

    compactAssay(ragexp)

    ##              sample1 sample2
    ## chr1:8-14:+        4      NA
    ## chr1:1-10:-        3       1
    ## chr2:11-18:+      NA       2
    ## chr2:15-18:+       5      NA

Similarly, to `sparseAssay` the `compactAssay` function provides the option to obtain a sparse matrix representation with the `sparse = TRUE` argument. This will return a `dgCMatrix` class from the `Matrix` package.

    compactAssay(ragexp, sparse = TRUE)

    ## 4 x 2 sparse Matrix of class "dgCMatrix"
    ##              sample1 sample2
    ## chr1:1-10:-        3       1
    ## chr1:8-14:+        4       .
    ## chr2:15-18:+       5       .
    ## chr2:11-18:+       .       2

## 7.3 disjoinAssay

This function returns a matrix of disjoint ranges across all samples. Elements of the matrix are summarized by applying the `simplifyDisjoin` functional argument to assay values of overlapping ranges.

    disjoinAssay(ragexp, simplifyDisjoin = mean)

    ##              sample1 sample2
    ## chr1:8-14:+        4      NA
    ## chr1:1-10:-        3       1
    ## chr2:11-14:+      NA       2
    ## chr2:15-18:+       5       2

## 7.4 qreduceAssay

The `qreduceAssay` function works with a `query` parameter that highlights a window of ranges for the resulting matrix. The returned matrix will have dimensions `length(query)` by `ncol(x)`. Elements contain assay values for the i th query range and the j th sample, summarized according to the `simplifyReduce` functional argument.

For demonstration purposes, we can have a look at the original `GRangesList` and the associated scores from which the current `ragexp` object is derived:

    unlist(grl, use.names = FALSE)

    ## GRanges object with 5 ranges and 1 metadata column:
    ##     seqnames    ranges strand |     score
    ##        <Rle> <IRanges>  <Rle> | <integer>
    ##   A     chr1      1-10      - |         3
    ##   B     chr1      8-14      + |         4
    ##   C     chr2     15-18      + |         5
    ##   D     chr1      1-10      - |         1
    ##   E     chr2     11-18      + |         2
    ##   -------
    ##   seqinfo: 2 sequences from an unspecified genome; no seqlengths

This data is represented as `rowRanges` and `assays` in `RaggedExperiment`:

    rowRanges(ragexp)

    ## GRanges object with 5 ranges and 0 metadata columns:
    ##     seqnames    ranges strand
    ##        <Rle> <IRanges>  <Rle>
    ##   A     chr1      1-10      -
    ##   B     chr1      8-14      +
    ##   C     chr2     15-18      +
    ##   D     chr1      1-10      -
    ##   E     chr2     11-18      +
    ##   -------
    ##   seqinfo: 2 sequences from an unspecified genome; no seqlengths

    assay(ragexp, "score")

    ##   sample1 sample2
    ## A       3      NA
    ## B       4      NA
    ## C       5      NA
    ## D      NA       1
    ## E      NA       2

Here we provide the “query” region of interest:

    (query <- GRanges(c("chr1:1-14:-", "chr2:11-18:+")))

    ## GRanges object with 2 ranges and 0 metadata columns:
    ##       seqnames    ranges strand
    ##          <Rle> <IRanges>  <Rle>
    ##   [1]     chr1      1-14      -
    ##   [2]     chr2     11-18      +
    ##   -------
    ##   seqinfo: 2 sequences from an unspecified genome; no seqlengths

The `simplifyReduce` argument in `qreduceAssay` allows the user to summarize overlapping regions with a custom method for the given “query” region of interest. We provide one for calculating a weighted average score per query range, where the weight is proportional to the overlap widths between overlapping ranges and a query range.

Note that there are three arguments to this function. See the documentation for additional details.

    weightedmean <- function(scores, ranges, qranges)
    {
        isects <- pintersect(ranges, qranges)
        sum(scores * width(isects)) / sum(width(isects))
    }

A call to `qreduceAssay` involves the `RaggedExperiment`, the `GRanges` query and the `simplifyReduce` functional argument.

    qreduceAssay(ragexp, query, simplifyReduce = weightedmean)

    ##              sample1 sample2
    ## chr1:1-14:-        3       1
    ## chr2:11-18:+       5       2

See the schematic for a visual representation.

back to top

# 8 Coercion

The `RaggedExperiment` provides a family of parallel functions for coercing to the `SummarizedExperiment` class. By selecting a particular assay index (`i`), a parallel assay coercion method can be achieved.

Here is the list of function names:

- `sparseSummarizedExperiment`
- `compactSummarizedExperiment`
- `disjoinSummarizedExperiment`
- `qreduceSummarizedExperiment`

See the documentation for details.

## 8.1 from dgCMatrix to RaggedExperiment

In the special case where the rownames of a `sparseMatrix` are coercible to `GRanges`, `RaggedExperiment` provides the facility to convert sparse matrices into `RaggedExperiment`. This can be done using the `as` coercion method. The example below first creates an example sparse `dgCMatrix` class and then shows the `as` method usage to this end.

    library(Matrix)

    ##
    ## Attaching package: 'Matrix'

    ## The following object is masked from 'package:S4Vectors':
    ##
    ##     expand

    sm <- Matrix::sparseMatrix(
        i = c(2, 3, 4, 3, 4, 3, 4),
        j = c(1, 1, 1, 3, 3, 4, 4),
        x = c(2L, 4L, 2L, 2L, 2L, 4L, 2L),
        dims = c(4, 4),
        dimnames = list(
            c("chr2:1-10", "chr2:2-10", "chr2:3-10", "chr2:4-10"),
            LETTERS[1:4]
        )
    )

    as(sm, "RaggedExperiment")

    ## class: RaggedExperiment
    ## dim: 7 3
    ## assays(1): counts
    ## rownames: NULL
    ## colnames(3): A C D
    ## colData names(0):

# 9 Session Information

    sessionInfo()

    ## R version 4.3.1 Patched (2023-06-17 r84564)
    ## Platform: x86_64-apple-darwin20 (64-bit)
    ## Running under: macOS Monterey 12.6.5
    ##
    ## Matrix products: default
    ## BLAS:   /Library/Frameworks/R.framework/Versions/4.3-x86_64/Resources/lib/libRblas.0.dylib
    ## LAPACK: /Library/Frameworks/R.framework/Versions/4.3-x86_64/Resources/lib/libRlapack.dylib;  LAPACK version 3.11.0
    ##
    ## locale:
    ## [1] C/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
    ##
    ## time zone: America/New_York
    ## tzcode source: internal
    ##
    ## attached base packages:
    ## [1] stats4    stats     graphics  grDevices utils     datasets  methods
    ## [8] base
    ##
    ## other attached packages:
    ## [1] Matrix_1.6-1.1          RaggedExperiment_1.26.0 GenomicRanges_1.54.0
    ## [4] GenomeInfoDb_1.38.0     IRanges_2.36.0          S4Vectors_0.40.0
    ## [7] BiocGenerics_0.48.0     BiocStyle_2.30.0
    ##
    ## loaded via a namespace (and not attached):
    ##  [1] jsonlite_1.8.7              compiler_4.3.1
    ##  [3] BiocManager_1.30.22         crayon_1.5.2
    ##  [5] BiocBaseUtils_1.4.0         SummarizedExperiment_1.32.0
    ##  [7] Biobase_2.62.0              bitops_1.0-7
    ##  [9] jquerylib_0.1.4             yaml_2.3.7
    ## [11] fastmap_1.1.1               lattice_0.22-5
    ## [13] R6_2.5.1                    XVector_0.42.0
    ## [15] S4Arrays_1.2.0              knitr_1.44
    ## [17] DelayedArray_0.28.0         bookdown_0.36
    ## [19] MatrixGenerics_1.14.0       GenomeInfoDbData_1.2.11
    ## [21] bslib_0.5.1                 rlang_1.1.1
    ## [23] cachem_1.0.8                xfun_0.40
    ## [25] sass_0.4.7                  SparseArray_1.2.0
    ## [27] cli_3.6.1                   zlibbioc_1.48.0
    ## [29] digest_0.6.33               grid_4.3.1
    ## [31] evaluate_0.22               abind_1.4-5
    ## [33] RCurl_1.98-1.12             rmarkdown_2.25
    ## [35] matrixStats_1.0.0           tools_4.3.1
    ## [37] htmltools_0.5.6.1