---
title: "Simple analysis of <%= ANALYSIS_TYPE %> data"
author: <%= AUTHOR %>
date: "`r Sys.Date()`"
output:
  BiocStyle::html_document:
    toc_float: yes
    titlecaps: false 
  md_document:
    preserve_yaml: yes
---

```{r preamble, echo=FALSE}
knitr::opts_chunk$set(error=FALSE, warning=FALSE, message=FALSE)
```

# Data loading

Our `SummarizedExperiment` object contains all of the experimental data and metadata for this study.
Each column is assumed to correspond to a single cell.
:BEGIN mainexp-text
The main experiment contains <%= MAIN_MODALITY %> data where each row corresponds to a <%= MAIN_FEATURE_TYPE %>.
:END
:BEGIN altexp-text
The alternative experiment `<%= ALTEXP_SE %>` contains <%= ALTEXP_MODALITY %> data where each row corresponds to a <%= ALTEXP_FEATURE_TYPE %>.
:END

```{r}
:BEGIN initialize-se
:END
se
```

We convert each relevant experiment into a `SingleCellExperiment` if it wasn't already one.

```{r}
loadNamespace("SingleCellExperiment")
sce <- as(se, "SingleCellExperiment")
:BEGIN altexp-force-sce
SingleCellExperiment::altExp(sce, <%= ALTEXP %>) <- as(SingleCellExperiment::altExp(sce, <%= ALTEXP %>), "SingleCellExperiment")
:END
```

:BEGIN subset-data
We subset the `SummarizedExperiment` to the desired levels of a factor of interest.

```{r}
sub.factor <- <%= SUBSET_FACTOR %>
sub.levels <- <%= SUBSET_LEVELS %>
sub.keep <- SummarizedExperiment::colData(sce)[,sub.factor] %in% sub.levels
sce <- sce[,sub.keep]
summary(sub.keep)
```
:END

:BEGIN block-setup
We identify the blocking factor from the column data.
This defines some uninteresting factor of variation, usually a batch of origin for each cell.

```{r}
table(SummarizedExperiment::colData(sce)[,<%= BLOCK %>])
```
:END

We also coerce each input matrix to Bioconductor's sparse format.
This isn't strictly necessary but it avoids silent performance degradation from inefficient formats (e.g., file-backed, `dgTMatrix` objects).

```{r}
loadNamespace("SparseArray")
:BEGIN rna-force-load
SummarizedExperiment::assay(<%= RNA_SE %>, <%= ASSAY %>, withDimnames=FALSE) <- local({
    mat <- SummarizedExperiment::assay(<%= RNA_SE %>, <%= ASSAY %>, withDimnames=FALSE)
    DelayedArray::DelayedArray(as(mat, "SVT_SparseMatrix"))
})
:END
:BEGIN adt-force-load
SummarizedExperiment::assay(<%= ADT_SE %>, <%= ASSAY %>, withDimnames=FALSE) <- local({
    mat <- SummarizedExperiment::assay(<%= ADT_SE %>, <%= ASSAY %>, withDimnames=FALSE)
    DelayedArray::DelayedArray(as(mat, "SVT_SparseMatrix"))
})
:END
```

# Quality control

:BEGIN rna-qc-metrics
## For RNA

We compute per-cell QC metrics from the RNA counts.
If mitochondrial genes are present, we use them as a control subset.
High mitochondrial proportions are indicators of cell damage as cytoplasmic transcripts leak out of the cell.

