seurat-PrepSCTFindMarkers源码解析

一、FindAllMarkers()函数报错

最近事情太多,好久没有写简书,断更很久,惭愧。
前几天,将服务器的seurat包升级到4.1.0版本,在跑单细胞转录组流程的时候发现在FindAllMarkers步骤报错,想细究下原因。
前面的流程是我使用SCTransform方法对单细胞数据进行标准化;
代码如下:

DefaultAssay(seurat_obj) <- "SCT" 
Idents(seurat_obj) <- "cluster"
markers_all <- FindAllMarkers(object = seurat_obj, min.pct = 0.25, logfc.threshold = 0.25, only.pos=T)

报错的截图:


提示的信息是我需要在执行FindMarkers()函数前,先运行PrepSCTFindMarkers()函数。


二、PrepSCTFindMarkers()函数的用途

去年,对于SCTransform归一化处理后的单细胞数据如何进行差异基因表达分析,我就很纠结,之前写了一篇随笔《sctransform预处理后,如何进行差异表达分析》。如今,在2022年1月14日,seurat终于对SCTransform归一化处理后如何合理进行差异表达分析,官宣了PrepSCTFindMarkers()函数。

seurat团队应该是慎之又慎,才宣布此函数,很想看看它具体做了一些什么操作。

查了下PrepSCTFindMarkers()的说明
该函数的说明文档:
https://rdrr.io/cran/Seurat/man/PrepSCTFindMarkers.html
https://cran.r-project.org/web/packages/Seurat/Seurat.pdf

函数用途:

Prepare object to run differential expression on SCT assay with multiple models

详细说明:

Given a merged object with multiple SCT models, this function uses minimum of the median UMI (calculated using the raw UMI counts) of individual objects to reverse the individual SCT regression model using minimum of median UMI as the sequencing depth covariate. The counts slot of the SCT assay is replaced with recorrected counts and the data slot is replaced with log1p of recorrected counts.

大致翻译一下:
对多样本的单细胞转录组数据整合后生成的seurat对象,提前是已经进行了SCTransform归一化处理。针对SCT模式下,进行差异表达分析,需要对seurat对象进行预处理,修正 SCT 模式下的counts矩阵和data矩阵。该函数计算每个样本的UMI中位数(使用原始 UMI 计数计算),取其最小值,然后应用于每个样本的SCT回归模型,使用UMI 中位数的最小值作为测序深度协变量。SCT模式下的counts矩阵被重新校正的counts值所取代,同时,data矩阵也被重新校正counts的 log1p 替换。

换句话说,用SCT模式的data矩阵做差异表达分析,不是很妥,需要预先用PrepSCTFindMarkers()函数校正,校正后的data矩阵,更适合做下游的差异基因表达分析。

函数用法:

PrepSCTFindMarkers(object, assay = "SCT", verbose = TRUE)

那为什么用原先的SCT模式的data做差异表达分析不行?
作者在曾在seurat的discussions部分中也给出了一段说明:

We had anticipated extending Seurat to actively support DE using the pearson residuals of sctransform, but have decided not to do so. In some cases, Pearson residuals may not be directly comparable across different datasets, particularly if there are batch effects that are unrelated to sequencing depth. While it is possible to correct these differences using the SCTransform-based integration workflow for the purposes of visualization/clustering/etc., we do not recommend running differential expression directly on Pearson residuals. Instead, we recommend running DE on the standard RNA assay.

我们曾期望扩展Seurat的功能以支持使用sctransform的pearson残差进行DE分析,但已决定不这样做。在某些情况下,皮尔逊残差可能无法在不同的数据集之间直接进行比较,尤其是当存在与测序深度无关的批次效应时。虽然为了可视化/聚类等目的,可以使用基于SCTransform的集成工作流来纠正这些差异,但我们不建议直接在Pearson残差上运行差分表达式。相反,我们建议在标准RNA分析中运行DE分析。

我们可以这样理解,Satija团队本来期望用sctransform的皮尔逊残差进行DE分析,但是他们发现,Pearson残差可能无法在不同数据集之间直接进行比较,决定不这么做。基于SCTransform 的分析工作流可以剔除实验差异以实现可基因可视化和聚类等功能。但是他们建议在标准RNA assay中运行DE分析。

