--- title: "nnSVG tutorial" author: - name: Lukas M. Weber affiliation: &id1 "Johns Hopkins Bloomberg School of Public Health, Baltimore, MD, USA" - name: Stephanie C. Hicks affiliation: &id1 package: nnSVG output: BiocStyle::html_document vignette: > %\VignetteIndexEntry{nnSVG tutorial} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- # Introduction `nnSVG` is a method for scalable identification of spatially variable genes (SVGs) in spatially-resolved transcriptomics data. The `nnSVG` method is based on nearest-neighbor Gaussian processes ([Datta et al., 2016](https://www.tandfonline.com/doi/full/10.1080/01621459.2015.1044091), [Finley et al., 2019](https://www.tandfonline.com/doi/full/10.1080/10618600.2018.1537924)) and uses the BRISC algorithm ([Saha and Datta, 2018](https://onlinelibrary.wiley.com/doi/full/10.1002/sta4.184)) for model fitting and parameter estimation. `nnSVG` allows identification and ranking of SVGs with flexible length scales across a tissue slide or within spatial domains defined by covariates. The method scales linearly with the number of spatial locations and can be applied to datasets containing thousands or more spatial locations. `nnSVG` is implemented as an R package within the Bioconductor framework, and is available from [Bioconductor](https://bioconductor.org/packages/nnSVG). More details describing the method are available in our preprint, available from [bioRxiv](https://www.biorxiv.org/content/10.1101/2022.05.16.492124v1). # Installation The following code will install the latest release version of the `nnSVG` package from Bioconductor. Additional details are shown on the [Bioconductor](https://bioconductor.org/packages/nnSVG) page. ```{r, eval=FALSE} if (!requireNamespace("BiocManager", quietly = TRUE)) { install.packages("BiocManager") } BiocManager::install("nnSVG") ``` The latest development version can also be installed from the `devel` version of Bioconductor or from [GitHub](https://github.com/lmweber/nnSVG). # Input data format In the examples below, we assume the input data are provided as a [SpatialExperiment](https://bioconductor.org/packages/SpatialExperiment) Bioconductor object. In this case, the outputs are stored in the `rowData` of the `SpatialExperiment` object. However, the inputs can also be provided as a numeric matrix of normalized and transformed counts (e.g. log-transformed normalized counts, also known as logcounts) and a numeric matrix of spatial coordinates. To provide the inputs as numeric matrices, please install the development version of the package from [GitHub](https://github.com/lmweber/nnSVG) or the `devel` version of Bioconductor (which will become the new Bioconductor release version in October 2022). # Tutorial ## Standard analysis ### Run nnSVG Here we show a short example demonstrating how to run `nnSVG`. For faster runtime in this example, we subsample the dataset and run `nnSVG` on only a small number of genes. For a full analysis, the subsampling step can be skipped. ```{r, message=FALSE} library(SpatialExperiment) library(STexampleData) library(scran) library(nnSVG) library(ggplot2) ``` ```{r} # load example dataset from STexampleData package spe <- Visium_humanDLPFC() dim(spe) ``` ```{r} # preprocessing steps # keep only spots over tissue spe <- spe[, colData(spe)$in_tissue == 1] dim(spe) # skip spot-level quality control, since this has been performed previously # on this dataset # filter low-expressed and mitochondrial genes # using default filtering parameters spe <- filter_genes(spe) # calculate logcounts (log-transformed normalized counts) using scran package # using library size factors spe <- computeLibraryFactors(spe) spe <- logNormCounts(spe) assayNames(spe) ``` ```{r} # select small set of random genes and several known SVGs for # faster runtime in this example set.seed(123) ix_random <- sample(seq_len(nrow(spe)), 10) known_genes <- c("MOBP", "PCP4", "SNAP25", "HBB", "IGKC", "NPY") ix_known <- which(rowData(spe)$gene_name %in% known_genes) ix <- c(ix_known, ix_random) spe <- spe[ix, ] dim(spe) ``` ```{r} # run nnSVG # set seed for reproducibility set.seed(123) # using a single thread in this example spe <- nnSVG(spe) # show results rowData(spe) ``` ### Investigate results The results are stored in the `rowData` of the `SpatialExperiment` object. The main results of interest are: - `LR_stat`: likelihood ratio (LR) statistics - `rank`: rank of top SVGs according to LR statistics - `pval`: p-values from asymptotic chi-squared distribution with 2 degrees of freedom - `padj`: p-values adjusted for multiple testing, which can be used to define a cutoff for statistically significant SVGs (e.g. `padj` <= 0.05) - `prop_sv`: effect size, defined as proportion of spatial variance out of total variance ```{r} # number of significant SVGs table(rowData(spe)$padj <= 0.05) ``` ```{r} # show results for top n SVGs rowData(spe)[order(rowData(spe)$rank)[1:10], ] ``` ```{r, fig.width=5, fig.height=5} # plot spatial expression of top-ranked SVG ix <- which(rowData(spe)$rank == 1) ix_name <- rowData(spe)$gene_name[ix] ix_name df <- as.data.frame( cbind(spatialCoords(spe), expr = counts(spe)[ix, ])) ggplot(df, aes(x = pxl_col_in_fullres, y = pxl_row_in_fullres, color = expr)) + geom_point(size = 0.8) + coord_fixed() + scale_y_reverse() + scale_color_gradient(low = "gray90", high = "blue", trans = "sqrt", breaks = range(df$expr), name = "counts") + ggtitle(ix_name) + theme_bw() + theme(plot.title = element_text(face = "italic"), panel.grid = element_blank(), axis.title = element_blank(), axis.text = element_blank(), axis.ticks = element_blank()) ``` ## With covariates ### Run nnSVG `nnSVG` can also be run on datasets with known covariates, such as spatial domains or cell types. In this case, the method will identify SVGs within regions defined by the selected covariates, e.g. within spatial domains. Here we run a short example demonstrating this functionality. As above, for faster runtime in this example, we subsample a very small subset of the data. For a full analysis, the subsampling step can be skipped. ```{r, message=FALSE} library(SpatialExperiment) library(STexampleData) library(scran) library(nnSVG) library(ggplot2) ``` ```{r} # load example dataset from STexampleData package spe <- SlideSeqV2_mouseHPC() dim(spe) ``` ```{r} # preprocessing steps # remove spots with NA cell type labels spe <- spe[, !is.na(colData(spe)$celltype)] dim(spe) # check cell type labels table(colData(spe)$celltype) # filter low-expressed and mitochondrial genes # using adjusted filtering parameters for this platform spe <- filter_genes( spe, filter_genes_ncounts = 1, filter_genes_pcspots = 1, filter_mito = TRUE ) dim(spe) # calculate log-transformed normalized counts using scran package # using library size normalization spe <- computeLibraryFactors(spe) spe <- logNormCounts(spe) assayNames(spe) ``` ```{r} # select small set of random genes and several known SVGs for # faster runtime in this example set.seed(123) ix_random <- sample(seq_len(nrow(spe)), 10) known_genes <- c("Cpne9", "Rgs14") ix_known <- which(rowData(spe)$gene_name %in% known_genes) ix <- c(ix_known, ix_random) spe <- spe[ix, ] dim(spe) ``` ```{r} # run nnSVG with covariates # create model matrix for cell type labels X <- model.matrix(~ colData(spe)$celltype) dim(X) stopifnot(nrow(X) == ncol(spe)) # set seed for reproducibility set.seed(123) # using a single thread in this example spe <- nnSVG(spe, X = X) # show results rowData(spe) ``` Note that if there are any empty levels in the factor used to create the design matrix, these can be removed as follows: ```{r} # create model matrix after dropping empty factor levels X <- model.matrix(~ droplevels(as.factor(colData(spe)$celltype))) ``` ### Investigate results ```{r} # number of significant SVGs table(rowData(spe)$padj <= 0.05) ``` ```{r} # show results for top n SVGs rowData(spe)[order(rowData(spe)$rank)[1:10], ] ``` ```{r, fig.width=5, fig.height=3.25} # plot spatial expression of top-ranked SVG ix <- which(rowData(spe)$rank == 1) ix_name <- rowData(spe)$gene_name[ix] ix_name df <- as.data.frame( cbind(spatialCoords(spe), expr = counts(spe)[ix, ])) ggplot(df, aes(x = xcoord, y = ycoord, color = expr)) + geom_point(size = 0.1) + coord_fixed() + scale_color_gradient(low = "gray90", high = "blue", trans = "sqrt", breaks = range(df$expr), name = "counts") + ggtitle(ix_name) + theme_bw() + theme(plot.title = element_text(face = "italic"), panel.grid = element_blank(), axis.title = element_blank(), axis.text = element_blank(), axis.ticks = element_blank()) ``` # Session information ```{r} sessionInfo() ```