We need of course destiny, scran for preprocessing, and some tidyverse niceties.
library(conflicted) library(destiny) suppressPackageStartupMessages(library(scran)) library(purrr) library(ggplot2) library(SingleCellExperiment)
Let’s use data from the scRNAseq
[1] package. If necessary, install it via BiocManager::install('scRNAseq')
.
[1] Risso D, Cole M (2019). scRNAseq: A Collection of Public Single-Cell RNA-Seq Datasets.
# The parts of the help we’re interested in help('scRNAseq-package', package = 'scRNAseq') %>% repr::repr_html() %>% stringr::str_extract_all(stringr::regex('<p>The dataset.*?</p>', dotall = TRUE)) %>% unlist() %>% paste(collapse = '') %>% knitr::raw_html()
379 cells seems sufficient to see something!
allen <- scRNAseq::ReprocessedAllenData()
We’ll mostly stick to the scran vignette here. Let’s add basic information to the data and choose what to work with.
As scran
expects the raw counts in the counts
assay, we rename the more accurate RSEM counts to counts
.
Our data has ERCC spike-ins in an altExp
slot:
rowData(allen)$Symbol <- rownames(allen) rowData(allen)$EntrezID <- AnnotationDbi::mapIds(org.Mm.eg.db::org.Mm.eg.db, rownames(allen), 'ENTREZID', 'ALIAS') rowData(allen)$Uniprot <- AnnotationDbi::mapIds(org.Mm.eg.db::org.Mm.eg.db, rownames(allen), 'UNIPROT', 'ALIAS', multiVals = 'list') assayNames(allen)[assayNames(allen) == 'rsem_counts'] <- 'counts' assayNames(altExp(allen, 'ERCC'))[assayNames(altExp(allen, 'ERCC')) == 'rsem_counts'] <- 'counts' allen
Now we can use it to renormalize the data. We normalize the counts
using the spike-in size factors and logarithmize them into logcounts
.
allen <- computeSpikeFactors(allen, 'ERCC') allen <- logNormCounts(allen) allen
We also use the spike-ins to detect highly variable genes more accurately:
decomp <- modelGeneVarWithSpikes(allen, 'ERCC') rowData(allen)$hvg_order <- order(decomp$bio, decreasing = TRUE)
We create a subset of the data containing only rasonably highly variable genes:
allen_hvg <- subset(allen, hvg_order <= 5000L)
Let’s create a Diffusion map. For rapid results, people often create a PCA first, which can be stored in your SingleCellExperiment
before creating the Diffusion map or simply created implicitly using DiffusionMap(..., n_pcs = <number>)
.
However, even with many more principal components than necessary to get a nicely resolved Diffusion Map, the close spatial correspondence between diffusion components and genes are lost.
#To go from PCA: reducedDim(allen_hvg, 'pca') <- irlba::prcomp_irlba(t(assay(allen, 'logcounts')), 50)$x
The chosen distance metric has big implications on your results, you should try at least cosine and rankcor.
set.seed(1) dms <- c('euclidean', 'cosine', 'rankcor') %>% #, 'l2' set_names() %>% map(~ DiffusionMap(allen_hvg, distance = ., knn_params = list(method = 'covertree')))
dms %>% imap(function(dm, dist) plot(dm, 1:2, col_by = 'driver_1_s') + ggtitle(dist)) %>% cowplot::plot_grid(plotlist = ., nrow = 1)
grs <- map(dms, gene_relevance)
gms <- imap(grs, function(gr, dist) plot(gr, iter_smooth = 0) + ggtitle(dist)) cowplot::plot_grid(plotlist = gms, nrow = 1)
As you can see, despite the quite different embedding, the rankcor and Cosine diffusion Maps display a number of the same driving genes.
gms[-1] %>% map(~ .$ids[1:10]) %>% purrr::reduce(base::intersect) %>% cat(sep = ' ')
options(readr.show_col_types = FALSE) tryCatch({ httr::GET('https://rest.uniprot.org/uniprotkb/search', query = list( fields = 'accession,gene_names,cc_tissue_specificity', format = 'tsv', query = rowData(allen)$Uniprot[gms$cosine$ids[1:6]] %>% unlist() %>% paste(collapse = ' OR ') )) %>% httr::content(type = 'text/tab-separated-values', encoding = 'utf-8') }, error = function(e) e)
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.