前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >单细胞实战之细胞周期矫正,双胞体和RNA污染去除——入门到进阶(初级篇2)

单细胞实战之细胞周期矫正,双胞体和RNA污染去除——入门到进阶(初级篇2)

原创
作者头像
凑齐六个字吧
发布2025-02-09 23:54:38
发布2025-02-09 23:54:38
53800
代码可运行
举报
文章被收录于专栏:单细胞单细胞
运行总次数:0
代码可运行

该推文首发于公众号:单细胞天地

我们在上一讲内容中得到处理好后的数据集之后,接着来学习“矫正”数据的三个工具~ 分别为细胞周期矫正,去除双胞体和RNA污染

初级篇1的示例数据可以通过网盘下载:https://pan.baidu.com/s/1wHZBdYAr6f0H-yMxDZZBHw 提取码: t2vb --来自百度网盘超级会员v4的分享,该数据是延续了上一讲的内容~

此外,可以通过向公众号发送关键词‘单细胞’,直接获取Seurat V5版本的完整代码。

关于上一讲内容最后的boxplot代码,补充如下

代码语言:javascript
代码运行次数:0
运行
复制
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)
三种工具的简单介绍和思考
1.细胞周期矫正

单细胞细胞周期矫正的目的从宽泛的角度来说是因为它可以消除由于细胞周期阶段不同而导致的基因表达异质性。在单细胞数据中,不同细胞可能处于不同的细胞周期阶段(如G1、S、G2或M期),这些阶段会影响细胞的基因表达模式,尤其是那些与细胞周期密切相关的基因。如果不进行细胞周期矫正,这些周期性变化的基因可能会误导后续的数据分析,如聚类和差异表达分析,导致分析结果不能准确反映细胞的生物学状态和异质性。

但这个时候问题就来了,如果可以消除由于细胞周期阶段不同而导致的基因表达异质性,那么换句话来说是不是也就可能消除了本身的生物学差异,而这种差异也许是研究者正是所想要研究的呢?

笔者认为正确也不一定正确,这需要依情况而定。

我们在做单细胞分析的时候是需要根据样本的"特征"(这里可以泛指是各种重要的且表达有差异的基因)去对数据做分析。那么如何更好的去找出这些特征基因且必须把一些表达有差异但其实是没有意义的基因给筛去?在刚开始的质控环节就已经开始了,比如限制线粒体基因的百分比,nCount和nFeature的值等等。

在单细胞时代之下数据的分辨率已经达到了细胞级,不同细胞应看做不同的样本,那么它们之间是存在极大异质性的。这种异质性从表象上都可归类于生物学差异,但更深层次的,研究者真正想要知道是生物学差异背后的样本之间的功能差异!

而细胞周期既可以看做是生物学上的混杂因素(不同细胞的细胞周期情况在取样的那一瞬间就已经被定格了,而他们的周期情况绝对是不可能完全一样的),也可以在控制大部分混杂因素(控制变量这件事要完美是不可能的)后的后续分析中认为是功能上的差异(不同处理下,细胞周期通路在实验组和对照组之间存在的差异)。如果说是生物学的差异,我们就需要在正式分析之前就要进行矫正,尽量减少这种生物学差异导致的影响!

但这里还有一个问题,细胞周期的影响真的那么大吗?是的,在既往的文献中(见参考资料)已经明确提到了细胞周期这个生物学差异会导致很大的异质性,从笔者的理解来看,这种异质性可能会掩盖功能学上的差异!(这个掩盖可以解释为由于细胞周期导致的生物学误差过大之后,其他非细胞周期相关的功能学差异反而显得没那么有差异了..)

当然,我们还是要依情况而定,如果研究方向就是跟细胞周期有直接且密切关系的,那就不可以去矫正这个因素了,比如现在热门—衰老/干细胞(涉及静息态等情况?这块不太懂)等相关的研究。如果是非直接关系的,应当要去矫正,换个角度去想,如果一个矫正能把"所谓的差异"给矫正没了,那请问这个"差异"到底真有那么大的研究价值吗?

2.去除双胞体

在单细胞RNA测序过程中,有时两个或多个细胞可能在制备过程中意外结合成一个单一的"假细胞",称为双峰细胞或双胞体。这些细胞可能会扭曲数据分析和解释,因此,需要使用一些方法对它们进行识别和剔除。

