---
title: "SPAROscore: Data Structures"
package: "SPAROscore"
author: "Venkatesh Kamaraj"
date: "`r Sys.Date()`"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{SPAROscore: Data Structures}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r global-options, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  fig.width = 12,
  fig.height = 6, 
  out.width = "100%" 
)
```

```{r setup, warning=FALSE, message=FALSE}
# load the necessary libraries
library(SPAROscore)
library(airway)
library(msigdbr)
library(dplyr)
library(Matrix)
library(DelayedArray)
library(GSEABase)
library(Seurat)
library(scater)
```


# Supported input data structures
SPAROscore is designed to integrate seamlessly with data structures commonly
used in bulk, single-cell, and spatial RNA-seq analysis workflows. 
Rather than requiring users to convert their data into a specific format, 
SPAROscore accepts a range of gene expression and gene signature 
data structures. This vignette demonstrates the supported input formats 
and illustrates how the output adapts to each input type.


* For gene expression input, the following data structures are supported

    * Matrix-like objects: Dense matrices, Sparse Matrices, Delayed Arrays, and
    Data frames

    * Bioconductor data structures: SummarizedExperiment and its derived classes 
    like SingleCellExperiment, SpatialExperiment, RangedSummarizedExperiment,
    tidySummarizedExperiment, etc.

    * Seurat objects: containing both single-cell and spatial datasets

*Note: Matrix-like inputs return a score table, whereas container objects 
(e.g. SummarizedExperiment and Seurat) are returned with scores 
added to their metadata.*

<table>

| Input | Accepted | Output |
| :--- | :---: | :--- |
| matrix | ✓ | matrix |
| sparseMatrix | ✓ | matrix |
| DelayedArray | ✓ | matrix |
| data.frame | ✓ | matrix |
| SummarizedExperiment | ✓ | SummarizedExperiment |
| SingleCellExperiment | ✓ | SingleCellExperiment |
| SpatialExperiment | ✓ | SpatialExperiment |
| Seurat | ✓ | Seurat |

</table>

* For the gene signature input, the following formats are supported

    * For a single signature: A character vector, A GeneSet object
    
    * For multiple signatures: A named list of character vectors, 
    GeneSetCollection objects.



*Note: The Gene identifiers (gene names, ensembl ids, etc) in the 
signature must match those used in the expression data.*



## Example datasets

In this vignette, we will demonstrate the data structure handling capabilities 
of  SPAROscore using the example dataset from the `airway` package. For the
gene signatures, we will use the top differentially expressed genes 
reported in the [original study](https://doi.org/10.1371/journal.pone.0099625), 
as well as the hallmark inflammatory response genes taken from the 
Molecular Signatures Database (MSigDB).

### Load the gene expression dataset
```{r load-data}
# load the airway dataset
data(airway)

# extract the gene expression matrix
counts_data <- assay(airway)

# extract the metadata
metadata <- as.data.frame(colData(airway))
```

### Load the gene signatures
```{r load-signature}
# set the glucocorticoid response markers from the original study as a signature 
response_genes <- c("ENSG00000112936", "ENSG00000170606", "ENSG00000120129", 
                     "ENSG00000152795", "ENSG00000211445", "ENSG00000163884",
                     "ENSG00000189221", "ENSG00000101347", "ENSG00000196136", 
                     "ENSG00000152583", "ENSG00000120658", "ENSG00000157514",
                     "ENSG00000103196", "ENSG00000179094", "ENSG00000152763",
                     "ENSG00000103310", "ENSG00000127954")


# load the inflammatory response hallmark genes from msigdb
inflammation_genes <- msigdbr(species = "Homo sapiens", collection = "H") %>% 
  filter(gs_name == "HALLMARK_INFLAMMATORY_RESPONSE") %>%
    pull("ensembl_gene") %>%
    as.character()

```
 

# Gene expression as matrix-like data structures

## Dense matrix
```{r matrix}
# load gene expression data as dense matrix
counts_matrix <- as.matrix(counts_data)

# score for the dense matrix
scores_matrix <- sparoscore(data = counts_matrix, 
                                 signatures = inflammation_genes)

# view scores
scores_matrix
```

Note that one signature gene is absent from the input expression data, 
and SPAROscore reports this as a warning during scoring.

## SparseMatrix
```{r smatrix}
# load gene expression data as sparseMatrix
counts_sparse <- as(counts_matrix, "sparseMatrix")

# score for the sparseMatrix
scores_sparse <- sparoscore(data = counts_sparse, 
                                 signatures = inflammation_genes)

# view scores
scores_sparse
```

Gene ranking is performed directly on sparse matrices without first converting 
them to dense matrices, reducing memory usage and improving computational 
efficiency.

## DelayedArrays
```{r dmatrix}
# load gene expression data as delayedArray
counts_delayed <- DelayedArray(counts_matrix)

# score for the DelayedArray
scores_delayed <- sparoscore(data = counts_delayed, 
                                 signatures = inflammation_genes)

# view scores
scores_delayed
```

Note that the gene ranking is performed natively on the DelayedArray object, 
enabling improved computational efficiency.

## Data frame
```{r dataframe}
# load gene expression data as delayedArray
counts_df <- as.data.frame(counts_matrix)

