scRNA-系统学习单细胞转录组测序scRNA-Seq(四)

刘小泽写于19.3.26 陌生的领域就像一张白纸,怎么画就看自己了

前言

关于单细胞测序,我们已经知道了它和bulk RNA-seq的区别,简单说就是scRNA体现异质性(个体),bulk体现平均程度(总体),因此利用bulk RNA不能区分一个样本中的不同细胞类型

scRNA&bulk RNAseq

做scRNA的最大的好处就是可以"对症下药”(当然这也是最理想的情况=》什么细胞配合什么种类或者剂量的药物)

对症下药

** Biomarker:**A biological molecule found in blood, other body fluids, or tissues that is a sign of a normal or abnormal process, or of a condition or disease. A biomarker may be used to see how well the body responds to a treatment for a disease or condition. Also called molecular marker and signature molecule.

一般在下机后会得到一个表达矩阵,行为基因,列为细胞样本(根据测序平台不同细胞数不同,目前商业化平台有10X和BD,一般都能达到2000-8000细胞量,可以覆盖绝大部分组织的single cell群体类型)。 还包括两个协变量(covariates:解释变量,不为实验者所操纵,但仍影响实验结果):gene-level(比如GC含量)和cell-level(比如细胞测序的批次信息)。

单细胞和bulk转录组表达矩阵主要有两点不同:

  • bulk的表达矩阵是reads比对到基因的数目;单细胞的表达矩阵是reads比对到某个细胞的某个基因的数目(barcode用来区分细胞;UMI用来区分转录本/基因)

  • 单细胞存在许多的0,有两个可能:第一种是真的基因在细胞中不表达(参与细胞分化的基因并不是在每个cell cycle都表达);第二种是技术误差,基因实际表达但测序没有测到(也就是"dropouts”)

从头开始慢慢理解

这一部分暂时不讨论细节,先看Big Picture

探索表达矩阵

The matrix of counts is the number of sequenced reads aligned to each gene and each sample

假设这个矩阵有10个基因(Lamp5, Fam19a1, Cnr1, Rorb, Sparcl1, Crym, Ddah1, Lmo3, Serpine2, Pde1a) 和5个细胞样本(SRR2140028, SRR2140022, SRR2140055, SRR2140083, SRR2139991)

表达矩阵如下:

> counts
         SRR2140028 SRR2140022 SRR2140055 SRR2140083 SRR2139991
Lamp5            10         11          0          0          8
Fam19a1          11          9          0          6          0
Cnr1              0          0          0         12          0
Rorb              0          8          0          0          0
Sparcl1           0          7          0         13          0
Crym              8          9         10         12          5
Ddah1            16          0          0          8          0
Lmo3              0          0          8         13          0
Serpine2          8          0          0          0          0
Pde1a             0         14          8         11          0
# head of count matrix
counts[1:3, 1:3]

# count of specific gene and cell
reads <- counts["Cnr1", "SRR2140055"]

# overall proportion of zero counts 
pZero <- mean(counts==0)

# cell library size
libsize <- colSums(counts)

The cell coverage is the average number of expressed genes that are greater than zero in each cell.

目的:ggplot2画出cell coverage

假设目前有一个关于细胞样本的协变量(cell-level),内容如下:

> cell_info
                names batch patient 
SRR2140028 SRR2140028     1       a      
SRR2140022 SRR2140022     1       a     
SRR2140055 SRR2140055     2       b     
SRR2140083 SRR2140083     2       c     
SRR2139991 SRR2139991     2       c      

先计算coverage,然后加在cell_info的最后

# find cell coverage 计算count大于0的平均值
# 先将表达量为0的值赋值一个NA,然后计算时忽略掉它们
is.na(counts)=counts==0
coverage <- colMeans(counts,na.rm=T)

cell_info$coverage <- coverage
library(ggplot2)
ggplot(cell_info, aes(x = names, y = coverage)) + 
  geom_point() +
  ggtitle('Cell Coverage') + 
  xlab('Cell Name') + 
  ylab('Coverage')

看看大体的流程

测序=》single cell 发展历史

图片来自:Exponential scaling of single-cell RNA-seq in the past decade. (2018 Nature Protocols)

2009-2017不到十年,已报道的细胞研究数目就从1个飞跃至一百万个

scRNA发展

尽管这些方法各不相同,但原理的主要不同之处有两个:分子定量(Quantification)和捕获方法(Capture)

  • RNA分子定量(Quantification):主要是两种技术(full-length和tag-based) The full-length:基于全长,试图获取每个转录本覆盖度一致且独特的全长片段; The tag-based:基于标签探针,捕获RNA的5’或3’端序列
  • 捕获方法(Capture):不仅决定了细胞的筛选方式以及实验的通量,同时也间接反映了测序外的其他信息。目前主要有三种捕获方法: 微孔 microwell 微流 microfluidic 微滴 droplet

详细看这里:https://hemberg-lab.github.io/scRNA.seq.course/introduction-to-single-cell-rna-seq.html

目前两大商业化单细胞测序公司:

10X Genomics Chromium 利用微流控油滴包裹barcode标记实现高通量细胞捕获和标记,一次最多捕获8万细胞。特点就是:细胞通量高**,**建库成本低**,**捕获周期短 主要用于**细胞分型**和**标记因子**的鉴定,从而实现对细胞群体的划分与细胞群体间基因表达差异的检测,此外该技术还可以**预测**细胞分化与**研究**发育轨 迹。

BD Rhapsody 基于微孔平台,通过刚性磁珠进行单细胞捕获(捕获后磁珠可以保存3个月以上);利用成像系统对单细胞分离进行质控。特点是:细胞样本损伤小;质控可视化,监控双细胞比例;细胞捕获较稳定;可以结合蛋白表达量分析转录组;可以设计panel分析特定基因

质控

目的就是减少技术误差,去掉有问题的细胞,一般反映细胞是否有问题主要看两个方面:

  • library size:能比对到每个细胞的总reads数(一个ibrary指一个细胞)
  • cell coverage:每个细胞中表达基因的平均数
标准Workflow

来自: Bioconductor workflow for single-cell RNA sequencing

可以从下图看到:

第一步就是基因和细胞层次的标准化(normalization):消除与研究无关的生物和技术影响;

第二步是降维,基因数量决定维度,需要从J个基因降到K个基因,实现了两个事情,一是维度降低可以更好掌控数据,二是保留感兴趣的特征同时减少了背景噪音;

第三步是聚类分群,按照降维后的矩阵(K x N,N是细胞数量),这一步给了每个细胞一个cluster number;

第四步是差异分析,找到biomarker,也就是两组细胞的表达差异基因

workflow

慢慢翻开细枝末节

加载、创建、获取single-cell数据集

单细胞分析利用的几个包都是高度整合的,里面需要用到许多S4对象,只有理解好这些对象才能看懂分析流程

首先了解一下SingleCellExperiment对象

介绍:

https://www.bioconductor.org/packages/devel/bioc/vignettes/SingleCellExperiment/inst/doc/intro.html

SingleCellExperiment(SCE)是一个S4对象,作者是Davide Risso and Aaron Lun 。官网称它是 light-weight container for single-cell genomics data ,看出它是一个容器,可以装单细胞组学数据。那么就知道和我们日常接触的一般表达矩阵不同,它肯定还包含一些表型信息(对象Object 简单理解就是:包含多个数据框、矩阵或者列表,用到哪个提取哪个。数据框取子集我们用$,对象取子集我们要用@)

之前知道了单细胞数据一般有三个矩阵(表达矩阵raw counts、协变量信息gene-level & cell-level metadata),这个对象就可以将这三个同时存储在一个容器中,另外还有一些其他重要信息,比如:spike-in info,dimentionality reduction coordinates,size factors for each cell

下载、加载包

if (!requireNamespace("BiocManager", quietly = TRUE))
    install.packages("BiocManager")
BiocManager::install("SingleCellExperiment", version = "3.8")
library(SingleCellExperiment)
创建对象

三个要素:表达矩阵、行名、列名

> # 先设计矩阵(假设counts值服从泊松分布)
> counts <- matrix(rpois(8,lambda = 10),ncol = 2,nrow = 4)
> rownames(counts) <- c("Lamp5","Fam19a1","Cnr1","Rorb")
> colnames(counts) <- c("SRR2140021","SRR2140023")
> counts
        SRR2140021 SRR2140023
Lamp5            4          6
Fam19a1          9         13
Cnr1            14          5
Rorb            15          7

# 创建对象第一种方法
#第一个参数assays就是取表达矩阵的list
> sce <- SingleCellExperiment(assays = list(counts = counts),
+                             rowData = data.frame(gene = rownames(counts)),
+                             colData = data.frame(cell = colnames(counts)))
> sce
class: SingleCellExperiment 
dim: 4 2 
metadata(0):
assays(1): counts
rownames(4): Lamp5 Fam19a1 Cnr1 Rorb
rowData names(1): gene
colnames(2): SRR2140021 SRR2140023
colData names(1): cell
reducedDimNames(0):
spikeNames(0):

# 创建对象第二种方法(先构建bulk RNA的一个对象SummarizedExperiment,然后转换格式)
> se <- SummarizedExperiment(assays = list(counts = counts))
# as =》 Force an Object to Belong to a Class 
> sce <- as(se,"SingleCellExperiment")
上面都是自己创造的模拟数据集,下面看一个真的数据集

来自:Adult mouse cortical cell taxonomy revealed by single cell transcriptomics(2016,Nature Neuroscience)

library(scRNAseq);data(allen)
# 其中包含了379个成年雄性小鼠不同神经元的视觉皮层细胞数据
# allen数据集是一个SummarizedExperiment对象,需要先转换
> sce <- as(allen,"SingleCellExperiment")
> sce
class: SingleCellExperiment 
dim: 20908 379 
metadata(2): SuppInfo which_qc
assays(4): tophat_counts
  cufflinks_fpkm rsem_counts
  rsem_tpm
rownames(20908): 0610007P14Rik
  0610009B22Rik ... Zzef1 Zzz3
rowData names(0):
colnames(379): SRR2140028
  SRR2140022 ... SRR2139341
  SRR2139336
colData names(22): NREADS
  NALIGNED ... Animal.ID
  passes_qc_checks_s
reducedDimNames(0):
spikeNames(0):
# size factors
> sizeFactors(sce) <- colSums(assay(sce))

质控

目的:get out of technical issues and biases

利用的数据集来自:Batch effects and the effective design of single-cell gene expression studies. (2017, Tung et al. )

6 RNA-sequencing datasets per individual : 3 bulk & 3 single-cell (on C1 plates) and each dataset was sequenced in a different batch【Fluidigm C1 platform的低通量的微流控技术,能在捕获细胞时可视化的控制empty wells or doublets】

Three C1 96 well-integrated fluidic circuit (IFC) replicates were collected from each of the three Yoruba individuals.

表达矩阵下载:ftp://ftp.ncbi.nlm.nih.gov/geo/series/GSE77nnn/GSE77288/suppl/GSE77288_reads-raw-single-per-sample.txt.gz

library("data.table")
library("dplyr")
reads_raw <- fread("GSE77288_reads-raw-single-per-sample.txt")
setDF(reads_raw)
dim(reads_raw)
# 得到metadata
anno <- reads_raw %>%
    select(individual:well) %>%
    mutate(batch = paste(individual, replicate, sep = "."),
           sample_id = paste(batch, well, sep = "."))
head(anno)
dim(anno)
# 得到表达矩阵
reads <- reads_raw %>%
    select(starts_with("ENSG"), starts_with("ERCC")) %>%
    t
colnames(reads) <- anno$sample_id
reads[1:5, 1:5]
dim(reads)
# 构建对象
sce <- SingleCellExperiment(assays = list(counts = reads),
                            rowData = data.frame(gene = rownames(reads)),
                            colData = anno)

# 去掉样本中表达量均为0的基因
sce2 <- sce[apply(counts(sce), 1, sum) >0,]
dim(sce);dim(sce2)#大约1700个基因不表达
# 添加Spike信息
isSpike(sce2, "ERCC") <- grepl("^ERCC-", rownames(sce2))

> sce2
class: SingleCellExperiment 
dim: 18726 864 
metadata(0):
assays(1): counts
rownames(18726): ENSG00000237683 ENSG00000187634 ... ERCC-00170
  ERCC-00171
rowData names(9): gene is_feature_control ... total_counts
  log10_total_counts
colnames(864): NA19098.r1.A01 NA19098.r1.A02 ... NA19239.r3.H11
  NA19239.r3.H12
colData names(41): individual replicate ...
  pct_counts_in_top_200_features_ERCC
  pct_counts_in_top_500_features_ERCC
reducedDimNames(0):
spikeNames(1): ERCC

# Calculate QC metrics
library(scater)
# 常用的feature control:such as ERCC spike-in sets or mitochondrial genes
sce2 <- calculateQCMetrics(sce2, feature_controls = list(ERCC = isSpike(sce2, "ERCC")))
# 结果会在colData这一部分增加许多列,帮助过滤低表达基因和样本
names(colData(sce2))

进行简单的细胞过滤:可以根据**文库大小(Library size)和批次(Batch)**进行

# 1.Filter cells with Library size
# 设置阈值(文库大小基本在2M reads以上,可以设置文库大小的5%作为过滤)
# 阈值的设定没有固定标准,需要考虑具体数据集
threshold <- 100000
# 画密度图
plot(density(sce2$total_counts), main = 'Density - total_counts')
abline(v = threshold)
# 直方图
#####################################
## 一个非常关键的问题:怎么决定哪里作为阈值?
# 理论上细胞分布是遵循泊松分布的“钟形曲线”
# 如果表达量太高(最右侧),可能是一孔两个细胞(doublets);
# 如果表达量太低(最左侧),可能是细胞质量不好或没有细胞
#####################################
hist(
    sce2$total_counts,
    breaks = 100
)
abline(v=1.3e6, col="red")

# 要保留的细胞
keep <- sce2$total_counts > threshold
# 统计过滤信息
> table(keep)
keep
FALSE  TRUE 
  180   684 

# 2.Filter cells with Batch[结果见下图]
# 方法一:
cDataFrame <- as.data.frame(colData(sce2))
# plot cell data
ggplot(cDataFrame, aes(x = total_counts, y = total_counts_ERCC, col = batch)) + 
    geom_point()
# 方法二:
plotColData(
    sce2,
    y = "total_counts_ERCC",
    x = "total_counts",
    colour_by = "batch")
# 要保留的细胞
> filter_by_ERCC <- 
    sce2$batch != "NA19098.r2" & sce2$pct_counts_ERCC < 25
> table(filter_by_ERCC)
filter_by_ERCC
FALSE  TRUE 
  103   761 

> filter_by_MT <- sce2$pct_counts_MT < 30
> table(filter_by_MT)
filter_by_MT
FALSE  TRUE 
   18   846
> sce2 <- filter(sce2,batch != "NA19098.r2")

下图中,x轴是total counts,y轴是仅有ERCC基因的total counts,每个点代表一个细胞,细胞根据批次上色。

可以看到**橙色点(r2)**的ERCC占比更高,这个批次在原文中也体现出来了,和其他batch非常不同

ERCC是已知序列和数量的合成mRNA,RNA的读数将提供样本间差异的信息。spike-in control 在确定测序过程中的empty Wells和的dead cells有重要作用,高的ERCC含量与低质量数据相关,并且通常是排除的标准。

Filter cells with Batch

细胞过滤后,然后进行基因过滤 (因为有的基因虽然表达,但是却是在被过滤掉的细胞中,因此也算作无效)

目的就是保留至少在1个细胞中表达量不为0

filter_genes <- apply(counts(sce2), 1, function(x){
    length(x[x >= 1]) >= 1
})
table(filter_genes)
# 虽然这里不存在低表达基因
sce2 <- sce2[filter_genes,]

下次开始标准化

Yunze Liu
Yunze Liu
Bioinformatics Sharer

Co-founder of Bioinfoplanet(生信星球)

Next
Previous

Related