其中DoubletFinder是最常用的一个工具,其分析流程可以分为以下四个步骤:

  1. 从现有的 scRNA-seq 数据中生成人工双胞体:通过对现有的单细胞 RNA 测序数据进行合成操作,生成具有双胞体特征的人工数据。
  2. 对合并后的真实数据和人工数据进行预处理:将真实单细胞数据与生成的人工双胞体数据进行合并,并对合并数据进行标准化和其他必要的预处理步骤。
  3. 执行主成分分析(PCA),并利用主成分距离矩阵计算每个细胞的人工双胞体最近邻(k nearest neighbors, kNN)比例(pANN):基于 PCA 降维后的数据,计算每个细胞在其最近邻细胞中属于人工双胞体的比例。
  4. 据预期双胞体数量对 pANN 值进行排序和阈值筛选:将计算得到的 pANN 值按照降序排列,并结合预期的双胞体数量确定适当的阈值,以筛选出可能的双胞体。

官方对DoubletFinder输入的对象和参数介绍:

  1. seu:这是一个完全处理过的 Seurat 对象,即已经完成了数据规范化(NormalizeData)、寻找变异基因(FindVariableGenes)、数据标准化(ScaleData)、主成分分析(RunPCA)和 t-SNE 分析(RunTSNE)。
  2. PCs:指定用于分析的统计显著的主成分数量,例如 PCs = 1:10。
  3. pN:定义生成的人工双胞体数量,以合并的真实-人工数据比例表示。默认设置为 25%,根据 McGinnis, Murrow 和 Gartner 在 2019 年的 Cell Systems 文章,DoubletFinder 的表现在很大程度上与 pN 参数无关。
  4. pK:定义用于计算 pANN 的 PC 邻域大小,同样以合并的真实-人工数据比例表示。没有默认值,因为每个单细胞 RNA 测序数据集都应该调整 pK 值。最优的 pK 值应该使用下面描述的策略来估计。
  5. nExp:定义用于做出最终双倍体/单倍体预测的 pANN 阈值。这个值最好从 10X 或 Drop-Seq 设备的细胞加载密度中估计,并根据同源双倍体的预估比例进行调整。

下面的表格是DoubletRate参数选择的参考文件(10X),在分析之前参照这个表格上边的细胞数选择DoubletRate值。

3.去除RNA污染(DecontX)

DecontX是一种用于单细胞 RNA 测序数据的去除环境污染物(decontamination)的算法,主要用于减少由细胞外RNA造成的污染效应。该算法旨在从测序数据中去除由环境或邻近细胞释放的外源 RNA,改善下游分析的准确性,如细胞类型鉴定和差异表达分析。

开发者在20年的文章中已经把这个工具适用的情况说的非常清楚了:简单来说就是尽管基于微流控的单细胞技术会导致环境中的RNA增多,这种环境中的RNA是来自于自受压或经历细胞凋亡的细胞。当环境RNA掺入液滴中并与细胞的天然mRNA一起被标记和扩增时,就会发生交叉污染。

其可以通过贝叶斯统计模型对每个细胞的RNA读数进行推断,将原始数据分解为细胞内真实表达的 RNA 和污染的 RNA。该模型假设污染 RNA 来自于细胞群体的背景分布。进而估计每个细胞中污染RNA的比例,并根据污染的概率对原始表达矩阵进行校正,以生成一个含有去污染的表达矩阵(简单来说是给每个细胞一个污染评分,而使用者可以根据自己的需求调整筛选分值,分值是选择是在0-1之间)。

分析步骤—矫正细胞周期
1.导入
代码语言:javascript
代码运行次数:0
运行
复制
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前面)中去。

2.数据预处理
代码语言:javascript
代码运行次数:0
运行
复制
# 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)。

3.ScaleData矫正
代码语言:javascript
代码运行次数:0
运行
复制
# features = rownames(scRNA)的情况需要前后一致
scRNA_new <- ScaleData(scRNA,
                       vars.to.regress = c("S.Score", "G2M.Score"),
                       features = rownames(scRNA))
3.1 处理和不处理结果对比
代码语言:javascript
代码运行次数:0
运行
复制
# 查看不进行细胞周期和细胞之后处理之后的数据情况
# 分别查看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的结果是有差别的