```{r}
:BEGIN define-mito-from-ranges
is.mito <- local({
    rr <- SummarizedExperiment::rowRanges(<%= RNA_SE %>)
    mito.names <- <%= MITO_SEQNAMES %>
    mito <- !is.na(S4Vectors::match(Seqinfo::seqnames(rr), mito.names))
    if (is(rr, 'GenomicRanges')) {
        BiocGenerics::which(mito)
    } else {
        any(mito)
    }
})
:END
:BEGIN define-mito-from-rownames
is.mito <- grepl(<%= MITO_REGEX %>, rownames(<%= RNA_SE %>))
:END
:BEGIN define-mito-from-symbols
is.mito <- grepl(<%= MITO_REGEX %>, SummarizedExperiment::rowData(<%= RNA_SE %>)[,<%= SYMBOL_FIELD %>])
:END
summary(is.mito)

<%= RNA_SE %> <- scrapper::quickRnaQc.se(
    <%= RNA_SE %>,
    assay.type = <%= ASSAY %>,
    subsets = list(mito = is.mito),
:BEGIN block-args
    block = SummarizedExperiment::colData(sce)[,<%= BLOCK %>],
:END
    more.suggest.args = list(num.mads = <%= NUM_MADS %>),
    num.threads = <%= NUM_THREADS %>
)

# Storing the unfiltered metrics in a separate DF for posterity.
rna.qc.fields <- c("sum", "detected", "subset.proportion.mito", "keep")
rna.qc.df <- SummarizedExperiment::colData(<%= RNA_SE %>)[,rna.qc.fields]
rna.qc.df
```

Filtering thresholds are automatically suggested for each metric using an outlier-based approach.
The idea is that low-quality cells will have outlier values (in the "bad" direction) for one or more of the metrics.
A cell is considered to be of low quality if its library size or number of detected features is too small, or if the mitochondrial proportion is too high.
:BEGIN block-text
A separate threshold is computed for each block to ensure that uninteresting differences between blocks (e.g., different sequencing depths) do not skew the thresholds.
:END

```{r}
thresholds <- S4Vectors::metadata(<%= RNA_SE %>)$qc$thresholds
thresholds
```

We can count the number of cells that would be retained according to these thresholds.

```{r}
summary(rna.qc.df$keep)
```

We might as well make some plots at this point.

```{r plot-rna-qc}
:BEGIN plot-simple
scater::plotColData(<%= RNA_SE %>, y = "sum", colour_by = "keep") +
    ggplot2::scale_y_continuous(transform = "log1p") + 
    scater::categoricalHlines(x = NULL, y = thresholds[["sum"]])

scater::plotColData(<%= RNA_SE %>, y = "detected", colour_by = "keep") +
    ggplot2::scale_y_continuous(transform = "log1p") +
    scater::categoricalHlines(x = NULL, y = thresholds[["detected"]])

scater::plotColData(<%= RNA_SE %>, y = "subset.proportion.mito", colour_by = "keep") +
    ggplot2::scale_y_continuous(transform = "log1p") +
    scater::categoricalHlines(x = NULL, y = thresholds[["subset.proportion"]][["mito"]])
:END
:BEGIN plot-blocked
scater::plotColData(<%= RNA_SE %>, x = <%= BLOCK %>, y = "sum", colour_by = "keep") +
    ggplot2::scale_y_continuous(transform = "log1p") + 
    scater::categoricalHlinesNamed(y = thresholds[["sum"]])

scater::plotColData(<%= RNA_SE %>, x = <%= BLOCK %>, y = "detected", colour_by = "keep") +
    ggplot2::scale_y_continuous(transform = "log1p") +
    scater::categoricalHlinesNamed(y = thresholds[["detected"]])

scater::plotColData(<%= RNA_SE %>, x = <%= BLOCK %>, y = "subset.proportion.mito", colour_by = "keep") +
    ggplot2::scale_y_continuous(transform = "log1p") +
    scater::categoricalHlinesNamed(y = thresholds[["subset.proportion"]][["mito"]])
:END
```
:END

:BEGIN adt-qc-metrics
## For ADT

We compute per-cell QC metrics from the ADT counts.
We use the IgG tags as the subset of control features if they are available.
High IgG counts are indicators of non-specific antibody aggregates. 

