该推文首发于公众号:单细胞天地
我们在上一讲内容中得到处理好后的数据集之后,接着来学习“矫正”数据的三个工具~ 分别为细胞周期矫正,去除双胞体和RNA污染。
初级篇1的示例数据可以通过网盘下载:https://pan.baidu.com/s/1wHZBdYAr6f0H-yMxDZZBHw 提取码: t2vb --来自百度网盘超级会员v4的分享,该数据是延续了上一讲的内容~
此外,可以通过向公众号发送关键词‘单细胞’,直接获取Seurat V5版本的完整代码。
关于上一讲内容最后的boxplot代码,补充如下
load('../phe.Rdata')
phe$group = sce$group = ifelse(sce$orig.ident %in% c("GSM5688706_WGC","GSM5688707_JCA","GSM5688708_LS-CRC3"),"left","right")
head(phe)
gplots::balloonplot(
table(phe$celltype,phe$orig.ident)
)
x = phe$orig.ident
y = phe$celltype
tbl = table(x,y)
df = dcast(as.data.frame(tbl),x~y)
ptb = round(100*tbl/rowSums(df[,-1]),2)
df = as.data.frame(ptb)
colnames(df)=c('orig.ident','celltype','Percentage')
head(df)
gpinfo=unique(phe[,c('orig.ident','group')]);gpinfo
df=merge(df,gpinfo,by='orig.ident')
head(df)
p <- ggplot(df,aes(x=group,y=Percentage))+
geom_boxplot(outlier.alpha=0, aes(fill=celltype))+facet_grid(~celltype,scales = "free")+
geom_jitter(aes(x = group, y = Percentage,color=celltype))+
scale_color_npg() +
scale_fill_npg() +
theme_bw()+
theme(axis.text.x = element_text(face = "bold",angle = 45, hjust=1, vjust=0.5,size = 14),
legend.position= "none",
strip.background = element_rect(color="black",fill = "steelblue3", linetype = "solid"),
strip.text = element_text(face = "bold", color = "black",hjust = 0.5, size = 12,),
plot.title=element_text(face="bold.italic",size="20", color="brown",hjust = 0.5),
axis.text.y = element_text(face = "bold",size = 14) ,
axis.title = element_text(size = 14),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank())
p
pro='Percentage-of-each--celltype-in-orig.ident-by-group'
ggsave(paste0(pro,"_boxplot.pdf"),width = 12,height = 6)
单细胞细胞周期矫正的目的从宽泛的角度来说是因为它可以消除由于细胞周期阶段不同而导致的基因表达异质性。在单细胞数据中,不同细胞可能处于不同的细胞周期阶段(如G1、S、G2或M期),这些阶段会影响细胞的基因表达模式,尤其是那些与细胞周期密切相关的基因。如果不进行细胞周期矫正,这些周期性变化的基因可能会误导后续的数据分析,如聚类和差异表达分析,导致分析结果不能准确反映细胞的生物学状态和异质性。
但这个时候问题就来了,如果可以消除由于细胞周期阶段不同而导致的基因表达异质性,那么换句话来说是不是也就可能消除了本身的生物学差异,而这种差异也许是研究者正是所想要研究的呢?
笔者认为正确也不一定正确,这需要依情况而定。
我们在做单细胞分析的时候是需要根据样本的"特征"(这里可以泛指是各种重要的且表达有差异的基因)去对数据做分析。那么如何更好的去找出这些特征基因且必须把一些表达有差异但其实是没有意义的基因给筛去?在刚开始的质控环节就已经开始了,比如限制线粒体基因的百分比,nCount和nFeature的值等等。
在单细胞时代之下数据的分辨率已经达到了细胞级,不同细胞应看做不同的样本,那么它们之间是存在极大异质性的。这种异质性从表象上都可归类于生物学差异,但更深层次的,研究者真正想要知道是生物学差异背后的样本之间的功能差异!
而细胞周期既可以看做是生物学上的混杂因素(不同细胞的细胞周期情况在取样的那一瞬间就已经被定格了,而他们的周期情况绝对是不可能完全一样的),也可以在控制大部分混杂因素(控制变量这件事要完美是不可能的)后的后续分析中认为是功能上的差异(不同处理下,细胞周期通路在实验组和对照组之间存在的差异)。如果说是生物学的差异,我们就需要在正式分析之前就要进行矫正,尽量减少这种生物学差异导致的影响!
但这里还有一个问题,细胞周期的影响真的那么大吗?是的,在既往的文献中(见参考资料)已经明确提到了细胞周期这个生物学差异会导致很大的异质性,从笔者的理解来看,这种异质性可能会掩盖功能学上的差异!(这个掩盖可以解释为由于细胞周期导致的生物学误差过大之后,其他非细胞周期相关的功能学差异反而显得没那么有差异了..)
当然,我们还是要依情况而定,如果研究方向就是跟细胞周期有直接且密切关系的,那就不可以去矫正这个因素了,比如现在热门—衰老/干细胞(涉及静息态等情况?这块不太懂)等相关的研究。如果是非直接关系的,应当要去矫正,换个角度去想,如果一个矫正能把"所谓的差异"给矫正没了,那请问这个"差异"到底真有那么大的研究价值吗?
在单细胞RNA测序过程中,有时两个或多个细胞可能在制备过程中意外结合成一个单一的"假细胞",称为双峰细胞或双胞体。这些细胞可能会扭曲数据分析和解释,因此,需要使用一些方法对它们进行识别和剔除。
其中DoubletFinder是最常用的一个工具,其分析流程可以分为以下四个步骤:
官方对DoubletFinder输入的对象和参数介绍:
下面的表格是DoubletRate参数选择的参考文件(10X),在分析之前参照这个表格上边的细胞数选择DoubletRate值。
DecontX是一种用于单细胞 RNA 测序数据的去除环境污染物(decontamination)的算法,主要用于减少由细胞外RNA造成的污染效应。该算法旨在从测序数据中去除由环境或邻近细胞释放的外源 RNA,改善下游分析的准确性,如细胞类型鉴定和差异表达分析。
开发者在20年的文章中已经把这个工具适用的情况说的非常清楚了:简单来说就是尽管基于微流控的单细胞技术会导致环境中的RNA增多,这种环境中的RNA是来自于自受压或经历细胞凋亡的细胞。当环境RNA掺入液滴中并与细胞的天然mRNA一起被标记和扩增时,就会发生交叉污染。
其可以通过贝叶斯统计模型对每个细胞的RNA读数进行推断,将原始数据分解为细胞内真实表达的 RNA 和污染的 RNA。该模型假设污染 RNA 来自于细胞群体的背景分布。进而估计每个细胞中污染RNA的比例,并根据污染的概率对原始表达矩阵进行校正,以生成一个含有去污染的表达矩阵(简单来说是给每个细胞一个污染评分,而使用者可以根据自己的需求调整筛选分值,分值是选择是在0-1之间)。
rm(list=ls())
source('scRNA_scripts/lib.R')
library(DoubletFinder)
library(BiocParallel)
getwd()
dir.create('4-Corrective-data/')
setwd('4-Corrective-data/')
register(MulticoreParam(workers = 4, progressbar = TRUE))
sce.all <- readRDS("../2-harmony/sce.all_int.rds")
sp='human'
load('../phe.Rdata')
identical(rownames(phe) , colnames(sce.all))
sce.all=sce.all[,colnames(sce.all) %in% rownames(phe) ]
sce.all@meta.data = phe[match(colnames(sce.all),rownames(phe) ),]
sel.clust = "celltype"
sce.all <- SetIdent(sce.all, value = sel.clust)
table(sce.all@active.ident)
DimPlot(sce.all)
colnames(sce.all@meta.data)
scRNA <- sce.all
check一下
需要提醒的是,在自行分析的时候请把细胞周期矫正代码嵌入到标准流程(PCA前面)中去。
# Seurat包自带了一组细胞周期标记基因,这些基因来自 Tirosh 等人在 2015 年发表的研究(通常是指用于单细胞 RNA 测序数据中的细胞周期分析)。
# 这些基因被用来识别细胞所处的细胞周期阶段,如G1、S、G2/M 期。
s.genes <- intersect(cc.genes$s.genes,rownames(scRNA))
g2m.genes <- intersect(cc.genes$g2m.genes,rownames(scRNA))
scRNA <- CellCycleScoring(scRNA,
s.features = s.genes,
g2m.features = g2m.genes,
set.ident = T)
colnames(scRNA@meta.data)
p1=VlnPlot(scRNA,features = "S.Score" ,pt.size = 0,group.by = 'celltype') +NoLegend()
p2=VlnPlot(scRNA,features = "G2M.Score" ,pt.size = 0,group.by = 'celltype')+NoLegend()
p1+p2
table(scRNA$Phase,scRNA$celltype)
# B cells endothelial cells epithelial/cancer cells fibroblasts mast cells myeloid cells plasma proliferative cells T/NK cells VSMCs
# G1 2027 437 1478 955 468 2763 1832 68 5855 365
# G2M 857 56 637 35 78 336 164 396 2557 14
# S 920 56 306 50 50 357 100 339 2033 32
在增殖性细胞群中的分值是最高的。
官方教程认为需要去除细胞周期相关基因以及TOP2A,MKI67的高表达的影响(我们这里的proliferative cells其实是高表达了TOP2A和MKI67)。
# features = rownames(scRNA)的情况需要前后一致
scRNA_new <- ScaleData(scRNA,
vars.to.regress = c("S.Score", "G2M.Score"),
features = rownames(scRNA))
# 查看不进行细胞周期和细胞之后处理之后的数据情况
# 分别查看celltype和细胞周期基因标记情况
p1=DimPlot(scRNA_new,reduction="pca",group.by='celltype') + DimPlot(scRNA,reduction="pca",group.by='celltype');p1
p2=DimPlot(scRNA_new,reduction="pca") + DimPlot(scRNA,reduction="pca") +NoLegend();p2
p1/p2
PCA的结果是有差别的
# 在这里我们重新对scRNA和scRNA_new数据进行PCA以及后续流程并进行结果对比
seurat <- RunPCA(scRNA,
features = VariableFeatures(object = scRNA),
verbose = FALSE)
seurat <- RunHarmony(seurat,"orig.ident")
seurat <- FindNeighbors(seurat, dims = 1:15,
reduction = "harmony",
verbose = FALSE)
seurat <- FindClusters(seurat,resolution = 0.8,
verbose = FALSE)
seurat <- RunUMAP(seurat,dims=1:15,reduction = "harmony",
umap.method = "uwot", metric = "cosine")
seurat <- DimPlot(seurat,reduction = 'umap',label = T);seurat
# scRNA_new
seuratObj <- RunPCA(scRNA_new,
features = VariableFeatures(object = scRNA_new),
verbose = FALSE)
seuratObj <- RunHarmony(seuratObj,"orig.ident")
seuratObj <- FindNeighbors(seuratObj, dims = 1:15,
reduction = "harmony",
verbose = FALSE)
seuratObj <- FindClusters(seuratObj,resolution = 0.8,
verbose = FALSE)
seuratObj <- RunUMAP(seuratObj,dims=1:15,reduction = "harmony",
umap.method = "uwot", metric = "cosine")
seuratObj <- DimPlot(seuratObj,reduction = 'umap',label = T);seuratObj
从图形就可以看出其实还是存在一定差别的
需要再次提醒的是,是否要用细胞周期,各位还是要根据自己数据结合生物学背景去决定。
这里的数据没有进行细胞周期矫正~
rm(list=ls())
source('scRNA_scripts/lib.R')
library(DoubletFinder)
library(BiocParallel)
getwd()
dir.create('4-Corrective-data/')
setwd('4-Corrective-data/')
register(MulticoreParam(workers = 4, progressbar = TRUE))
sce.all <- readRDS("../2-harmony/sce.all_int.rds")
sp='human'
load('../phe.Rdata')
identical(rownames(phe) , colnames(sce.all))
sce.all=sce.all[,colnames(sce.all) %in% rownames(phe) ]
sce.all@meta.data = phe[match(colnames(sce.all),rownames(phe) ),]
sel.clust = "celltype"
sce.all <- SetIdent(sce.all, value = sel.clust)
table(sce.all@active.ident)
DimPlot(sce.all)
colnames(sce.all@meta.data)
# 定义主要参数和函数
run_doublet_finder <- function(sce_object, doublet_rate, pcs = 1:15, sct = FALSE) {
# 参数扫描,选择最优 pK 值
sweep.res <- paramSweep(sce_object, PCs = pcs, sct = sct)
sweep.stats <- summarizeSweep(sweep.res, GT = FALSE)
bcmvn <- find.pK(sweep.stats)
pK_bcmvn <- bcmvn$pK[which.max(bcmvn$BCmetric)] %>% as.numeric()
# 计算 homotypic doublets 和调整的 doublet 数目
homotypic.prop <- modelHomotypic(sce_object$seurat_clusters)
nExp_poi <- round(doublet_rate * ncol(sce_object))
nExp_poi.adj <- round(nExp_poi * (1 - homotypic.prop))
# 鉴定 doublets
sce_object <- doubletFinder(sce_object,
PCs = pcs,
pN = 0.25,
pK = pK_bcmvn,
nExp = nExp_poi.adj,
reuse.pANN = FALSE,
sct = sct)
return(sce_object)
}
# 运行 DoubletFinder 分析
sce_list <- SplitObject(scRNA, split.by = "orig.ident")
# check每个样本的细胞数
sce_list
# doublet_rate中的值需要根据
sce_list[["GSM5688706_WGC"]] <- run_doublet_finder(sce_list[["GSM5688706_WGC"]],
doublet_rate = 0.018)
由于每个样本的细胞数都不一样,所以每次都需要重新选择样本并且设定doublet比例。
# 选择一个可视化一下
DimPlot(sce_list[["GSM5688706_WGC"]],
reduction = "umap",
group.by = "DF.classifications_0.25_24_59")
# 遍历每个子对象,直接修改列名
sce_list <- lapply(sce_list, function(sce) {
# 修改 pANN 列名(匹配以 "pANN_0.25" 开头的列)
pANN_col <- grep("^pANN_0.25", colnames(sce@meta.data), value = TRUE)
if (length(pANN_col) > 0) {
colnames(sce@meta.data)[colnames(sce@meta.data) %in% pANN_col] <- "pANN_0.25"
}
# 修改 DF.classifications 列名(匹配以 "DF.classifications_0.25" 开头的列)
DF_col <- grep("^DF\\.classifications_0.25", colnames(sce@meta.data), value = TRUE)
if (length(DF_col) > 0) {
colnames(sce@meta.data)[colnames(sce@meta.data) %in% DF_col] <- "DF.classifications"
}
return(sce)
})
sce.all <- merge(sce_list[[1]], y= sce_list[ -1 ]) #add.cell.ids = samples)
LayerData(sce.all, assay = "RNA", layer = "counts")
sce.all <- JoinLayers(sce.all)
dim(sce.all[["RNA"]]$counts )
colnames(sce.all@meta.data)
sce.all <- NormalizeData(sce.all,normalization.method = "LogNormalize",
scale.factor = 1e4)
sce.all <- FindVariableFeatures(sce.all)
sce.all <- ScaleData(sce.all)
sce.all <- RunPCA(sce.all, features = VariableFeatures(object = sce.all))
sce.all <- RunHarmony(sce.all, "orig.ident")
sce.all <- RunUMAP(sce.all, dims = 1:15,reduction = "harmony")
DimPlot(sce.all,group.by = "DF.classifications")
# 如果需要过滤双细胞的话
sce.all.filtered <- subset(sce.all, subset = DF.classifications == "Singlet")
DimPlot(sce.all.filtered,group.by = "DF.classifications")
上一步过滤完双细胞之后,我们接下来在去除环境游离RNA污染
set.seed(123)
# 得到表达矩阵
counts <- GetAssayData(object = sce.all.filtered, slot = "counts")
decontX_res <- decontX(counts)
sce.all.filtered$contamination <- decontX_res$contamination
sce.all.filtered$contamination
# 可视化
library(ggplot2)
FeaturePlot(sce.all.filtered,
features = 'contamination',
raster=FALSE # 细胞过多时候需要加这个参数
) +
scale_color_viridis_c()+
theme_bw()+
theme(panel.grid = element_blank(),
axis.text = element_blank(),
axis.ticks = element_blank())+
xlab('UMAP_1')+
ylab('UMAP_2')
# contamination值在0-1之间,官方没有给出具体的参考值
# 有老师认为0.2可能是一个比较合适的界值
sce_filt <- sce.all.filtered[,sce.all.filtered$contamination < 0.2]
DimPlot(sce_filt,label = T)+NoLegend()
sce_filt
# An object of class Seurat
# 21308 features across 24080 samples within 1 assay
# Active assay: RNA (21308 features, 2000 variable features)
# 3 layers present: scale.data, data, counts
# 3 dimensional reductions calculated: pca, harmony, umap
contamination值在0-1之间,官方没有给出具体的参考值,有老师认为0.2可能是一个比较合适的界值。最终我们的数据从25621个细胞变成了24080个细胞~
qsave(sce_filt,"sce.all.qs")
setwd("..")
此时我们所保存的数据是一个去除了双细胞和RNA污染的数据(这部分数据里面没有矫正细胞周期), 后续我们将围绕这个数据再进行分析~
致谢:感谢曾老师以及生信技能树团队全体成员。更多精彩内容可关注公众号:生信技能树,单细胞天地,生信菜鸟团等公众号。
注:若对内容有疑惑或者有发现明确错误的朋友,请联系后台(欢迎交流)。更多相关内容可关注公众号:生信方舟
- END -
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。