批次校正-单细胞转录组分析批次效应的校正scRNA-02

背景:2013年到2023年是单细胞转录组分析的第一个十年。批次校正在单细胞转录组分析中至关重要,根据不同样本的基因表达模式可以分析样本与样本之间的生物学差异。然而基因表达模式可能会因为实验方法、技术方法等原因引入偶然误差,例如明明是同一个细胞类型的细胞,却在不同的样本间无法聚类在一起。

  • 基因表达模式简介
    什么是单细胞转录组分析的基因表达模式呢?举例来说,如果我们要描述不同的人,可以通过身高,体重,肤色等特征来说明:
同学A [身高:180,体重:50,肤色:白色...]
同学B [身高:170,体重:55,肤色:黄色...]
同学C [身高:175,体重:60,肤色:白色...]
同学D [身高:165,体重:50,肤色:黑色...]

那么我们对这些同学进行分类(单细胞转录组细胞分群),就是根据他们的这些特征情况,计算每个同学之间的相似性,比如肤色相同的同学更相近,如果在肤色相同的情况下,他们的体重也相近,那么他们的关系就更加接近...对于单细胞转录组分析来说,每个细胞就是每个同学,每个特征就是每个基因。

于是,构建同学的信息矩阵如下:

#'同学的信息矩阵
        同学A 同学B 同学C 同学D
身高   180    170   175  165
体重    50    55    60    50
肤色   白色   黄色  白色  黑色
...
#'单细胞转录组的counts矩阵
        细胞A 细胞B 细胞C 细胞D
基因1   0    1    1    0
基因2   1    0    0    1
基因3   1   1     0    0
...

这其实是和单细胞转录组的原始counts矩阵类似,那么单细胞转录组后续的分析就是算法在这种矩阵信息的基础上,根据每个细胞的所有基因表达情况对细胞进行基因表达模式的分析,将相似的基因表达模式的细胞聚类在一起。

而批次效应,就是因为每个批次的实验方法等不同造成同一个类型的细胞的某些(非细胞类型的关键基因)基因表达量存在较大的差异,使得在聚类可视化的时候,明明是同一个类型的细胞,却因为批次没有聚在一起。例如下图红框中的细胞,根据细胞类型注释它们都是CD14+细胞,然而在最左边的可视化图中,却因为batch不同形成了分群。


batch.png

样本的批次无法消除,要数据量多就需要尽可能使用不同实验室、不同机构测出来的数据。去除批次效应,只能通过各种算法,重新计算细胞之间的相似度(根据细胞类型的关键基因),将同一个类型的细胞的基因表达量校正回来。例如在肿瘤分析中,尽量减少不同病人之间同一细胞类型的误差。

类型一:使用R包Seurat方法

library(Seurat)
library(dplyr)
sc<-readRDS("sc.merge.rds")
data<-NormalizeData(sc)%>% FindVariableFeatures(nfeatures=2000) %>% 
      ScaleData() %>% RunPCA() %>% FindNeighbors(dims = 1:30) %>%
      RunUMAP(dims = 1:30) %>% FindClusters(resolution=0.05)
saveRDS(data,"sc.rds")
#'未去除批次时候降维聚类
seurat_clusters.png

samples.png
  • 方法一 Seurat-integration
library(Seurat)
sc<-"" #'Seurat对象
split.by<-"" #'需要去除批次的批次标签名字
method<-"" #'标准化的方法 log或者sct或者harmony
nfeatures<-3500 #'高变基因保留的数目,默认3500
dims<-30 #'降维的维度
resolution<-0.5 #'聚类分辨率
sc.list<-SplitObject(sc, split.by = split.by)
if (method == "log") {
  sc.list = lapply(sc.list, function(x) {
    x = NormalizeData(x) %>% FindVariableFeatures(nfeatures = nfeatures) %>% 
      ScaleData()
    return(x)
  })
  sc.anchors <- FindIntegrationAnchors(object.list = sc.list, 
                                        dims = 1:30)
  sc.combined <- IntegrateData(anchorset = sc.anchors, 
                                dims = 1:30)
  DefaultAssay(sc.combined) <- "integrated"
  sc.combined <- sc.combined %>% ScaleData() %>% 
    RunPCA() %>% FindNeighbors(dims = 1:dims) %>% RunUMAP(dims = 1:dims) %>% 
    FindClusters(resolution = resolution)
  data <- sc.combined
}
pdf("umap_seurat_clusters.pdf",height=7,width=10)
DimPlot(data,reduction = "umap",group.by="seurat_clusters")
dev.off()
pdf("umap_groups.pdf",height=7,width=10)
DimPlot(data,reduction = "umap",group.by="groups")
dev.off()
pdf("umap_samples.pdf",height=7,width=10)
DimPlot(data,reduction = "umap",group.by="orig.ident")
dev.off()
#'data就是去除批次后的数据
integrated_seurat_clusters.png

integrated_samples.png
  • 方法二 Seurat-SCTransform