```{r}
:BEGIN define-igg-from-rownames
is.igg <- grepl(<%= IGG_REGEX %>, rownames(<%= ADT_SE %>))
:END
:BEGIN define-igg-from-symbols
is.igg <- grepl(<%= IGG_REGEX %>, SummarizedExperiment::rowData(<%= ADT_SE %>)[,<%= SYMBOL_FIELD %>])
:END
summary(is.igg)

<%= ADT_SE %> <- scrapper::quickAdtQc.se(
    <%= ADT_SE %>,
    assay.type = <%= ASSAY %>,
    subsets = list(igg = is.igg),
:BEGIN block-args
    block = SummarizedExperiment::colData(sce)[,<%= BLOCK %>],
:END
    num.threads =<%=  NUM_THREADS %>
)

# Storing the unfiltered metrics in a separate DF for posterity.
adt.qc.fields <- c("sum", "detected", "subset.sum.igg", "keep")
adt.qc.df <- SummarizedExperiment::colData(<%= ADT_SE %>)[,adt.qc.fields]
adt.qc.df
```

Filtering thresholds are automatically suggested for each metric using an outlier-based approach.
The idea is that low-quality cells will have outlier values (in the "bad" direction) for one or more of the metrics.
A cell is considered to be of low quality if its number of detected tags is too small or if its IgG count is too high.
:BEGIN block-text
A separate threshold is computed for each block to ensure that uninteresting differences between blocks (e.g., different sequencing depths) do not skew the thresholds.
:END

```{r}
thresholds <- S4Vectors::metadata(<%= ADT_SE %>)$qc$thresholds
thresholds
```

We can count the number of cells that would be retained according to these thresholds.

```{r}
summary(adt.qc.df$keep)
```

We might as well make some plots at this point.

```{r plot-adt-qc}
:BEGIN plot-simple
scater::plotColData(<%= ADT_SE %>, y = "sum", colour_by = "keep") +
    ggplot2::scale_y_continuous(transform = "log1p")

scater::plotColData(<%= ADT_SE %>, y = "detected", colour_by = "keep") +
    ggplot2::scale_y_continuous(transform = "log1p") +
    scater::categoricalHlines(x = NULL, y = thresholds[["detected"]])

scater::plotColData(<%= ADT_SE %>, y = "subset.sum.igg", colour_by = "keep") +
    ggplot2::scale_y_continuous(transform = "log1p") +
    scater::categoricalHlines(x = NULL, y = thresholds[["subset.sum"]][["igg"]])
:END
:BEGIN plot-blocked
scater::plotColData(<%= ADT_SE %>, x = <%= BLOCK %>, y = "sum", colour_by = "keep") +
    ggplot2::scale_y_continuous(transform = "log1p")

scater::plotColData(<%= ADT_SE %>, x = <%= BLOCK %>, y = "detected", colour_by = "keep") +
    ggplot2::scale_y_continuous(transform = "log1p") +
    scater::categoricalHlinesNamed(y = thresholds[["detected"]])

scater::plotColData(<%= ADT_SE %>, x = <%= BLOCK %>, y = "subset.sum.igg", colour_by = "keep") +
    ggplot2::scale_y_continuous(transform = "log1p") +
    scater::categoricalHlinesNamed(y = thresholds[["subset.sum"]][["igg"]])
:END
```
:END

## Applying the filter

:BEGIN filter-qc
We apply those filters to subset the dataset so that we only retain high-quality cells in all modalities of interest.
This simplifies interpretation of the results as we don't have to worry as much about removing low-quality artifacts in later steps.

```{r}
keep <- <%= QC_SUBSET %>
summary(keep)
:BEGIN with-block
# Checking the number of cells discarded from each block.
table(keep, SummarizedExperiment::colData(sce)[,<%= BLOCK %>])
:END
sce <- sce[,keep]
```
:END

:BEGIN no-filter-qc
Well, actually, we don't apply any filtering to our matrix.
Instead, we'll manually remove low-quality artifacts after clustering.
This aims to minimize the risk of removing genuine cell subpopulations.
:END

# Normalization

