OSCA单细胞数据分析笔记-5、Quality control

对应原版教程第6章http://bioconductor.org/books/release/OSCA/overview.html
在单细胞数据分析中的第一步质控往往是剔除不合格的细胞。本小节主要介绍了如何鉴定出低质量细胞的相关知识点。

笔记要点

1、为什么要质控
1.1 低质量细胞文库的特点
1.2 低质量细胞文库的影响
2、质控指标的选择与计算
2.1 质控指标选择的假设
2.2 常见的指标
2.3 计算方法
3 确定低质量细胞的指标阈值
3.1 固定阈值
3.2 outliner确定异常细胞
3.3 遇到batch批次效应的解决方法
4 根据阈值筛选细胞
4.1 可视化阈值指标
4.2 核实剔除细胞是否包含一种特定的细胞类型
4.3 剔除低质量细胞

1、为什么要质控

1.1 低质量细胞文库的特点

  • scRNA-seq的结果是col为细胞标签,row为基因名的表达矩阵。一列即代表一个细胞的文库测序情况。
  • 低质量的细胞文库一般有3个特征:(1)总counts数很少,即列总和较低;(2)非零基因很少,即相当部分基因的count为0;(3)spike-in或者线粒体基因表达相对较高。
  • 低质量细胞的测序结果主要是实验过程中细胞发生裂解,低效的逆转录、PCR扩增等因素造成的。

1.2 低质量细胞文库的影响

(1)无意义的cluster

这些低质量细胞因为“低质量的相似性”在聚类时聚成一个单独的cluster,但这个cluster本身是没有任何生物意义的;反而会干扰后续的差异分析、细胞注释、拟时序分析等步骤

(2)异常的异质性

在后续的挑选高变基因、PCA主成分分析等。这些细胞的基因表达会低于其它细胞的平均水平,“贡献”出非生物学意义的异质性(高质量细胞与低质量细胞间的异质性),从而挑出来的高变基因、计算的Top前几的主成分捕捉的生物学水平的异质性占比下降。

(3)冒牌的高表达基因

细胞或者说一群细胞的高表达基因是鉴定细胞类型的marker gene。但如果存在一些基因自身表达在所有细胞中均维持在较低水平,但受文库大小的标准化处理后,处于低质量细胞的这些基因表达相对其它正常细胞就会被无端放大。

2、质控指标的选择与计算

2.1 质控指标选择的假设

  • 对于所选择用于质控的若干指标,我们其实就假设了细胞的这些指标的波动variance主要由于实验过程造成的,而非生物学水平的因素造成的。
  • 基于这个假设,我们可以认为根据这些指标删除的是低质量细胞。如果违反了这一假设(例如某一类细胞的线粒体表达就是相对较高),就可能误删了部分合格的细胞。

2.2 常见的指标

即根据上面1.1小点低质量细胞文库的三个特点对应的3/4个指标

  • (1)library size
    细胞文库大小,即对于表达矩阵的每一列的求和。该值越小,越有可能是低质量细胞。原因如1.1点的第三小点
  • (2)the number of expressed gene
    细胞表达基因数目,即对于表达矩阵的每一列的非0值的基因数目。该值越小,越有可能是低质量细胞。因为未能捕获到细胞的转录水平的多样性。
  • (3)spike-in/线粒体表达相对比例:比例越高,越有可能是低质量细胞。
    对于spike-in外参转录本,由于初始每个细胞加的量都是一样的。如果某些细胞的spike-in表达相比其它细胞异常增大,可能就意味着细胞的内源基因表达相对偏低。
    对于线粒体基因,在穿孔裂解的细胞里,细胞质RNA较容易游离到胞外,而由于线粒体体积较大,不易“逃出”胞体,从而线粒体基因含量相对稳定。

2.3 计算方法

  • 使用scater包的addPerCellQC函数,可自动计算上述所有指标
library(scRNAseq)
sce.416b <- LunSpikeInData(which="416b") 
sce.416b$block <- factor(sce.416b$block)
sce.416b
  • 由于需要计算线粒体基因表达比例,并且基因ID为ensembl格式(小鼠),所以需要注释下基因所处的染色体序列信息。
# ALTERNATIVELY: using resources in AnnotationHub to retrieve chromosomal
# locations given the Ensembl IDs; this should yield the same result.
library(AnnotationHub)
ens.mm.v97 <- AnnotationHub()[["AH73905"]]
chr.loc <- mapIds(ens.mm.v97, keys=rownames(sce.416b),
                  keytype="GENEID", column="SEQNAME")
is.mito.alt <- which(chr.loc=="MT")
library(scater)
sce.416b <- addPerCellQC(sce.416b, subsets=list(Mito=is.mito))
colData(sce.416b)
colnames(colData(sce.416b))
# [1] "Source Name"              "cell line"                "cell type"               
# [4] "single cell well quality" "genotype"                 "phenotype"               
# [7] "strain"                   "spike-in addition"        "block"                   
# [10] "sum"                      "detected"                 "percent_top_50"          
# [13] "percent_top_100"          "percent_top_200"          "percent_top_500"         
# [16] "subsets_Mito_sum"         "subsets_Mito_detected"    "subsets_Mito_percent"    
# [19] "altexps_ERCC_sum"         "altexps_ERCC_detected"    "altexps_ERCC_percent"    
# [22] "altexps_SIRV_sum"         "altexps_SIRV_detected"    "altexps_SIRV_percent"    
# [25] "total"  

