单细胞Seurat包升级之2,700 PBMCs分析(下)

刘小泽写于19.8.19
主要介绍官方教程PBMC 3K的下游分析(https://satijalab.org/seurat/v3.0/pbmc3k_tutorial.html
上篇:单细胞Seurat包升级之2,700 PBMCs分析(上)

数据标准化(scale)

它的目的是实现数据的线性转换(scaling),这也是降维处理(如PCA)之前的标准预处理。
主要利用了ScaleData函数,回忆一下,之前归一化(normalize)这里做的操作是log处理,它是对所有基因的表达量统一对待的,最后放在了一个起跑线上。但是为了真正找到差异,我们还要基于这个起跑线,考虑不同样本对表达量的影响

它做了:

  • 将每个基因在所有细胞的表达量进行调整,使得每个基因在所有细胞的表达量均值为0
  • 将每个基因的表达量进行标准化,让每个基因在所有细胞中的表达量方差为1
# 先使用全部基因
all.genes <- rownames(pbmc)
> length(all.genes)
[1] 13714
pbmc <- ScaleData(pbmc, features = all.genes)
# 结果存储在pbmc[["RNA"]]@scale.data中

感觉这一步太慢,能不能加速一点?

这一步主要目的就是下一步的降维,使用全部基因是比较慢。如果想提高速度,可以只对鉴定的HVGs(之前FindVariableFeatures设置的是2000个)进行操作,其实这个函数默认就是对部分差异基因进行操作,:pbmc <- ScaleData(pbmc) 。这样操作的结果中降维和聚类不会被影响,但是只对HVGs基因进行标准化,下面画的热图依然会受到全部基因中某些极值的影响。所以为了热图的结果,还是对所有基因进行标准化比较好。

ScaleData的操作过程是怎样的?

看一下帮助文档就能了解:
ScaleData这个函数有两个默认参数:do.scale = TRUEdo.center = TRUE,然后需要输入进行scale/center的基因名(默认是取前面FindVariableFeatures的结果)。
scalecenter这两个默认参数应该不陌生了,center就是对每个基因在不同细胞的表达量都减去均值;
scale就是对每个进行center后的值再除以标准差(就是进行了一个z-score的操作

再看一个来自BioStar的讨论:https://www.biostars.org/p/349824/

问题1:为什么在NormalizeData()之后,还需要进行ScaleData操作?

前面NormalizeData进行的归一化主要考虑了测序深度/文库大小的影响(reads *10000 divide by total reads, and then log),但实际上归一化的结果还是存在基因表达量分布区间不同的问题;而ScaleData做的就是在归一化的基础上再添zero-centres (mean/sd =》 z-score),将各个表达量放在了同一个范围中,真正实现了”表达量的同一起跑线“。

另外,新版的ScaleData函数还支持设定回归参数,如(nUMI、percent.mito),来校正一些技术噪音

问题2: FindVariableGenes() or RunPCA() or FindCluster()这些参数是基于归一化Normalized_Data还是标准化Scaled_Data

所有操作都是基于标准化的数据Scaled_Data ,因为这个数据是针对基因间比较的。例如有两组基因表达量如下:

g1 10 20 30 40 50
g2 20 40 60 80 100

虽然它们看起来是g2的表达量是g1的两倍,但真要降维聚类时,就要看scale的结果:

> scale(c(10,20,30,40,50))
           [,1]
[1,] -1.2649111
[2,] -0.6324555
[3,]  0.0000000
[4,]  0.6324555
[5,]  1.2649111
attr(,"scaled:center")
[1] 30
attr(,"scaled:scale")
[1] 15.81139
> scale(c(20,40,60,80,100))
           [,1]
[1,] -1.2649111
[2,] -0.6324555
[3,]  0.0000000
[4,]  0.6324555
[5,]  1.2649111
attr(,"scaled:center")
[1] 60
attr(,"scaled:scale")
[1] 31.62278

二者标准化后的分布形态是一样的(只是中位数不同),而我们后来聚类也是看数据的分布,分布相似聚在一起。所以归一化差两倍的两个基因,根据标准化的结果依然聚在了一起

问题3: ScaleData()在scRNA分析中一定要用吗?

实际上标准化这个过程不是单细胞分析固有的,在很多机器学习、降维算法中比较不同feature的距离时经常使用。当不进行标准化时,有时feature之间巨大的差异(就像👆问题2中差两倍的基因表达量,实际上可能差的倍数会更多)会影响分析结果,让我们误以为它们的数据分布相差很远。

很多时候,我们会混淆normalizationscale:那么看一句英文解释:

Normalization "normalizes" within the cell for the difference in sequenicng depth / mRNA throughput. 主要着眼于样本的文库大小差异
Scaling "normalizes" across the sample for differences in range of variation of expression of genes . 主要着眼于基因的表达分布差异

另外,看scale()函数的帮助文档就会发现,它实际上是对列进行操作:

那么具体操作中是要先对表达矩阵转置,然后scale,最后转回来

t(scale(t(dat[genes,])))
问题4:RunCCA()函数需要归一化数据还是标准化数据?

通过看这个函数的帮助文档:

RunCCA(object, object2, group1, group2, group.by, num.cc = 20, genes.use,
  scale.data = TRUE, rescale.groups = FALSE, ...)

显示scale.data = TRUE,那么就需要标准化后的数据

怎样移除不想要的差异来源?

在进行降维聚类时,主要就是考虑数据差异对分布产生的影响。由于技术误差导致的差异是我们不感兴趣的,排除这一部分其实也在上面的问题1中提到了,如果说不想线粒体污染导致的差异影响整体差异分布:

pbmc <- ScaleData(pbmc, vars.to.regress = "percent.mt")

这个函数仅针对初级用户,如果想深入探索技术误差的排除,官方给出了另一个专业版函数:SCTransform,它也包含vars.to.regress这个选项。详细的说明在:https://satijalab.org/seurat/v3.0/sctransform_vignette.html


进行线性降维

最常用的线性降维就是PCA方法,使用标准化的数据

它默认选择之前鉴定的差异基因(2000个)作为input,但可以使用features进行设置;默认分析的主成分数量是50个

# 这里就使用默认值
pbmc <- RunPCA(pbmc, features = VariableFeatures(object = pbmc))
# 结果保存在reductions 这个接口中

检查下PCA结果:

> print(pbmc[["pca"]], dims = 1:3, nfeatures = 5)
# 或者用 print(pbmc@reductions$pca, dims = 1:3, nfeatures = 5)
PC_ 1 
Positive:  CST3, TYROBP, LST1, AIF1, FTL 
Negative:  MALAT1, LTB, IL32, IL7R, CD2 
PC_ 2 
Positive:  CD79A, MS4A1, TCL1A, HLA-DQA1, HLA-DQB1 
Negative:  NKG7, PRF1, CST7, GZMB, GZMA 
PC_ 3 
Positive:  HLA-DQA1, CD79A, CD79B, HLA-DQB1, HLA-DPB1 
Negative:  PPBP, PF4, SDPR, SPARC, GNG11 

VizDimLoadings对print的结果可视化:

VizDimLoadings(pbmc, dims = 1:3, reduction = "pca")
对print的结果可视化

DimPlot进行降维后的可视化

提取两个主成分(默认前两个,当然可以修改dims选项)绘制散点图

DimPlot(pbmc, reduction = "pca")

这个图中每个点就是一个样本,它根据PCA的结果坐标进行画图,这个坐标保存在了cell.embeddings中:

> head(pbmc[['pca']]@cell.embeddings)[1:2,1:2]
                     PC_1       PC_2
AAACATACAACCAC -4.7296855 -0.5184265
AAACATTGAGCTAC -0.5174029  4.5918957

其实还支持定义分组:根据pbmc@meta.data中的ident分组:

> table(pbmc@meta.data$orig.ident)

pbmc3k 
  2638 
# 当然这里只有一个分组

探索异质性来源—DimHeatmap

每个细胞和基因都根据PCA结果得分进行了排序,默认画前30个基因(nfeatures设置),1个主成分(dims设置),细胞数没有默认值(cells设置)

DimHeatmap(pbmc, dims = 1:15, cells = 500, balanced = TRUE)
# 其中balanced表示正负得分的基因各占一半

降维的真实目的是尽可能减少具有相关性的变量数目,例如原来有700个样本,也就是700个维度/变量,但实际上根据样本中的基因表达量来归类,它们或多或少都会有一些关联,这样有关联的一些样本就会聚成一小撮,表示它们的”特征距离“相近。最后直接分析这些”小撮“,就用最少的变量代表了实际最多的特征

既然每个主成分都表示具有相关性的一小撮变量(称之为”metafeature“,元特征集),那么问题来了:

降维后怎么选择合适数量的主成分来代表整个数据集

选10个?20个?50个?最简单的方法就是:

ElbowPlot(pbmc)

它是根据每个主成分对总体变异水平的贡献百分比排序得到的图,我们主要关注”肘部“的PC,它是一个转折点(也即是这里的PC9-10),说明取前10个主成分可以比较好地代表总体变化

但官方依然建议我们,下游分析时多用几个成分试试(10个、15个甚至50个),如果起初选择的合适,结果不会有太大变化;另外推荐开始不确定时要多选一些主成分,也不要直接就定下5个成分进行后续分析,那样很有可能会出错


细胞聚类

Seurat 3版本提供了graph-based 聚类算法,参考两篇文章:SNN-Cliq, Xu and Su, Bioinformatics, 2015]PhenoGraph, Levine et al., Cell, 2015]. 简言之,这些graph的方法都是将细胞嵌入一个算法图层中,例如:K-nearest neighbor (KNN) graph 中,每个细胞之间的连线就表示相似的基因表达模式,然后按照这个相似性将图分隔成一个个的高度相关的小群体(名叫‘quasi-cliques’ or ‘communities’);