We perform scaling normalization to remove technical biases in sequencing depth, capture efficiency, etc. between cells.
Specifically, we compute a size factor for each cell and divide that cell's counts by its factor to obtain normalized expression values.
Normalization removes the scaling biases and simplifies comparisons between cells in downstream analyses. 

All size factors are centered to a mean of 1 so that the normalized expression values are on roughly the same scale as the input counts.
:BEGIN norm-block-text
If multiple blocks are present, the centering is done for the lowest-coverage block to avoid strong upscaling that inflates technical noise.
:END
We also log-transform so that differences in the log-normalized expression values represent log-fold changes in downstream analyses.

:BEGIN rna-norm
## For RNA

For the RNA count matrix, we use the total sum for each cell to compute library size-based size factors.

```{r}
<%= RNA_SE %> <- scrapper::normalizeRnaCounts.se(
    <%= RNA_SE %>,
    size.factors = SummarizedExperiment::colData(<%= RNA_SE %>)$sum,
:BEGIN block-args
    block = SummarizedExperiment::colData(sce)[,<%= BLOCK %>],
:END
    assay.type = <%= ASSAY %>
)
summary(BiocGenerics::sizeFactors(<%= RNA_SE %>))
```

We visualize the distribution of the size factors, most of which should be around 1.

```{r plot-rna-sf}
scater::plotColData(<%= RNA_SE %>, x = <%= PLOT_X %>, y = "sizeFactor") +
    ggplot2::scale_y_log10()
```

We can also look at the log-transformed counts:

```{r}
SingleCellExperiment::logcounts(<%= RNA_SE %>)
```
:END

:BEGIN adt-norm
## For ADT

For the ADT count matrix, we use the (adjusted) geometric mean of counts within each cell to compute a size factor.
Unlike the library size, this is more robust to any spikes in abundance for a few ADTs.
Such spikes are not uncommon due to the binary nature of many surface targets.

```{r}
<%= ADT_SE %> <- scrapper::normalizeAdtCounts.se(
    <%= ADT_SE %>,
    assay.type = <%= ASSAY %>,
:BEGIN block-args
    block = SummarizedExperiment::colData(sce)[,<%= BLOCK %>],
:END
    num.threads = <%= NUM_THREADS %>
)
summary(BiocGenerics::sizeFactors(<%= ADT_SE %>))
```

We visualize the distribution of the size factors, most of which should be around 1.

```{r plot-adt-sf}
scater::plotColData(<%= ADT_SE %>, x = <%= PLOT_X %>, y = "sizeFactor") +
    ggplot2::scale_y_log10()
```

We can also look at the log-transformed counts:

```{r}
SingleCellExperiment::logcounts(<%= ADT_SE %>)
```
:END

:BEGIN rna-hvg
# Variance modelling

We identify highly variable genes (HVGs) from the log-normalized RNA counts. 
The idea is to only use the HVGs in downstream analyses to enrich for biological signal and reduce computational work.

```{r}
<%= RNA_SE %> <- scrapper::chooseRnaHvgs.se(
    <%= RNA_SE %>,
    top = <%= TOP_HVGS %>,
:BEGIN block-args
    block = SummarizedExperiment::colData(sce)[,<%= BLOCK %>],
    include.per.block = TRUE,
:END
    num.threads = <%= NUM_THREADS %>,
    more.var.args = list(use.min.width = TRUE)
)

rna.hvg.fields <- c(<%= HVG_SYMBOL_FIELD %>"means", "variances", "fitted", "residuals", "hvg")
rna.hvg.df <- SummarizedExperiment::rowData(<%= RNA_SE %>)[,rna.hvg.fields]
```

To model the mean-variance relationship in log-count data, we fit a mean-dependent trend to the variances of the log-expression values. 
We set a minimum width on the trend-fitting window to mitigate imbalances in the distribution of gene abundances.
:BEGIN block-text
When multiple blocks are present, we fit a trend separately to the means/variances in each block and then we average all statistics across blocks.
This accounts for a block-specific relationship and avoids absorbing uninteresting differences between blocks into the variance estimates.
:END

