This file describes the different steps to perform the second part of the single cell RNAseq data analysis training course for the EBAII n1 2023, covering these steps :
## Here, we diretly copy the whole RESOURCES directory from the origin (2325_ebaii project) to its destination (your project)
cp -r /shared/projects/2325_ebaii/SingleCell/TD_DATA/RESOURCES /shared/projects/<your_project>/TD/Set your working directory in your TD output directory (golf is mine!)
Setting parameters
## Data directory
input_matrix <- '../DATA/TD3A/GSM4861194_gex_2_raw_gene_expression.tsv.gz'
## Sample name
samplename <- 'TD3A'
## Seed for the RNG
my_seed <- 1337We can load the matrix
## R can read compressed text file without hassle
scmat <- as.matrix(
read.table(
file = input_matrix,
header = TRUE,
sep = '\t'))
## Displaying its size in-memory (this is a basic matrix)
format(object.size(scmat), units = "auto")[1] "545.7 Mb"
A quick look at its content
int [1:31053, 1:4587] 0 0 0 0 0 0 0 0 0 0 ...
- attr(*, "dimnames")=List of 2
..$ : chr [1:31053] "Xkr4" "Gm1992" "Gm37381" "Rp1" ...
..$ : chr [1:4587] "AAACCTGAGACGCTTT.1" "AAACCTGAGGCATTGG.1" "AAACCTGGTCAACATC.1" "AAACCTGTCGAGGTAG.1" ...
Wait a second …
We have a raw count matrix that’s already
been filtered for empty droplets. What may we do with it before creating
a Seurat object from it ?
Soup-contributing features (Top 20) :
| | est| counts|
|:-------|---------:|------:|
|Actb | 0.0080580| 132061|
|Gm42418 | 0.0069444| 113810|
|Eef1a1 | 0.0055896| 91607|
|Malat1 | 0.0051948| 85137|
|Ppia | 0.0049767| 81562|
|Tmsb10 | 0.0046515| 76232|
|mt-Co1 | 0.0044872| 73540|
|Ptma | 0.0044203| 72443|
|mt-Atp8 | 0.0040750| 66784|
|Rplp0 | 0.0038984| 63890|
|Uba52 | 0.0038012| 62297|
|Gm10076 | 0.0037717| 61814|
|Rps24 | 0.0037488| 61438|
|mt-Co2 | 0.0036859| 60407|
|Pfn1 | 0.0035459| 58113|
|Fau | 0.0034193| 56038|
|Ubb | 0.0033782| 55364|
|Rps16 | 0.0032381| 53068|
|Rpl13 | 0.0031730| 52002|
|Rps3a1 | 0.0031379| 51426|
Soup fraction : 0.01
We can create our Seurat object
## Create Seurat object
sobj <- Seurat::CreateSeuratObject(
counts = scmat,
project = samplename,
assay = 'RNA')
## Remove the matrix to free some RAM
rm(scmat)
## Displaying sobj size in-memory (matrices are converted to dgCMatrix)
format(object.size(sobj), units = "auto")[1] "179.4 Mb"
Let’s describe it
OBJECT VERSION : 5.0.0
PROJECT : [TD3A]
[ASSAYS]
ASSAY 1 : [RNA] [ACTIVE]
SLOT 1 : [counts] Dims:[31053 x 4587] Range:[0.00-3103.00]
Counts : 16388810
Sparsity : 94.72482%
SLOT 2 : [data] Dims:[31053 x 4587] Range:[0.00-3103.00]
Sparsity : 94.72482%
SLOT 3 : [scale.data] Dims:[0 x 0]
[DIMREDS]
[BARCODES METADATA]
orig.ident Freq
----------- -----
TD3A 4587
NA 0
nCount_RNA
Min. 1st Qu. Median Mean 3rd Qu. Max.
502 1986 2397 3573 3134 48875
nFeature_RNA
Min. 1st Qu. Median Mean 3rd Qu. Max.
22 1291 1476 1638 1733 5975
Here we will focus on the barcode/cell-level count metrics :
Both were automatically computed by Seurat when creating the object (but you already observed it, isn’t it ?)
For graphical purpose, we will convert count metrics (nCount_RNA) to log10
Question : Why this “+1” ?
We can visualize these metrics’ distribution as violins …
Question : Please describe these distributions (modes, tailing, outliers, …)
… but for my own part, I find them hard to read. I prefer histograms.
Still hard to read… let’s focus on the left part
Question : Which thresholds would you pick to select cells of interest, based on nCount_RNA ?
## . For the left part of the distribution (very low
## counts), I would take 1000 as a lower bound
## . For the right part ... this is highly debatable
## due to the high spread of this right tailing.
## Removing it infers the risk of discarding somme
## cell subtypes with different characteristics than
## the mode of the distribution. Keeping them may
## just pollute the dataset... I'm feeling
## optimistic, I would take all values (so the upper
## threshold would be the distribution max), but will
## keep in mind that I may have to adapt my anaylsis
## to this variation in global expression levels.Let’s set this nCount_RNA selection range and display it
## Setting nCount conservation range
ncount_range <- c(1000, max(sobj$nCount_RNA))
## Replot histogram with cutoffs
hist(x = sobj$nCount_RNA,
breaks = 1000,
xlim = c(0,10000))
abline(v = ncount_range, lty = 3, col = "red")Question : Which thresholds would you pick to select cells of interest, based on nFeature_RNA ?
## . For the left part of the distribution (very low
## counts), I would take 750 as a lower bound.
## . For the right part ... for the same reason,
## I would take all values (so the distribution
## max)Let’s set this nFeature_RNA selection range and display it
## Setting nFeature conservation range
nfeature_range <- c(750, max(sobj$nFeature_RNA))
## Replot histogram with cutoffs
hist(x = sobj$nFeature_RNA, breaks = 1000)
abline(v = nfeature_range, lty = 3, col = "red")Here we will build 3 new metrics that correspond to the fraction of expression (in counts) of three categories of gene signatures among the global expression. These are
We will need a function that eases the computational step for you :
## Load the genelist
mito_symbols <- readRDS(file = '../RESOURCES/GENELISTS/mus_musculus_mito_symbols_20191015.rds')
## Compute the metric
sobj$percent_mt <- EBAII.n1.SC.helper::count_rate_metric(
sobj = sobj,
features = mito_symbols,
assay = 'RNA')
## Summarize the computed values
summary(object = sobj$percent_mt) Min. 1st Qu. Median Mean 3rd Qu. Max.
0.00000 0.01985 0.02489 0.02997 0.03125 0.97250
## Load the genelist
ribo_symbols <- readRDS(file = '../RESOURCES/GENELISTS/mus_musculus_cribo_symbols_20191015.rds')
## Compute the metric
sobj$percent_rb <- EBAII.n1.SC.helper::count_rate_metric(
sobj = sobj,
features = ribo_symbols,
assay = 'RNA')
## Summarize the computed values
summary(object = sobj$percent_rb) Min. 1st Qu. Median Mean 3rd Qu. Max.
0.00000 0.07884 0.09577 0.10880 0.12167 0.42010
## Load the genelist
stress_symbols <- readRDS(file = '../RESOURCES/GENELISTS/mus_musculus_stress_symbols_20200224.rds')
## Compute the metric
sobj$percent_st <- EBAII.n1.SC.helper::count_rate_metric(
sobj = sobj,
features = stress_symbols,
assay = 'RNA')
## Summarize the computed values
summary(object = sobj$percent_st) Min. 1st Qu. Median Mean 3rd Qu. Max.
0.00000 0.02946 0.03338 0.03368 0.03749 0.10198
We can then visualize these metrics as violins (as well as older ones)
Seurat::VlnPlot(object = sobj, features = c(
'nFeature_RNA', 'nCount_RNA',
'log10_nCount_RNA', 'percent_mt',
'percent_rb', 'percent_st'),
ncol = 3)Did you notice I preferred histograms ? :)
Hard to read, let’s slash it to focus on lower values
Let’s observe the %mito distribution in the data space
## Performing the "quick viz" and saving
## it (to speed up next plots)
VIZ <- EBAII.n1.SC.helper::QnD_viz(
sobj = sobj,
features = 'percent_mt',
return_object = TRUE)Let’s set this %mito selection range to [0, 5%] and display it
## Setting %mito conservation range
pc_mito_range <- c(0, .05)
# pc_mito_range <- c(0, .04)
## Replot histogram with cutoffs
hist(x = sobj$percent_mt,
breaks = 1000,
xlim = c(0,.1))
abline(v = pc_mito_range,
lty = 3,
col = "red")Let’s observe the %ribo distribution in the data space
## Performing the "quick viz" faster with
## the "VIZ" Seurat object
EBAII.n1.SC.helper::QnD_viz(
sobj = VIZ,
slot = NULL, dimred = 'umap',
features = 'percent_rb')We don’t have any clue to consider thise %ribo variations as biological or technical artefacts… So we will keep all values : [0, 1]
Let’s observe the %stress distribution in the data space
## Performing the "quick viz" faster with
## the "VIZ" Seurat object
EBAII.n1.SC.helper::QnD_viz(
sobj = VIZ,
slot = NULL, dimred = 'umap',
features = 'percent_st')Let’s set this %stress selection range to [0, 6%] and display it
## Setting %stress conservation range
pc_stress_range <- c(0, .06)
## Replot histogram with cutoffs
hist(x = sobj$percent_st,
breaks = 1000,
xlim = c(0,.1))
abline(v = pc_stress_range, lty = 3,
col = "red")We can delete the VIZ object
But we’re not done yet !
Single cell data from droplet-based technologies can be biased in so many ways…
Evaluating their quality, increasing the knowledge of biases affecting the values is mandatory to perform an analysis of quality !
As we are analysis cells independently, they may each be in a different phase of their cell cycle. Interestingly, the effect of this cell cycle step on the cell genes expression is strong, and can bias the data. In order to assess (and maybe, remove) this bias, we have to quantify it.
We will perform this estimation thanks to heuristics based on knowledge : Seurat includes a method that evaluates the cell cycle phase of cells through scores for the S and G2M phases, each based on phase-specific gene signatures.
Some of these require the evaluation of genes which expression is expected to be null : that’s the reason why we did not filter out for unexpressed genes till now.
Let’s perform this estimation. But how ?
We will actually use a helper function to ease up the process :
Run it !
## Load the cell cycle reference genes lists
seu.cc <- readRDS(file = '../RESOURCES/GENELISTS/mus_musculus_Seurat_cc.genes_20191031.rds')
## Perform the estimation
sobj <- EBAII.n1.SC.helper::CC_Seurat(
sobj = sobj, assay = 'RNA',
seurat_cc_genes = seu.cc, SmG2M = TRUE,
nbin = 20, my_seed = my_seed)Description of the object to see the data added
OBJECT VERSION : 5.0.0
PROJECT : [TD3A]
[BARCODES METADATA]
orig.ident Freq
----------- -----
TD3A 4587
NA 0
nCount_RNA
Min. 1st Qu. Median Mean 3rd Qu. Max.
502 1986 2397 3573 3134 48875
nFeature_RNA
Min. 1st Qu. Median Mean 3rd Qu. Max.
22 1291 1476 1638 1733 5975
log10_nCount_RNA
Min. 1st Qu. Median Mean 3rd Qu. Max.
2.702 3.298 3.380 3.429 3.496 4.689
percent_mt
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.00000 0.01985 0.02489 0.02997 0.03125 0.97250
percent_rb
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.00000 0.07884 0.09577 0.10880 0.12167 0.42010
percent_st
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.00000 0.02946 0.03338 0.03368 0.03749 0.10198
CC_Seurat_S.Score
Min. 1st Qu. Median Mean 3rd Qu. Max.
-2.6505 -0.3444 -0.2557 -0.2278 -0.1815 4.3548
CC_Seurat_G2M.Score
Min. 1st Qu. Median Mean 3rd Qu. Max.
-2.04357 -0.21453 -0.15637 -0.02719 -0.10224 12.76613
CC_Seurat_Phase Freq
---------------- -----
G1 4259
G2M 209
S 119
NA 0
CC_Seurat_SmG2M.Score
Min. 1st Qu. Median Mean 3rd Qu. Max.
-13.3524 -0.1735 -0.1036 -0.2006 -0.0439 3.2976
As usual, we can visualize the results as violins :
Seurat::VlnPlot(object = sobj,
features = c('CC_Seurat_S.Score',
'CC_Seurat_G2M.Score',
'CC_Seurat_SmG2M.Score'))But it’s not that easy to interpret… Let’s plot it in the cell space
## Using the 'features' parameter to plot the SmG2M score (continuous data)
## Here, we will keep the modified Seurat object to speed up further plots
VIZ <- EBAII.n1.SC.helper::QnD_viz(
sobj = sobj,
features = 'CC_Seurat_SmG2M.Score',
return_object = TRUE)## Using the 'group_by' parameter to plot the estimated phases (categorical data)
## Here, we recycle keep the VIZ Seurat object that contains everything to perform the plot without computing it again
EBAII.n1.SC.helper::QnD_viz(sobj = VIZ, slot = NULL, dimred = 'umap',
group_by = 'CC_Seurat_Phase')