给出的案例是:
针对SCTransform归一化流程,在FindMarkers之前需要运行PrepSCTFindMarkers()函数。

data("pbmc_small") 
pbmc_small1 <- SCTransform(object = pbmc_small, variable.features.n = 20) 
pbmc_small2 <- SCTransform(object = pbmc_small, variable.features.n = 20) 
pbmc_merged <- merge(x = pbmc_small1, y = pbmc_small2) 
pbmc_merged <- PrepSCTFindMarkers(object = pbmc_merged) 
markers <- FindMarkers( 
  object = pbmc_merged, 
  ident.1 = "0", 
  ident.2 = "1", 
  assay = "SCT" 
) 
pbmc_subset <- subset(pbmc_merged, idents = c("0", "1")) 
markers_subset <- FindMarkers( 
  object = pbmc_subset, 
  ident.1 = "0", 
  ident.2 = "1", 
  assay = "SCT", 
  recorrect_umi = FALSE 
)

三、PrepSCTFindMarkers()函数解析

源码位置:https://github.com/satijalab/seurat/blob/f1b2593ea72f2e6d6b16470dc7b9e9b645179923/R/differential_expression.R

library(Seurat)
data("pbmc_small") 
pbmc_small1 <- SCTransform(object = pbmc_small, variable.features.n = 20) 
pbmc_small2 <- SCTransform(object = pbmc_small, variable.features.n = 20) 
pbmc_merged <- merge(x = pbmc_small1, y = pbmc_small2)
pbmc_merged
# An object of class Seurat 
# 450 features across 160 samples within 2 assays 
# Active assay: SCT (220 features, 0 variable features)
# 1 other assay present: RNA

下面我们进入到PrepSCTFindMarkers()函数中,代码较长,我们分解一下:
step1:判断seurat对象的SCT模型的数目,如果只有一个样本,跳出此函数,不需要进行SCT模型的counts和data数据校正;
在我们的案例中,有2个样本,即两个SCR模型。

if (length(x = levels(x = object[[assay]])) == 1) {
    if (verbose) {
      message("Only one SCT model is stored - skipping recalculating corrected counts")
    }
    return(object)
  }

step2:此处调用了SCTResults函数,计算每个SCT模型的UMI中位数。先不细究该函数。

observed_median_umis <- lapply(
  X = SCTResults(object = object[[assay]], slot = "cell.attributes"),
  FUN = function(x) median(x[, "umi"])
)
observed_median_umis
# $model1
# [1] 180
# 
# $model1.1
# [1] 180

step3:计算min_median_umi

model.list <- slot(object = object[[assay]], name = "SCTModel.list")
model.list
# $model1
# An sctransform model.
# Model formula:  y ~ log_umi 
# Parameters stored for 220 features, 80 cells
# $model1.1
# An sctransform model.
# Model formula:  y ~ log_umi 
# Parameters stored for 220 features, 80 cells

median_umi.status <- lapply(X = model.list,
                            FUN = function(x) { return(tryCatch(
                              expr = slot(object = x, name = 'median_umi'),
                              error = function(...) {return(NULL)})
                            )})
# median_umi.status
# $model1
# [1] 180
# 
# $model1.1
# [1] 180
if (any(is.null(x = unlist(x = median_umi.status)))){
  # For old SCT objects  median_umi is set to median umi as calculated from obserbed UMIs
  slot(object = object[[assay]], name = "SCTModel.list") <- lapply(X = model.list,
                                                                   FUN = UpdateSlots)
  SCTResults(object = object[[assay]], slot = "median_umi") <- observed_median_umis
  
}
model_median_umis <- SCTResults(object = object[[assay]], slot = "median_umi")
min_median_umi <- min(unlist(x = observed_median_umis))

step4:生成校正的corrected_counts矩阵
step5:对corrected_counts取log1p,生成校正的corrected_data矩阵
时间太赶,先大致记录下,后面再补充。

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

推荐阅读更多精彩内容