```{r plot-hvg}
:BEGIN unblocked
fun <- approxfun(x = rna.hvg.df$means, y = rna.hvg.df$fitted, ties=mean)
scater::plotRowData(<%= RNA_SE %>, x = "means", y = "variances", colour_by="hvg") +
    ggplot2::geom_function(fun = fun, colour = "red", lty=2)
:END
:BEGIN blocked
baseplot <- scater::plotRowData(<%= RNA_SE %>, x = "means", y = "variances", colour_by="hvg")
per.block.stats <- SummarizedExperiment::rowData(<%= RNA_SE %>)$per.block
for (b in colnames(per.block.stats)) {
    pb <- per.block.stats[,b]
    fun <- approxfun(x = pb$means, y = pb$fitted, ties=mean)
    baseplot <- baseplot + ggplot2::geom_function(fun = fun, colour = "red")
}
SummarizedExperiment::rowData(<%= RNA_SE %>)$per.block <- NULL # no need after plotting.
baseplot
:END
```

We then define the highly variable genes as those with the largest residuals.

```{r}
head(rna.hvg.df[order(rna.hvg.df$residuals, decreasing=TRUE),], 10)
summary(rna.hvg.df$hvg)
```
:END

# Principal components analysis

We perform PCA on the log-normalized data to obtain the top few principal components that explain the most variance in our dataset.
We use these PCs instead of the log-expression matrix in downstream steps, 
where distances between cells in PC space approximate the distances in the original expression space.
Focusing on the top PCs allows us to remove technical noise and reduce computational work.
:BEGIN pca-block-text
For datasets with multiple blocks, the PCA only considers on the variance within each block to ensure that the top PCs do not capture uninteresting differences between blocks.
:END

:BEGIN rna-pca
## For RNA

For the RNA log-expression matrix, we perform a PCA on the previously-selected HVGs.
This enriches for biological variation while reducing compute time.

```{r}
<%= RNA_SE %> <- scrapper::runPca.se(
    <%= RNA_SE %>,
    features = SummarizedExperiment::rowData(<%= RNA_SE %>)$hvg,
:BEGIN block-args
    block = SummarizedExperiment::colData(sce)[,<%= BLOCK %>],
:END
    number = <%= TOP_PCS %>, 
    num.threads=<%= NUM_THREADS %>
)
dim(SingleCellExperiment::reducedDim(<%= RNA_SE %>, "PCA"))
```

Let's have a look at the percentage of variance explained for each successive PC:

```{r plot-rna-pca}
rna.pca.info <- S4Vectors::metadata(<%= RNA_SE %>)$PCA
rna.prop.exp <- rna.pca.info$variance.explained / rna.pca.info$total.variance * 100
plot(cumsum(rna.prop.exp), ylab="Variance explained (%)", xlab="PC number", type="b")
```
:END

:BEGIN adt-pca
## For ADT

For the ADT log-expression matrix, we perform a PCA on all of the tags.
There's not much point limiting ourselves to the most variable tags;
there are usually much fewer tags than genes, and sometimes fewer tags than the number of PCs,
in which case the PCA is just a rotation of the input data.

```{r}
<%= ADT_SE %> <- scrapper::runPca.se(
    <%= ADT_SE %>,
    features = NULL,
:BEGIN block-args
    block = SummarizedExperiment::colData(sce)[,<%= BLOCK %>],
:END
    number = <%= TOP_PCS %>, 
    num.threads = <%= NUM_THREADS %>
)
dim(SingleCellExperiment::reducedDim(<%= ADT_SE %>, "PCA"))
```

Let's have a look at the percentage of variance explained for each successive PC:

```{r plot-adt-pca}
adt.pca.info <- S4Vectors::metadata(<%= ADT_SE %>)$PCA
adt.prop.exp <- adt.pca.info$variance.explained / adt.pca.info$total.variance * 100
plot(cumsum(adt.prop.exp), ylab="Variance explained (%)", xlab="PC number", type="b")
```
:END