# score for the DelayedArray
scores_df <- sparoscore(data = counts_df, 
                                 signatures = inflammation_genes)

# view scores
scores_df
```

Data frames are internally converted to matrices before ranking 
because matrix operations are more efficient for this step.

### Verify if the output scores are the same with differing input formats

```{r check-results}
identical(scores_matrix, scores_sparse)
identical(scores_matrix, scores_delayed)
identical(scores_matrix, scores_df)
```

# Gene signature representations

## Single signature as a character vector
```{r character}
# compute scores from a character vector signature
scores_for_vector <- sparoscore(data = counts_matrix, 
                                 signatures = response_genes)

# view scores
scores_for_vector
```


## Single signature as a GeneSet object
```{r geneset}
# get a GeneSet object
response_geneset <- GeneSet(response_genes)

# compute scores from a GeneSet object
scores_for_geneset <- sparoscore(data = counts_sparse, 
                                 signatures = response_geneset, 
                                 prefix = "hello")

# view scores
scores_for_geneset
```

## Multiple signatures as a named list
```{r list}
# create a named list of vectors to score for both signatures simultaneously
gene_signatures <- list("glucocorticoid_response" = response_genes,
                        "inflammation_response" = inflammation_genes)

# compute scores from a list of signatures
scores_for_list <- sparoscore(data = counts_sparse, 
                                 signatures = gene_signatures)

# view scores
scores_for_list
```

## Multiple signatures as a GeneSetCollection object
```{r genesetcollection}
# create a GeneSetCollection object to score for both signatures simultaneously
gene_set_collection <- GeneSetCollection(
  mapply(function(g, n) GeneSet(unique(g), setName = n), 
         gene_signatures, names(gene_signatures))
)

# compute scores from a GeneSetCollection object
scores_for_gsc <- sparoscore(data = counts_matrix, 
                                 signatures = gene_set_collection)

# view scores
scores_for_gsc
```


# SummarizedExperiment and derived objects
For SummarizedExperiment and its derived objects, after scoring, SPAROscore 
returns an object of the same class with the scores and the rank caps
appended to the `colData`. The calculated gene ranks are added to a new assay 
named `ranks`.

In this example, let us work with a SingleCellExperiment object

```{r sce}
# convert airways to a SingleCellExperiment object
airway_sce <- as(airway, "SingleCellExperiment")

# view the metadata of the object pre-scoring
colData(airway_sce)

# view the assays present in the object pre-scoring
assays(airway_sce)

# compute scores for a SummarizedExperiment object
airway_sce <- sparoscore(data = airway_sce,
                        assay = "counts",
                        signatures = response_genes)


# view the metadata of the object after-scoring
colData(airway_sce)

# view the assays present in the object after-scoring
assays(airway_sce)


# the precomputed ranks can be reused for subsequent scoring runs
airway_sce <- sparoscore(data=airway_sce,
                         data_has_ranks = TRUE,
                         assay = "ranks",
                         signatures = inflammation_genes,
                         prefix = "run2_")

# view the metadata of the object after-scoring
colData(airway_sce)

```


# Seurat objects
For Seurat objects, after scoring, SPAROscore returns a Seurat object 
with the scores and the rank caps appended to the metadata. The ranks are stored
in a layer named `ranks` in the same assay as the counts.

```{r seurat}
# convert airway to a seurat object 
airway_seurat <- CreateSeuratObject(
  counts = assay(airway),
  meta.data = as.data.frame(colData(airway)),
  project = "Airway_Seurat"
)

# view the metadata of the object pre-scoring
airway_seurat[[]]

# view all the layers in the object pre-scoring
Layers(airway_seurat[["RNA"]])

# compute scores for a Seurat object
airway_seurat <- sparoscore(data = airway_seurat,
                    assay = "RNA",
                    layer = "counts",
                    signatures = response_genes)


# view the metadata of the object after-scoring
airway_seurat[[]]

# view all the layers in the object after-scoring
Layers(airway_seurat[["RNA"]])

# the precomputed ranks can be reused for subsequent scoring runs
airway_seurat <- sparoscore(data = airway_seurat,
                            data_has_ranks = TRUE,
                            assay = "RNA",
                            layer = "ranks",
                            signatures = inflammation_genes,
                            prefix = "run2_")

# view the metadata of the object after-scoring
airway_seurat[[]]
```

This vignette demonstrated the range of expression data containers and 
gene signature representations accepted by SPAROscore. 
Matrix-like inputs return score tables, while container objects such as 
SummarizedExperiment and Seurat are returned with scores and 
precomputed ranks integrated into the original object. 
This design allows SPAROscore to fit naturally into existing bulk RNA-seq, 
ingle-cell RNA-seq, and spatial transcriptomics workflows 
with minimal data conversion.

For examples of in-depth transcriptomics analyses using SPAROscore, refer here:
[Transcriptomics technologies](sparoscore_transcriptomics_technologies.html)


For examples of additional options and advanced usage, see:
[Advanced options](sparoscore_advanced_options.html)

```{r session-info}
sessionInfo()
```