KNN解释(https://slideplayer.com/slide/7087250/)

又比如,在PhenoGraph中,首先根据PCA的欧氏距离结果,构建了KNN图,找到了各个近邻组成的小社区;然后根据近邻中两两住户(细胞)之间的交往次数(shared overlap)计算每条线的权重(术语叫Jaccard similarity) 。这些计算都是用包装好的函数FindNeighbors() 得到的,它的输入就是前面降维最终确定的主成分

# 因为我们前面挑选了10个PCs,所以这里dims定义为10个
pbmc <- FindNeighbors(pbmc, dims = 1:10)

以上是凭个人理解发挥,大概就是这么个意思,不涉及算法层面推导

得到权重后,为了对细胞进行聚类,使用了计算模块的算法(默认使用Louvain,另外还有包括SLM的其他三种),使用FindClusters() 进行聚类。其中包含了一个resolution的选项,它会设置一个”间隔“值,该值越大,间隔越大,得到的cluster数量越多

怎么理解这个resolution参数?
可以想象成配眼镜的时候,镜片度数越高,分辨率越高,看的越清楚,看到的细节越丰富(cluster越多);反之,如果分辨率调的很低,结果就看的模模糊糊一大坨

一般来说,这个值在细胞数量为3000左右时设为0.4-1.2 会有比较好的结果

pbmc <- FindClusters(pbmc, resolution = 0.5)
# 结果聚成几类,用Idents查看
> head(Idents(pbmc), 5)
AAACATACAACCAC AAACATTGAGCTAC AAACATTGATCAGC AAACCGTGCTTCCG 
             1              3              1              2 
AAACCGTGTATGCG 
             6 
Levels: 0 1 2 3 4 5 6 7 8
# 发现3000细胞,设resolution=0.5时,得到Number of communities: 9

另一种降维方法—非线性降维(UMAP/tSNE)

线性降维PCA(1933)一个特点就是:它将高维中不相似的数据点放在较低维度时,会尽可能多地保留原始数据的差异,因此导致这些不相似的数据相距甚远,大多的数据重叠放置
tSNE(2008)兼顾了高维降维+低维展示,降维后的那些不相似的数据点依然可以放得靠近一些,保证了点既在局部分散,又在全局聚集
后来开发的UMAP(2018)相比tSNE又能展示更多的维度(参考文献:https://sci-hub.tw/https://doi.org/10.1038/nbt.4314)

做个对比:

https://www.nature.com/articles/nbt.4314
# 使用UMAP
pbmc <- RunUMAP(pbmc, dims = 1:10)
DimPlot(pbmc, reduction = "umap") # 还可以设置label = TRUE让数字显示在每个cluster上

# 对计算过程很费时的结果保存一下
save(pbmc, file = 'pbmc_umap.Rdata')

需要注意的点:

注意1:tSNE算法具有随机性,为了保证重复性,要固定一个随机种子

注意2:如何在R中使用UMAP?

以下测试是在个人电脑上,需要管理员权限

因为UMAP是基于Python的,如果要用它,必须先保证有一个python的安装环境,先试验一下:

> reticulate::py_install(packages ='umap-learn')
# 结果报错,说让我升级一下virtualenv(mac和linux默认使用virtualenv,而Windows只能使用conda)
Error: Prerequisites for installing Python packages not available.

Execute the following at a terminal to install the prerequisites:

$ sudo /usr/local/bin/pip install --upgrade virtualenv

然后直接在Rstudio中打开Terminal,输入:sudo /usr/local/bin/pip install --upgrade virtualenv

再运行一遍上面的R代码(成功与否取决于个人的网络,因为它要通过外链去下载,wlan有限制的话就用个人热点吧):

一般总共二三十兆的文件,需要下载个十几分钟,速度确实很慢,而这个过程很有可能出现断线,只能重新运行安装。已下载完的文件会直接识别不会重复下载。检查是否下载成功标志就是:library(umap)能否成功,这个是Seurat调用的基础。最后特别注意,要重启一下Rstudio再运行RunUMAP


找差异表达基因(cluster biomarker)

目的是根据表达量不同这个特征而分出不同细胞群的基因们(就是找具有生物学意义的HVGs=》即biomarkers)
marker可以理解成标记,就是一看到有这些基因,就说明是这个细胞群体

主要利用FindAllMarkers()函数,它默认会对单独的一个细胞群与其他几群细胞进行比较找到正、负表达biomarker(这里的正负有点上调、下调基因的意思;正marker表示在我这个cluster中表达量高,而在其他的cluster中低)

# 找到cluster1中的marker基因
cluster1.markers <- FindMarkers(pbmc, ident.1 = 1, min.pct = 0.25)
head(cluster1.markers, n = 5)

上面这个代码中min.pct的意思是:一个基因在任何两群细胞中的占比最低不能低于多少,当然这个可以设为0,但会大大加大计算量

另外,如果要比较cluster5和cluster0、cluster3的marker基因:

cluster5.markers <- FindMarkers(pbmc, ident.1 = 5, ident.2 = c(0, 3), min.pct = 0.25)
head(cluster5.markers, n = 5)

一步到位的办法FindAllMarkers(对所有cluster都比较一下,并只挑出来正表达marker):

pbmc.markers <- FindAllMarkers(pbmc, only.pos = TRUE, min.pct = 0.25, logfc.threshold = 0.25)
# 这一步过滤好好理解(进行了分类、排序、挑前2个)
pbmc.markers %>% group_by(cluster) %>% top_n(n = 2, wt = avg_logFC)

Seurat提供了多种差异分析的检验方法,在test.use选项中设置(详见:DE vignette )例如:

cluster1.markers <- FindMarkers(pbmc, ident.1 = 0, logfc.threshold = 0.25, test.use = "roc", only.pos = TRUE)
多种可视化方法:
  • VlnPlot:用小提琴图对某些基因在全部cluster中表达量进行绘制

    VlnPlot(pbmc, features = c("MS4A1", "CD79A"))
    
  • FeaturePlot:最常用的可视化=》将基因表达量投射到降维聚类结果中

    FeaturePlot(pbmc, features = c("MS4A1", "GNLY", "CD3E", "CD14", "FCER1A", "FCGR3A", "LYZ", "PPBP", "CD8A"))
    

另外还有:RidgePlot, CellScatter, and DotPlot

  • DoHeatmap 对给定细胞和基因绘制热图,例如对每个cluster绘制前20个marker

    top10 <- pbmc.markers %>% group_by(cluster) %>% top_n(n = 10, wt = avg_logFC)
    DoHeatmap(pbmc, features = top10$gene) + NoLegend()
    

赋予每个cluster细胞类型

重点在于背景知识的理解,能够将marker与细胞类型对应起来,例如这里从各个cluster中找到的marker对应到了不同的细胞(这一过程是比较考验课题理解程度的):

有了这个,绘图就很简单:

new.cluster.ids <- c("Naive CD4 T", "Memory CD4 T", "CD14+ Mono", "B", "CD8 T", "FCGR3A+ Mono", "NK", "DC", "Platelet")
names(new.cluster.ids) <- levels(pbmc)
pbmc <- RenameIdents(pbmc, new.cluster.ids)
DimPlot(pbmc, reduction = "umap", label = TRUE, pt.size = 0.5) + NoLegend()

欢迎关注我们的公众号~_~  
我们是两个农转生信的小硕,打造生信星球,想让它成为一个不拽术语、通俗易懂的生信知识平台。需要帮助或提出意见请后台留言或发送邮件到jieandze1314@gmail.com

Welcome to our bioinfoplanet!

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