:BEGIN combined-pca
## Combining modalities

We combine PCs from different modalities for downstream analyses. 
This involves scaling each matrix so that each modality has a comparable level of within-population variance,
and then combining those matrices together.
:BEGIN block-text
For multiple blocks, the variance is computed within each block and then averaged across blocks.
:END

```{r}
altexps.to.combine <- <%= ALTEXP_NAMES %>
sce <- scrapper::scaleByNeighbors.se(
    sce,
    main.reddims = <%= MAIN_NAME %>,
    altexp.reddims = setNames(rep("PCA", length(altexps.to.combine)), altexps.to.combine),
:BEGIN block-args
    block = SummarizedExperiment::colData(sce)[,<%= BLOCK %>],
:END
    num.threads = <%= NUM_THREADS %>
)
dim(SingleCellExperiment::reducedDim(sce, "combined"))
```

We check out the scaling factors for each modality.
A larger value indicates that the corresponding modality's PCs were up-scaled more than the other modalities. 

```{r}
S4Vectors::metadata(sce)$combined
```
:END

:BEGIN mnn
## Batch correction

We use the mutual nearest neighbors (MNN) method to remove differences between blocks in the PC space.
This merges corresponding subpopulations across blocks to simplify downstream steps, e.g., by reducing the number of redundant clusters.

```{r}
sce <- scrapper::correctMnn.se(
    sce,
    block = SummarizedExperiment::colData(sce)[,<%= BLOCK %>],
    more.mnn.args = list(
        num.neighbors = <%= MNN_NUM_NEIGHBORS %>,
        num.steps = <%= MNN_NUM_STEPS %>
    ),
    reddim.type = <%= CHOSEN_PCS %>,
    num.threads = <%= NUM_THREADS %>
)
dim(SingleCellExperiment::reducedDim(sce, "MNN"))
```
:END

:BEGIN cluster-kmeans
# k-means clustering

We use k-means clustering to quickly identify clusters of similar cells.
This uses variance partitioning to initialize the cluster centroids, and then refines the clustering with the Hartigan-Wong algorithm.

```{r}
sce <- scrapper::clusterKmeans.se(
    sce,
    k = <%= CLUSTER_KMEANS_K %>,
    num.threads = <%= NUM_THREADS %>,
    output.name = "kmeans.cluster",
    reddim.type = <%= CHOSEN_PCS %>
)
```

k-means can be used to obtain clusters for downstream interpretation.
More typically, a large `k` is used to partition cells into many small clusters that serve as input into more computationally expensive steps.

```{r}
table(sce$kmeans.cluster)
```

:BEGIN block
We examine the distribution of clusters across blocks.
For blocks corresponding to replicate samples, we should see a more-or-less even distribution of cells across blocks within each cluster.

```{r}
scrapper::countGroupsByBlock(
    sce$kmeans.cluster,
    SummarizedExperiment::colData(sce)[,<%= BLOCK %>],
    normalize.block = TRUE
)
```
:END

:END

:BEGIN neighbor-steps
# Nearest neighbor detection

Several neighbor-related steps downstream of the PCA are bundled into a single function. 
This includes graph-based clustering, t-SNE and/or UMAP, which are run in parallel for speed.
Each of these can also be run separately, e.g., with `runUmap.se()` if more control of the parameters is required.

```{r}
sce <- scrapper::runAllNeighborSteps.se(
    sce,
    more.tsne.args = <%= TSNE_ARGS %>,
    more.umap.args = <%= UMAP_ARGS %>,
    more.build.graph.args = <%= BUILD_SNN_GRAPH_ARGS %>,
    more.cluster.graph.args = <%= CLUSTER_GRAPH_ARGS %>,
    cluster.output.name = "graph.cluster",
    num.threads = <%= NUM_THREADS %>,
    reddim.type = <%= CHOSEN_PCS %>
)
```