如上,最重要的4列分别是sum列即为library size;detected即为表达基因数;subsets_Mito_percent即为线粒体基因表达比例;altexps_ERCC_percent即为spike-in转录本表达比例。
percent_top_x的若干列表示按该细胞的基因表达从高到低,前top x的基因表达总counts占细胞文库的比例大小。

3 确定低质量细胞的指标阈值

3.1 固定阈值

  • 手动设定低质量细胞的指标阈值虽然是最简单、最直接的方法,但需要对所做的细胞类型、状态、实验参数等都需要有准确的把控。
  • 例如针对上述数据,把文库大小阈值设为100000(这是smart-seq,与10X不可比);表达基因数设为5000,;spike-in以及线粒体基因比例均设为10%(仅作为举例,不可作为标准参考~)。如下结果,会剔除33个cell
qc.lib <- df$sum < 1e5
qc.nexprs <- df$detected < 5e3
qc.spike <- df$altexps_ERCC_percent > 10
qc.mito <- df$subsets_Mito_percent > 10
discard <- qc.lib | qc.nexprs | qc.spike | qc.mito

# Summarize the number of cells removed for each reason.
DataFrame(LibSize=sum(qc.lib), NExprs=sum(qc.nexprs),
          SpikeProp=sum(qc.spike), MitoProp=sum(qc.mito), Total=sum(discard))
# DataFrame with 1 row and 5 columns
# LibSize    NExprs SpikeProp  MitoProp     Total
# <integer> <integer> <integer> <integer> <integer>
#   1         3         0        19        14        33

3.2 outliner确定异常细胞

(1) outliner的假设
  • 首先还是这些使用的QC指标仅表示这些细胞的实验测序水平的误差。
  • 其次采用outliner方法,默认大部分细胞是合格的,只有少部分是低质量细胞,即寻找这些少数的离群点。
(2) 计算方法
  • 单独计算
qc.lib2 <- isOutlier(df$sum, log=TRUE, type="lower")
attr(qc.lib2, "thresholds")
qc.nexprs2 <- isOutlier(df$detected, log=TRUE, type="lower")
attr(qc.nexprs2, "thresholds")
qc.spike2 <- isOutlier(df$altexps_ERCC_percent, type="higher")
attr(qc.spike2, "thresholds")
qc.mito2 <- isOutlier(df$subsets_Mito_percent, type="higher")
attr(qc.mito2, "thresholds")

discard2 <- qc.lib2 | qc.nexprs2 | qc.spike2 | qc.mito2
# Summarize the number of cells removed for each reason.
DataFrame(LibSize=sum(qc.lib2), NExprs=sum(qc.nexprs2),
    SpikeProp=sum(qc.spike2), MitoProp=sum(qc.mito2), Total=sum(discard2))
# DataFrame with 1 row and 5 columns
# LibSize    NExprs SpikeProp  MitoProp     Total
# <integer> <integer> <integer> <integer> <integer>
#   1         4         0         1         2         6

如上代码分别计算了每个指标里的离群点(低质量细胞)。其中的type参数表示离群点方向,对于前2者,取低;对于后两者取高。至于前两个指标为何取log,如下图所示数据点log前后的变化,在log变换后可发现左边出现较明显的尾巴,从而符合之前大部分细胞正常(同一分布趋势),小部分细胞异常的假设。对于后两者,因为作图发现右边存在尾巴,符合筛选预期,故不做变动


  • scater包的quickPerCellQC函数可自动完成上述的计算
reasons <- quickPerCellQC(colData(sce.416b), 
                          percent_subsets=c("subsets_Mito_percent", "altexps_ERCC_percent"))
colSums(as.matrix(reasons))

3.3 遇到batch批次效应的解决方法

  • 如果每个batch分别在是一个sce对象,那就分别计算离群点即可;
  • 如果一个sce对象里包含多个批次,那么可以拆成多个同批次的sce,也可以使用scater包的quickPerCellQC函数时设置batch参数
  • 例如sce.416b的数据集,有两个批次的细胞,可如下处理
table(sce.416b$block)
batch <- paste0(sce.416b$phenotype, "-", sce.416b$block)
batch.reasons <- quickPerCellQC(df, batch=batch,
                                percent_subsets=c("subsets_Mito_percent", "altexps_ERCC_percent"))
colSums(as.matrix(batch.reasons))
# low_lib_size            low_n_features high_subsets_Mito_percent 
# 5                         4                         2 
# high_altexps_ERCC_percent                   discard 
# 6                         9 