4.重新对scRNA和scRNA_new数据进行PCA以及后续流程并进行结果对比
代码语言:javascript
代码运行次数:0
运行
复制
# 在这里我们重新对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

从图形就可以看出其实还是存在一定差别的

需要再次提醒的是,是否要用细胞周期,各位还是要根据自己数据结合生物学背景去决定。

分析步骤—去除双胞体+RNA污染

这里的数据没有进行细胞周期矫正~

1.导入
代码语言:javascript
代码运行次数:0
运行
复制
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)
2.DoubletFinder分析
代码语言:javascript
代码运行次数:0
运行
复制
# 定义主要参数和函数
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比例。

代码语言:javascript
代码运行次数:0
运行
复制
# 选择一个可视化一下
DimPlot(sce_list[["GSM5688706_WGC"]], 
        reduction = "umap", 
        group.by = "DF.classifications_0.25_24_59")
代码语言:javascript
代码运行次数:0
运行
复制
# 遍历每个子对象,直接修改列名
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")
3.decontX去除单细胞数据的环境游离RNA污染

上一步过滤完双细胞之后,我们接下来在去除环境游离RNA污染

代码语言:javascript
代码运行次数:0
运行
复制
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个细胞~

4.记得保存数据
代码语言:javascript
代码运行次数:0
运行
复制
qsave(sce_filt,"sce.all.qs")
setwd("..")

此时我们所保存的数据是一个去除了双细胞和RNA污染的数据(这部分数据里面没有矫正细胞周期), 后续我们将围绕这个数据再进行分析~

参考资料:
  1. Seurat :https://satijalab.org/seurat/articles/cell_cycle_vignette
  2. DoubletFinder: https://github.com/chris-mcginnis-ucsf/DoubletFinder
  3. decontX :https://bioconductor.org/packages/release//bioc/manuals/decontX/man/decontX.pdf https://github.com/campbio/decontX
  4. A single-cell resolution map of mouse hematopoietic stem and progenitor cell differentiation. Blood. 2016 Aug 25;128(8):e20-31.
  5. Complex Analysis of Single-Cell RNA Sequencing Data. Biochemistry (Mosc). 2023 Feb;88(2):231-252.
  6. Single-cell RNA-seq reveals changes in cell cycle and differentiation programs upon aging of hematopoietic stem cells. Genome Res. 2015 Dec;25(12):1860-72. doi: 10.1101/gr.192237.115 IF: 6.2 Q1 B2
  7. Decontamination of ambient RNA in single-cell RNA-seq with DecontX. Genome Biol. 2020 Mar 5;21(1):57. doi: 10.1186/s13059-020-1950-6 IF: 10.1 Q1 B1
  8. 生信技能树:https://mp.weixin.qq.com/s/b3b0WeL4c-4gRYY7LRg81w https://mp.weixin.qq.com/s/ndt9Fsgg5dNxIOh9m7j9Bw
  9. 单细胞天地:https://mp.weixin.qq.com/s/O0U8vlMIG9vUVE3FK08LJg
  10. 生信菜鸟团:https://mp.weixin.qq.com/s/UiO7AQczrcMdKENCWkLRCQ
  11. 生信星球:https://mp.weixin.qq.com/s/bJ0tNj4w5p4R1MX5RHG9Ng

致谢:感谢曾老师以及生信技能树团队全体成员。更多精彩内容可关注公众号:生信技能树,单细胞天地,生信菜鸟团等公众号。

:若对内容有疑惑或者有发现明确错误的朋友,请联系后台(欢迎交流)。更多相关内容可关注公众号:生信方舟

- END -

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 三种工具的简单介绍和思考
    • 1.细胞周期矫正
    • 2.去除双胞体
    • 3.去除RNA污染(DecontX)
  • 分析步骤—矫正细胞周期
    • 1.导入
    • 2.数据预处理
    • 3.ScaleData矫正
      • 3.1 处理和不处理结果对比
    • 4.重新对scRNA和scRNA_new数据进行PCA以及后续流程并进行结果对比
  • 分析步骤—去除双胞体+RNA污染
    • 1.导入
    • 2.DoubletFinder分析
    • 3.decontX去除单细胞数据的环境游离RNA污染
    • 4.记得保存数据
  • 参考资料:
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档