:BEGIN cluster-graph
We use graph-based clustering to find communities of related cells.
Specifically, we build a shared-nearest neighbors graph where the nodes are cells and edges are created between neighboring cells.
We then apply a community detection algorithm to find groups of cells that are enriched for intra-group edges compared to inter-group edges.

```{r}
table(sce$graph.cluster)
```

:BEGIN block
We examine the distribution of clusters across blocks.
For blocks corresponding to replicate samples, we should see a more-or-less even distribution of cells across blocks within each cluster.

```{r}
scrapper::countGroupsByBlock(
    sce$graph.cluster,
    SummarizedExperiment::colData(sce)[,<%= BLOCK %>],
    normalize.block = TRUE
)
```
:END

:END

:BEGIN plot-any
Alright, let's get onto the _real_ business of single-cell data analysis -
namely, to make some pretty plots to visualize our dataset.

:BEGIN tsne-simple
```{r plot-tsne, fig.asp=1, fig.wide=TRUE}
scater::plotReducedDim(sce, "TSNE"<%= COLOUR_BY_CHOICE %>)
```
:END

:BEGIN tsne-multi
## $t$-stochastic neighbor embedding {.tabset}

### By cluster {-}

```{r plot-tsne-cluster, fig.asp=1, fig.wide=TRUE}
scater::plotReducedDim(sce, "TSNE", colour_by = <%= CHOSEN_CLUSTER %>)
```

### By block {-}

```{r plot-tsne-block, fig.asp=1, fig.wide=TRUE}
scater::plotReducedDim(sce, "TSNE", colour_by = <%= BLOCK %>)
```
:END

:BEGIN umap-simple
```{r plot-umap, fig.asp=1, fig.wide=TRUE}
scater::plotReducedDim(sce, "UMAP"<%= COLOUR_BY_CHOICE %>)
```
:END

:BEGIN umap-multi
## Uniform manifold approximation and projection {.tabset}

### By cluster {-}

```{r plot-umap-cluster, fig.asp=1, fig.wide=TRUE}
scater::plotReducedDim(sce, "UMAP", colour_by = <%= CHOSEN_CLUSTER %>)
```

### By block {-}

```{r plot-umap-block, fig.asp=1, fig.wide=TRUE}
scater::plotReducedDim(sce, "UMAP", colour_by = <%= BLOCK %>)
```
:END

:END

:END

:BEGIN markers
# Marker detection

