OSCA单细胞数据分析笔记-14、Empty/Doublet droplet

对应原版教程第15、16章http://bioconductor.org/books/release/OSCA/overview.html
现行主流的Droplet-based单细胞测序技术主要思路是一个磁珠捕获一个细胞置于油包水的腔室里完成添加标签、建库操作。但在磁珠捕获的过程会出现未捕获到细胞或者两个细胞的异常情况。这就需要我们在分析单细胞数据中识别、过滤掉这些bad barcode(cell)。

(源网侵删~)

1、Empty droplet

1.1 关于Empty

  • 空液滴,即磁珠未捕获到细胞,是Droplet-based单细胞测序技术的常见现象。一般Cellranger 分析得到的单细胞表达矩阵往往是过滤了Empty droplet之后的,一般只有数千至上万个细胞。如果下载的表达矩阵里有几十万的细胞,那么很有可能是未过滤empty droplet的原始表达矩阵。例如GSE138665数据集。
  • 空液滴并非没有任何基因表达数据。由于溶液环境中的ambient RNA造成空液滴实际上会有少数基因的结果(lower library)。所以最简单的方式就是直接设定一个阈值过滤lower library的droplet/barcode/cell。但这就可能误删本身基因表达量就少的细胞类型。
  • 如下实例数据集是一个未过滤emoty droplet的原始测序数据
#--- loading ---#
library(DropletTestFiles)
raw.path <- getTestFile("tenx-2.1.0-pbmc4k/1.0.0/raw.tar.gz")
out.path <- file.path(tempdir(), "pbmc4k")
untar(raw.path, exdir=out.path)
library(DropletUtils)
fname <- file.path(out.path, "raw_gene_bc_matrices/GRCh38")
sce.pbmc <- read10xCounts(fname, col.names=TRUE)
sce.pbmc
#class: SingleCellExperiment 
#dim: 33694 737280

我们可以根据根据每个细胞的counts数大小从高到底排序,观察是否有明显的断崖趋势。

library(DropletUtils)
bcrank <- barcodeRanks(counts(sce.pbmc))
# Only showing unique points for plotting speed.
uniq <- !duplicated(bcrank$rank)
plot(bcrank$rank[uniq], bcrank$total[uniq], log="xy",
     xlab="Rank", ylab="Total UMI count", cex.lab=1.2)
abline(h=metadata(bcrank)$inflection, col="darkgreen", lty=2)
abline(h=metadata(bcrank)$knee, col="dodgerblue", lty=2)
legend("bottomleft", legend=c("Inflection", "Knee"), 
       col=c("darkgreen", "dodgerblue"), lty=2, cex=1.2)

如下图呈现结果,在count数1000左右,细胞数量突然减少可能就意味着empty droplet。


1.2 去除empty droplet

  • 如上所说,直接设定一个low count阈值去除empty droplet有些太过武断。针对上述例子:主要在于判断counts数处于500~1000的droplet是受ambient RNA影响的empty droplet还是自身表达量就很低的细胞类型 。
  • 这里我们使用DropletUtils包的emptyDrops()函数尽可能区分这两种混淆的droplet。
  • 其主要思路:首先设定counts量明显过低(<100)一定是empty droplet,然后(空假设)根据计算其余droplet的表达模式是否明显偏离empty droplet的表达模式。最后我们保留p值显著(<0.05)的结果,认为是捕获到cell的droplet,用于后续的数据分析。
set.seed(100)
e.out <- emptyDrops(counts(sce.pbmc))
summary(e.out$FDR <= 0.001)
##    Mode   FALSE    TRUE    NA's 
## logical     989    4300  731991
#最后保留得到4300个细胞
#NA值表示count < 100的empty droplet(default parameter,可自定义修改)
  • 观察去除empty droplet 之后的counts分布情况
retained <- sce.pbmc[,which(e.out$FDR <= 0.001)]
bcrank <- barcodeRanks(counts(retained))
uniq <- !duplicated(bcrank$rank)
plot(bcrank$rank[uniq], bcrank$total[uniq], log="xy",
     xlab="Rank", ylab="Total UMI count", cex.lab=1.2)
  • 可以看到依旧存在明显的拐点,但转点阈值已经提高到2000左右,而且只有不到500个细胞,可能是测序不合格的细胞,待下一步的QC解决。


  • 最后值得一提的是emptyDrops()进行假设检验的方法是蒙特卡洛抽样法(Monte Carlo simulations)。一般来说抽样次数约多,p值越低(前提是该droplet的确有可能不符合空假设)。所有对于emptyDrops()返回结果的limited为logical,表示是否可以随着增加抽样次数,进一步降低p值,从而挽救可能是cell captured droplet,但却被判定为empty droplet。但对于上述的分析结果,如下所示无该风险,可放心进行过滤。

