## nolint start suppressPackageStartupMessages({ library(goalie) library(basejump) library(ggplot2) library(bcbioSingleCell) }) prepareTemplate() source("_setup.R") ## nolint end
bcbioSingleCell
objectobject <- import(params[["bcb_file"]]) assert( is(object, "bcbioSingleCell"), validObject(object) ) print(object)
[bcbio][] run data was imported from:
r metadata(object)[["uploadDir"]]
.
sampleData(object)
These are counts of how many reads are assigned to a given cellular barcode. It
is normal for single cell RNA-seq data to contain a large number of low
complexity barcodes. The bcbio pipeline filters out most of these barcodes, and
here we have applied a threshold cutoff of a minimum of
r metadata(object)[["cellularBarcodeCutoff"]]
reads per cell. The unfiltered
read count distributions are shown here.
For high quality data, the proportional histogram should contain a single large peak that represents cells that were encapsulated. If we see a strong shoulder, or a bimodal distribution of the cells, that can indicate a couple problems. It might be that there is free floating RNA, which happens when cells are dying. It could also be that there are a set of cells that failed for some reason. Finally, it could also be that there are biologically different types of cells, and one type is much smaller than the other. If this is the case we would expect to see less RNA being sequenced from the smaller cells.
plotReadsPerCell( object = object, geom = "histogram", interestingGroups = "sampleName" )
An empirical distribution function (ECDF) plot will show the frequency distribution of the reads per cell. You can see that the vast majority of low complexity barcodes plateau at a read depth below 1000 reads per cell.
plotReadsPerCell(object = object, geom = "ecdf")
Now let's assess the distribution of unique molecular identifier (UMI)-deconvoluted counts per cell. In general, the distributions should be relatively uniform per sample. Here we are also including violin and ridgeline plots, with the average number of genes per cell labeled.
markdownHeader("Violin", level = 2L) plotCountsPerCell( object = object, geom = "violin", min = params[["min_counts"]], max = params[["max_counts"]] ) markdownHeader("Ridgeline", level = 2L) plotCountsPerCell( object = object, geom = "ridgeline", min = params[["min_counts"]], max = params[["max_counts"]] ) markdownHeader("Histogram", level = 2L) plotCountsPerCell( object = object, geom = "histogram", min = params[["min_counts"]], max = params[["max_counts"]] ) markdownHeader("ECDF", level = 2L) plotCountsPerCell( object = object, geom = "ecdf", interestingGroups = "sampleName", min = params[["min_counts"]], max = params[["max_counts"]] )
Let's apply this step first and then proceed to evaluating gene detection, mitocondrial transcript abundance, and novelty scores.
object <- filterCells( object = object, minCounts = params[["min_counts"]], maxCounts = params[["max_counts"]] )
Let's take a look at the UMI per cell distributions after this filtering step. Note that we haven't applied very strict filtering here -- we're going to cut off the "low quality" cells based on the gene detection rate, novelty score, and mitochondrial abundance.
plotCountsPerCell( object = object, geom = "histogram", min = params[["min_counts"]], max = params[["max_counts"]] )
Here by "detected", we mean genes with a non-zero count measurement per cell.
Seeing gene detection in the range of 500
-5000
is normal for most
single-cell experiments.
markdownHeader("Violin", level = 2L) plotFeaturesPerCell( object = object, geom = "violin", min = min(params[["min_features"]]), max = max(params[["max_features"]]) ) markdownHeader("Ridgeline", level = 2L) plotFeaturesPerCell( object = object, geom = "ridgeline", min = min(params[["min_features"]]), max = max(params[["max_features"]]) ) markdownHeader("Histogram", level = 2L) plotFeaturesPerCell( object = object, geom = "histogram", min = min(params[["min_features"]]), max = max(params[["max_features"]]) ) markdownHeader("ECDF", level = 2L) plotFeaturesPerCell( object = object, geom = "ecdf", min = min(params[["min_features"]]), max = max(params[["max_features"]]) )
If we graph out the total number of UMI counts per cell vs. the genes detected per cell, we can assess whether there is a large population of low quality cells with low counts and/or gene detection.
plotCountsVsFeatures(object)
Another way to QC the data is to look for less novelty, that is cells that have less genes detected per count than other cells. We can see the samples where we sequenced each cell less have a higher overall novelty, that is because we have not started saturated the sequencing for any given gene for these samples. Outlier cells in these samples might be cells that we have a less complex RNA species than other cells. Sometimes we can detect contamination with low complexity cell types like red blood cells via this metric.
markdownHeader("Violin", level = 2L) plotNovelty( object = object, geom = "violin", min = min(params[["min_novelty"]]) ) markdownHeader("Ridgeline", level = 2L) plotNovelty( object = object, geom = "ridgeline", min = min(params[["min_novelty"]]) ) markdownHeader("Histogram", level = 2L) plotNovelty( object = object, geom = "histogram", min = min(params[["min_novelty"]]) ) markdownHeader("ECDF", level = 2L) plotNovelty( object = object, geom = "ecdf", min = min(params[["min_novelty"]]) )
We evaluate overall mitochondrial gene expression as a biomarker of cellular stress during sample preparation.
markdownHeader("Violin", level = 2L) plotMitoRatio( object = object, geom = "violin", max = max(params[["max_mito_ratio"]]) ) markdownHeader("Ridgeline", level = 2L) plotMitoRatio( object = object, geom = "ridgeline", max = max(params[["max_mito_ratio"]]) ) markdownHeader("Histogram", level = 2L) plotMitoRatio( object = object, geom = "histogram", max = max(params[["max_mito_ratio"]]) ) markdownHeader("ECDF", level = 2L) plotMitoRatio( object = object, geom = "ecdf", max = max(params[["max_mito_ratio"]]) )
object <- filterCells( object = object, minCounts = params[["min_counts"]], maxCounts = params[["max_counts"]], minFeatures = params[["min_features"]], maxFeatures = params[["max_features"]], maxMitoRatio = params[["max_mito_ratio"]], minNovelty = params[["min_novelty"]], nCells = params[["n_cells"]], minCellsPerFeature = params[["min_cells_per_feature"]] )
plotQc(object, geom = "violin")
name <- basenameSansExt(params[["bcb_file"]]) assignAndSaveData( name = paste(name, "filtered", sep = "_"), object = object, dir = params[["data_dir"]] )
export( object = object, con = params[["output_dir"]], compress = TRUE )
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.