:BEGIN overview
For each modality, we create a ranking of marker features for each cluster.
Specifically, we compute effect sizes (e.g., Cohen's $d$, AUC) from pairwise comparisons between clusters.
Then, for each cluster, we summarize the effect sizes into a variety of statistics (e.g., mean, median) that can be used for ranking potential markers. 
Different combinations of effect sizes and summary statistics yield rankings that focus on different types of markers.

```{r}
chosen.clusters <- SummarizedExperiment::colData(sce)[[<%= CHOSEN_CLUSTER %>]]
```

:BEGIN block-text
In the presence of multiple blocks, we compute effect sizes between groups within each block and then average them across blocks.
This ensures that uninteresting differences between blocks do not inflate the effect sizes.
:END
:END

:BEGIN modality
## For <%= MARKER_MODALITY %> {.tabset}

We compute marker statistics for each <%= MARKER_FEATURE_TYPE %>. 

```{r}
<%= MARKER_PREFIX %>.markers <- scrapper::scoreMarkers.se(
    <%= MARKER_SE %>,
    chosen.clusters,<%= MARKER_ARGS %>
    order.by = <%= MARKER_ORDER %>,
:BEGIN block-args
    block = SummarizedExperiment::colData(sce)[,<%= BLOCK %>],
:END
:BEGIN extra-symbol-column
    extra.columns = <%= SYMBOL_FIELD %>,
:END
    num.threads = <%= NUM_THREADS %>
)
names(<%= MARKER_PREFIX %>.markers)
```

For each cluster, we obtain a `DataFrame` that contains one potential ranking of marker genes, along with all of the assorted statistics.

```{r}
scrapper::previewMarkers(<%= MARKER_PREFIX %>.markers[["1"]])
```

We then display the relative log-expression for the top 20 markers of each cluster.

```{r plot-<%= MARKER_PREFIX %>-markers, results='asis'}
for (n in names(<%= MARKER_PREFIX %>.markers)) {
    cat("\n\n### ", n, " {-}\n")
    scater::plotGroupedHeatmap(
        <%= MARKER_SE %>,
        group = data.frame(cluster = chosen.clusters),
        center = TRUE,
        main = paste('Cluster', n),
:BEGIN show-marker-symbols
        features = head(<%= MARKER_PREFIX %>.markers[[n]][[<%= SYMBOL_FIELD %>]], 20),
        swap_rownames = <%= SYMBOL_FIELD %>,
:END
:BEGIN show-marker-ids
        features = head(rownames(<%= MARKER_PREFIX %>.markers[[n]]), 20),
:END
        cluster_cols = FALSE
    )
}
```
:END

:END

# Saving results

We create a directory in which to save results.

```{r save-directory}
result.dir <- "results"
dir.create(result.dir)
```

:BEGIN create-common-metadata
We also set up some extra metadata to add to each result.

```{r}
common.meta <- <%= COMMON_METADATA %>
```
:END

We save our `SingleCellExperiment` object containing most of our results:

:BEGIN save-sce
```{r save-sce}
sce.meta <- list(
    title = "SingleCellExperiment for a simple <%= ANALYSIS_TYPE %> analysis",
    authors = <%= AUTHOR %>,
    type = "single_cell_experiment"
)

augere.core::saveResult(
    sce,
    file.path(result.dir, "sce"), 
:BEGIN merge-metadata
    metadata=c(common.meta, sce.meta)
:END
:BEGIN no-merge-metadata
    metadata=sce.meta
:END
)
```
:END

Saving the `DataFrame`s of QC statistics for each modality:

```{r save-qc}
:BEGIN save-qc-body
<%= MODALITY_LOWER %>.qc.meta <- list(
    title = "Quality control metrics for <%= MODALITY_NAME %> data",
    authors = <%= AUTHOR %>,
    type = "data_frame"
)
augere.core::saveResult(
    <%= MODALITY_LOWER %>.qc.df,
    file.path(result.dir, "qc-<%= MODALITY_LOWER %>"),
:BEGIN merge-metadata
    metadata=c(common.meta, <%= MODALITY_LOWER %>.qc.meta)
:END
:BEGIN no-merge-metadata
    metadata = <%=MODALITY_LOWER %>.qc.meta
:END
)
:END
```

:BEGIN save-markers
Finally, we store the marker rankings for each cluster.

```{r save-markers}
:BEGIN body
dir.create(file.path(result.dir, "markers-<%= MODALITY_LOWER %>"))
for (i in names(<%= MODALITY_LOWER %>.markers)) {
    <%= MODALITY_LOWER %>.marker.meta <- list(
        title = sprintf("Marker <%= MODALITY_FEATURE_TYPE %> for cluster %s", i),
        authors = <%= AUTHOR %>,
        type = "marker_gene_detection",
        marker_gene_detection=list(
            group = i, 
            order = <%= MARKER_ORDER %>, 
            factor = <%= CHOSEN_CLUSTER %>
        )
    )

    augere.core::saveResult(
        <%= MODALITY_LOWER %>.markers[[i]],
        file.path(result.dir, "markers-<%= MODALITY_LOWER %>", i),
:BEGIN merge-metadata
        metadata=c(common.meta, <%= MODALITY_LOWER %>.marker.meta)
:END
:BEGIN no-merge-metadata
        metadata=<%= MODALITY_LOWER %>.marker.meta
:END
    )
}
:END
```
:END

# Session information {-}

```{r}
sessionInfo()
```