table(Sig=e.out$FDR <= 0.001, Limited=e.out$Limited)
##        Limited
## Sig     FALSE TRUE
##   FALSE   989    0
##   TRUE   1728 2572

2、Doublet droplet

2.1 关于Doublet droplet

  • Doublet droplet,即一个磁珠同时捕获到了两个细胞,很有可能同时含有两种细胞类型的表达特征,会对后续的分析造成很大的混淆。
  • 目前cellranger软件尚不能识别、过滤Doublet droplet。下面介绍了基于表达矩阵的,两种判断是否为Doublet方法。
  • 如下为示例数据
sce.mam
#class: SingleCellExperiment 
#dim: 27998 2772

2.2 方法一:Detection with clusters

  • scDblFinder包的findDoubletClusters()函数。这种方法根据聚类结果,判断出是否由Doublet聚成的1个 cluster。
  • 该方法的空假设为:一个cluster(potential Doublet)的cell 是由两个 origin cluster里的cell混合而成的。所以该方法对于一个cluster考虑其余所有cluster的两两组合视为potential origin cluster。
  • 该结果为每个cluster找到了最有可能的2个 origin cluster,供分析者判断。相关指标包
library(scDblFinder)
dbl.out <- findDoubletClusters(sce.mam)
dbl.out

如下结果含义可通过?findDoubletClusters查看。关键的几个指标有 num.de: query cluster与 source cluster的差异基因数(越少越有可能为doublet),lib.size: source cluster的median cell library/query cluster的median cell library(越少越有可能为doublet,因为doublet cell的library size会大一些)。此外prop表示query cluster的细胞数占总细胞数的比例,这可为我们对预期的doublet 数量提供判断(一般小于5%)。

  • 根据上述结果,进一步判断最可能的doublet cluster
library(scater)
chosen.doublet <- rownames(dbl.out)[isOutlier(dbl.out$num.de, type="lower", log=TRUE)]
chosen.doublet
dbl.out[chosen.doublet,]
#DataFrame with 1 row and 9 columns
#      source1     source2    num.de median.de        best    p.value lib.size1 lib.size2      prop
#  <character> <character> <integer> <numeric> <character>  <numeric> <numeric> <numeric> <numeric>
#6           2           1        13     507.5       Pcbp2 0.00128336  0.811531  0.516399  0.030303
  • 根据来自两种细胞的marker gene进一步验证该cluster是否由doublet聚成的
#basal cells (Acta2) and alveolar cells (Csn2)
plotExpression(sce.mam, features=c("Acta2", "Csn2"), 
    x="label", colour_by="label")

如下图,的确cluster6高表达两种细胞类型的marker gene,很有可能就是doublet cluster。再后续分析种予以去除。


最后关于这种基于cluster的判断doublet方法比较依赖合适的聚类结果。如教程所说:Clusters that are too coarse will fail to separate doublets from other cells, while clusters that are too fine will complicate interpretation.

2.2 方法二:Detection by simulation

  • scran包的computeDoubletDensity()函数。这种方法会为每一个细胞计算doublet score,越高表明越可能是doublet。
  • 其主要思路是:首先从数据集中通过合并随机两个细胞的表达谱,模拟上千个doublet添加到表达矩阵中。然后根据PCA降维结果,分别以每一个细胞为中心,计算该细胞周围(高维距离)模拟doublet的密度与真实droplet的密度之间的比例。score比值越高表明该细胞越接近simulated doublet。
library(BiocSingular)
set.seed(100)
# Setting up the parameters for consistency with denoisePCA();
# this can be changed depending on your feature selection scheme.
dbl.dens <- computeDoubletDensity(sce.mam, subset.row=top.mam, 
    d=ncol(reducedDim(sce.mam)))
summary(dbl.dens)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   0.000   0.249   0.527   1.041   1.188  14.570
  • 可视化 DoubletScore
sce.mam$DoubletScore <- dbl.dens
plotTSNE(sce.mam, colour_by="DoubletScore")
plotColData(sce.mam, x="label", y="DoubletScore", colour_by="label")
  • 如上看到还是主要集中第六群最值得怀疑。直接删去high score的suspected doublet缺乏可解释性,因为很难定义一个合理的阈值(predicted doublet proportion?)。这里还是建议结合聚类结果进行判断出suspected cluster。

最后强调一点:无论是empty droplet还是doublet,都应当基于单批次的样本进行操作处理。因为每个批次的测序环境都是不一致的,不能够符合统一的假设。即对于多批次的样本,应该进行分别的鉴别、过滤,再进行后续的分析流程。

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

推荐阅读更多精彩内容