library(Seurat)
sc<-"" #'Seurat对象
split.by<-"" #'需要去除批次的批次标签名字
method<-"" #'标准化的方法 log或者sct或者harmony
nfeatures<-3500 #'高变基因保留的数目,默认3500
dims<-30 #'降维的维度
resolution<-0.5 #'聚类分辨率
sc.list<-SplitObject(sc, split.by = split.by)
if (method == "sct") {
sc.list <- lapply(X = sc.list, FUN = function(x){
    x <- SCTransform(x,assay = Assays(x)[1])
    return(x)
  })
  features <- SelectIntegrationFeatures(object.list = sc.list,nfeatures=nfeatures)
  sc.list <- PrepSCTIntegration(object.list = sc.list, anchor.features = features) 
  sc.list <- FindIntegrationAnchors(object.list = sc.list, normalization.method = "SCT",anchor.features = features)
  sc.combined <- IntegrateData(anchorset = sc.list, normalization.method = "SCT")
  sc.combined <- RunPCA(sc.combined, verbose = FALSE)
  sc.combined <- RunUMAP(sc.combined, reduction = "pca", dims = 1:dims)
  sc.combined <- FindNeighbors(sc.combined, reduction = "pca", dims = 1:dims)
  sc.combined <- FindClusters(sc.combined, resolution = resolution)
  data<-sc.combined
}
SCT_seurat_clusters.png
SCT_samples.png
  • 方法三 Seurat-harmony
library(Seurat)
library(harmony)
sc<-"" #'Seurat对象
split.by<-"" #'需要去除批次的批次标签名字
method<-"" #'标准化的方法 log或者sct或者harmony
nfeatures<-3500 #'高变基因保留的数目,默认3500
dims<-30 #'降维的维度
resolution<-0.5 #'聚类分辨率
sc.list<-SplitObject(sc, split.by = split.by)
if(method=="harmony"){
  sc <- NormalizeData(sc, normalization.method = "LogNormalize", scale.factor = 10000)
  sc <- FindVariableFeatures(sc, selection.method = "vst", nfeatures = nfeatures)
  all.genes <- rownames(sc) #'ScaleData使用的基因,否则后面可能报错
  sc <- ScaleData(sc , features = all.genes, vars.to.regress = "nCount_RNA")
  sc <- RunPCA(sc, features = VariableFeatures(object = sc))
  sc <- RunHarmony(sc, split.by , plot_convergence = F,dims.use = 1:dims)
  sc.combine <- sc
  sc.combine = RunTSNE(sc.combine, reduction = "harmony", dims = 1:dims)
  sc.combine = RunUMAP(sc.combine, reduction = "harmony", dims = 1:dims)
  sc.combine = FindNeighbors(sc.combine, reduction = "harmony",dims = 1:dims)
  sc.combine = FindClusters(sc.combine, resolution = resolution)
  data<-sc.combine
}
harmony_seurat_clusters.png

samples.png

类型二:使用python中的包(待整理)

安装需要的python包:

conda install -c conda-forge scanpy
pip install scib
conda install -c conda-forge omicverse
conda install -c bioconda scvi

首先需要将Seurat对象转换为.h5ad文件:

library(SeuratDisk)
library(Seurat)
sc<-readRDS("sc.rds") #'读入RDS文件
SaveH5Seurat(sc,filename="sc.h5seurat", overwrite = TRUE)
Convert("sc.h5seurat", dest = "h5ad", overwrite = TRUE) #'转换为当前路径下的.h5ad文件

未去批次时的降维聚类可视化:

import omicverse as ov
import scanpy as sc
adata=sc.read('sc.h5ad')
adata=ov.pp.qc(adata,
              tresh={'mito_perc': 0.2, 'nUMIs': 500, 'detected_genes': 250},
              batch_key='groups') #'质控
ov.utils.store_layers(adata,layers='counts')
adata=ov.pp.preprocess(adata,mode='shiftlog|pearson',
                       n_HVGs=2000,batch_key='groups') #'高变基因
#adata
adata.raw = adata
adata = adata[:, adata.var.highly_variable_features]
ov.pp.scale(adata)
ov.pp.pca(adata,layer='scaled',n_pcs=30) #PC选择30个维度
adata.obsm["X_mde_pca"] = ov.utils.mde(adata.obsm["scaled|original|X_pca"])
ov.utils.embedding(adata,
                basis='X_mde_pca',frameon='small',
                color=['groups','seurat_clusters'],show=False)
#'未去批次时的降维聚类

  • 方法一 combat
adata_combat=ov.single.batch_correction(adata,batch_key='groups',
                                        methods='combat',n_pcs=30)
adata.obsm["X_mde_combat"] = ov.utils.mde(adata.obsm["X_combat"])
ov.utils.embedding(adata,
                basis='X_mde_combat',frameon='small',
                color=['groups','seurat_clusters'],show=False)
  • 方法二 scanorama
adata_scanorama=ov.single.batch_correction(adata,batch_key='groups',
                                        methods='scanorama',n_pcs=30)
adata.obsm["X_mde_scanorama"] = ov.utils.mde(adata.obsm["X_scanorama"])
ov.utils.embedding(adata,
                basis='X_mde_scanorama',frameon='small',
                color=['groups','seurat_clusters'],show=False)

总结:批次校正是一个相当复杂的环节,对不同的样本数据有着不同的方法应用效果,具体使用哪个方法需要根据校正结果决定。此外,批次校正方法众多,还有很多新方法可以尝试。

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