如上方法与结果有两个注意点
(1)首先从筛选结果来看,共剔除9个细胞,高于上面不设batch的6个细胞的结果。原因是考虑了batch因素的影响,降低了相对于原来MAD的各个batch的MAD值,从而在每个batch中会出现更多偏离3倍MAD的细胞指标,即剔除更多细胞。
(2)使用batch参数的前提假设是每个batch的大部分细胞都是高质量细胞。如果某一个batch出现问题,按照此假设也只能检测出少量的outliner。如何确定存在有问题的批次,作者建议,在存在多个批次的情况下,并且假设大部分batch都是合格的,那么如果少数几个batch的阈值明显偏离其它大部分batch的阈值,那么很有可能就是bad batch。对于存在问题的batch的解决方法,可以通过参考其它合格batch的阈值进行鉴别outliner。相关代码如下--

library(scRNAseq)
sce.grun <- GrunPancreasData()
#这个sce里有5个batch,其中有两个是有问题的(ERCC占比过高)
sce.grun <- addPerCellQC(sce.grun)

discard.ercc <- isOutlier(sce.grun$altexps_ERCC_percent,
                          type="higher", batch=sce.grun$donor)
ercc.thresholds <- attr(discard.ercc, "thresholds")["higher",]
ercc.thresholds #如下结果 D10、D3的阈值存在明显异常
# D10        D17         D2         D3         D7 
# 73.610696   7.599947   6.010975 113.105828  15.216956

# 解决办法:综合参考其它正常batch的阈值
discard.ercc2 <- isOutlier(sce.grun$altexps_ERCC_percent,
    type="higher", batch=sce.grun$donor,
    subset=sce.grun$donor %in% c("D17", "D2", "D7"))
attr(discard.ercc2, "thresholds")["higher",]
# D10       D17        D2        D3        D7 
# 9.475052  7.599947  6.010975  9.475052 15.216956

4 根据阈值筛选细胞

4.1 可视化阈值指标

  • 主要用来进一步核实确定我们的指标是否合理,然后就可以剔除细胞了。
  • 还是以上面的sce.416b数据集为例
sce.416b
sce.416b$block <- factor(sce.416b$block)
#注意下这个数据集只有block(20160113 20160325)里注释的两个批次,如下是实验分组信息。
sce.416b$phenotype <- ifelse(grepl("induced", sce.416b$phenotype),
                             "induced", "wild type")
sce.416b$discard <- reasons$discard
(1)library size等单独指标
plotColData(sce.416b, x="block", y="sum", colour_by="discard",
            other_fields="phenotype") + facet_wrap(~phenotype) + 
  scale_y_log10() + ggtitle("Total count")
  • 如下图,橙色的点即为鉴定为不合格的细胞。把上述绘图代码里的sum换为detectedsubsets_Mito_percentaltexps_ERCC_percent即可绘制对应的图。
(2)线粒体基因比例和library size的点图(剔除细胞是否聚集在左上角)
plotColData(sce.416b, x="sum", y="subsets_Mito_percent", colour_by="discard")

如下图因为细胞数少,所以聚集不太明显


(3)线粒体基因比例和spike-in 比例的点图(保留细胞是否聚集在左下角)
plotColData(sce.416b, x="altexps_ERCC_percent", y="subsets_Mito_percent", colour_by="discard")

4.2 核实剔除细胞是否包含一种特定的细胞类型

  • 绘制剔除细胞与保留细胞的average count--logFoldchange的基因点图。
  • 标注出我们感兴趣的细胞类型的marker gene是否显著的分布在上方(高表达),如果有那么就有可能不小心误删某种细胞;如果没有那么就符合我们的预期。
# Using the 'discard' vector for demonstration purposes, 
# as it has more cells for stable calculation of 'lost'.
lost <- calculateAverage(counts(sce.416b)[,!discard])
kept <- calculateAverage(counts(sce.416b)[,discard])

library(edgeR)
logged <- cpm(cbind(lost, kept), log=TRUE, prior.count=2)
logFC <- logged[,1] - logged[,2]
abundance <- rowMeans(logged)

plot(abundance, logFC, xlab="Average count", ylab="Log-FC (lost/kept)", pch=16)
cell_type_marker_gene <- c("gene1", "gene2", "gene3",.....)
points(abundance[cell_type_marker_gene], logFC[cell_type_marker_gene], col="orange", pch=16)
  • 举一个其它数据集的例子。该数据集,确定阈值指标后,发现血小板的3个marker gene在剔除细胞中高表达,表明可能剔除了血小板细胞。


4.3 剔除低质量细胞

  • 在做完上述的步骤之后,就可以真正剔除细胞了。这一步还是非常简单的。一步到位。
# Keeping the columns we DON'T want to discard.
filtered <- sce.416b[,!reasons$discard]

在质控完成后,就是对原始count数据的标准化,会在下一节进行学习。如有问题,欢迎留言指出~~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,265评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,078评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,852评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,408评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,445评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,772评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,921评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,688评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,130评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,467评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,617评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,276评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,882评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,740评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,967评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,315评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,486评论 2 348

推荐阅读更多精彩内容