Seurat小提琴图为什么有的只有点儿?

男,

一个长大了才会遇到的帅哥,

稳健,潇洒,大方,靠谱。

一段生信缘,一棵技能树,

一枚大型测序工厂的螺丝钉,

一个随机森林中提灯觅食的津门旅客。

library(Seurat)
library(SeuratData)
levels(pbmc3k.final)

[1] "Naive CD4 T"  "Memory CD4 T" "CD14+ Mono"   "B"            "CD8 T"       
[6] "FCGR3A+ Mono" "NK"           "DC"           "Platelet"

VlnPlot(pbmc3k.final, "CD4",slot = "data")

作为一个生物信息工程师,看到这样的图,请解释。

为什么CD14+ Mono和 Memory CD4 T 有怎么多的点,却没有小提琴呢?

那, 我们要看看作图细节了。

> VlnPlot
function (object, features, cols = NULL, pt.size = 1, idents = NULL, 
    sort = FALSE, assay = NULL, group.by = NULL, split.by = NULL, 
    adjust = 1, y.max = NULL, same.y.lims = FALSE, log = FALSE, 
    ncol = NULL, combine = TRUE, slot = "data", ...) 
{
    return(ExIPlot(object = object, type = "violin", features = features, 
        idents = idents, ncol = ncol, sort = sort, assay = assay, 
        y.max = y.max, same.y.lims = same.y.lims, adjust = adjust, 
        pt.size = pt.size, cols = cols, group.by = group.by, 
        split.by = split.by, log = log, slot = slot, combine = combine, 
        ...))
}
<bytecode: 0x1a20fce0>
<environment: namespace:Seurat>`

可惜并没有细节,再看ExIPlot。

> ExIPlot
Error: object 'ExIPlot' not found

不是显式函数啊。我们通过debug的方式进入函数内部。

> debug(VlnPlot)
> VlnPlot(pbmc3k.final, "CD4",slot = "data")
debugging in: VlnPlot(pbmc3k.final, "CD4", slot = "data")
debug: {
    return(ExIPlot(object = object, type = "violin", features = features, 
        idents = idents, ncol = ncol, sort = sort, assay = assay, 
        y.max = y.max, same.y.lims = same.y.lims, adjust = adjust, 
        pt.size = pt.size, cols = cols, group.by = group.by, 
        split.by = split.by, log = log, slot = slot, combine = combine, 
        ...))
}
Browse[2]> 
debug: return(ExIPlot(object = object, type = "violin", features = features, 
    idents = idents, ncol = ncol, sort = sort, assay = assay, 
    y.max = y.max, same.y.lims = same.y.lims, adjust = adjust, 
    pt.size = pt.size, cols = cols, group.by = group.by, split.by = split.by, 
    log = log, slot = slot, combine = combine, ...))
Browse[2]> ExIPlot
function (object, features, type = "violin", idents = NULL, ncol = NULL, 
    sort = FALSE, assay = NULL, y.max = NULL, same.y.lims = FALSE, 
    adjust = 1, cols = NULL, pt.size = 0, group.by = NULL, split.by = NULL, 
    log = FALSE, combine = TRUE, slot = "data", ...) 
{
    assay <- assay %||% DefaultAssay(object = object)
    DefaultAssay(object = object) <- assay
    ncol <- ncol %||% ifelse(test = length(x = features) > 9, 
        yes = 4, no = min(length(x = features), 3))
    data <- FetchData(object = object, vars = features, slot = slot)
    features <- colnames(x = data)
    if (is.null(x = idents)) {
        cells <- colnames(x = object)
    }
    else {
        cells <- names(x = Idents(object = object)[Idents(object = object) %in% 
            idents])
    }
    data <- data[cells, , drop = FALSE]
    idents <- if (is.null(x = group.by)) {
        Idents(object = object)[cells]
    }
    else {
        object[[group.by, drop = TRUE]][cells]
    }
    if (!is.factor(x = idents)) {
        idents <- factor(x = idents)
    }
    if (is.null(x = split.by)) {
        split <- NULL
    }
    else {
        split <- object[[split.by, drop = TRUE]][cells]
        if (!is.factor(x = split)) {
            split <- factor(x = split)
        }
        if (is.null(x = cols)) {
            cols <- hue_pal()(length(x = levels(x = idents)))
            cols <- Interleave(cols, InvertHex(hexadecimal = cols))
        }
        else if (length(x = cols) == 1 && cols == "interaction") {
            split <- interaction(idents, split)
            cols <- hue_pal()(length(x = levels(x = idents)))
        }
        else {
            cols <- Col2Hex(cols)
        }
        if (length(x = cols) < length(x = levels(x = split))) {
            cols <- Interleave(cols, InvertHex(hexadecimal = cols))
        }
        cols <- rep_len(x = cols, length.out = length(x = levels(x = split)))
        names(x = cols) <- sort(x = levels(x = split))
    }
    if (same.y.lims && is.null(x = y.max)) {
        y.max <- max(data)
    }
    plots <- lapply(X = features, FUN = function(x) {
        return(SingleExIPlot(type = type, data = data[, x, drop = FALSE], 
            idents = idents, split = split, sort = sort, y.max = y.max, 
            adjust = adjust, cols = cols, pt.size = pt.size, 
            log = log, ...))
    })
    label.fxn <- switch(EXPR = type, violin = ylab, ridge = xlab, 
        stop("Unknown ExIPlot type ", type, call. = FALSE))
    for (i in 1:length(x = plots)) {
        key <- paste0(unlist(x = strsplit(x = features[i], split = "_"))[1], 
            "_")
        obj <- names(x = which(x = Key(object = object) == key))
        if (length(x = obj) == 1) {
            if (inherits(x = object[[obj]], what = "DimReduc")) {
                plots[[i]] <- plots[[i]] + label.fxn(label = "Embeddings Value")
            }
            else if (inherits(x = object[[obj]], what = "Assay")) {
                next
            }
            else {
                warning("Unknown object type ", class(x = object), 
                  immediate. = TRUE, call. = FALSE)
                plots[[i]] <- plots[[i]] + label.fxn(label = NULL)
            }
        }
        else if (!features[i] %in% rownames(x = object)) {
            plots[[i]] <- plots[[i]] + label.fxn(label = NULL)
        }
    }
    if (combine) {
        combine.args <- list(plots = plots, ncol = ncol)
        combine.args <- c(combine.args, list(...))
        if (!"legend" %in% names(x = combine.args)) {
            combine.args$legend <- "none"
        }
        plots <- do.call(what = "CombinePlots", args = combine.args)
    }
    return(plots)
}
<bytecode: 0x19dc8580>
<environment: namespace:Seurat>

这一层函数也没讲小提琴如何画的,再看SingleExIPlot。

Browse[2]> SingleExIPlot
function (data, idents, split = NULL, type = "violin", sort = FALSE, 
    y.max = NULL, adjust = 1, pt.size = 0, cols = NULL, seed.use = 42, 
    log = FALSE) 
{
    if (!is.null(x = seed.use)) {
        set.seed(seed = seed.use)
    }
    if (!is.data.frame(x = data) || ncol(x = data) != 1) {
        stop("'SingleExIPlot requires a data frame with 1 column")
    }
    feature <- colnames(x = data)
    data$ident <- idents
    if ((is.character(x = sort) && nchar(x = sort) > 0) || sort) {
        data$ident <- factor(x = data$ident, levels = names(x = rev(x = sort(x = tapply(X = data[, 
            feature], INDEX = data$ident, FUN = mean), decreasing = grepl(pattern = paste0("^", 
            tolower(x = sort)), x = "decreasing")))))
    }
    if (log) {
        noise <- rnorm(n = length(x = data[, feature]))/200
        data[, feature] <- data[, feature] + 1
    }
    else {
        noise <- rnorm(n = length(x = data[, feature]))/1e+05
    }
    if (all(data[, feature] == data[, feature][1])) {
        warning(paste0("All cells have the same value of ", feature, 
            "."))
    }
    else {
        data[, feature] <- data[, feature] + noise
    }
    axis.label <- "Expression Level"
    y.max <- y.max %||% max(data[, feature])
    if (is.null(x = split) || type != "violin") {
        vln.geom <- geom_violin
        fill <- "ident"
    }
    else {
        data$split <- split
        vln.geom <- geom_split_violin
        fill <- "split"
    }
    switch(EXPR = type, violin = {
        x <- "ident"
        y <- paste0("`", feature, "`")
        xlab <- "Identity"
        ylab <- axis.label
        geom <- list(vln.geom(scale = "width", adjust = adjust, 
            trim = TRUE), theme(axis.text.x = element_text(angle = 45, 
            hjust = 1)))
        jitter <- geom_jitter(height = 0, size = pt.size)
        log.scale <- scale_y_log10()
        axis.scale <- ylim
    }, ridge = {
        x <- paste0("`", feature, "`")
        y <- "ident"
        xlab <- axis.label
        ylab <- "Identity"
        geom <- list(geom_density_ridges(scale = 4), theme_ridges(), 
            scale_y_discrete(expand = c(0.01, 0)), scale_x_continuous(expand = c(0, 
                0)))
        jitter <- geom_jitter(width = 0, size = pt.size)
        log.scale <- scale_x_log10()
        axis.scale <- function(...) {
            invisible(x = NULL)
        }
    }, stop("Unknown plot type: ", type))
    plot <- ggplot(data = data, mapping = aes_string(x = x, y = y, 
        fill = fill)[c(2, 3, 1)]) + labs(x = xlab, y = ylab, 
        title = feature, fill = NULL) + theme_cowplot() + theme(plot.title = element_text(hjust = 0.5))
    plot <- do.call(what = "+", args = list(plot, geom))
    plot <- plot + if (log) {
        log.scale
    }
    else {
        axis.scale(min(data[, feature]), y.max)
    }
    if (pt.size > 0) {
        plot <- plot + jitter
    }
    if (!is.null(x = cols)) {
        if (!is.null(x = split)) {
            idents <- unique(x = as.vector(x = data$ident))
            splits <- unique(x = as.vector(x = data$split))
            labels <- if (length(x = splits) == 2) {
                splits
            }
            else {
                unlist(x = lapply(X = idents, FUN = function(pattern, 
                  x) {
                  x.mod <- gsub(pattern = paste0(pattern, "."), 
                    replacement = paste0(pattern, ": "), x = x, 
                    fixed = TRUE)
                  x.keep <- grep(pattern = ": ", x = x.mod, fixed = TRUE)
                  x.return <- x.mod[x.keep]
                  names(x = x.return) <- x[x.keep]
                  return(x.return)
                }, x = unique(x = as.vector(x = data$split))))
            }
            if (is.null(x = names(x = labels))) {
                names(x = labels) <- labels
            }
        }
        else {
            labels <- levels(x = droplevels(data$ident))
        }
        plot <- plot + scale_fill_manual(values = cols, labels = labels)
    }
    return(plot)
}
<bytecode: 0x1a735330>
<environment: namespace:Seurat>

我们看到核心了,ggplot的代码。

geom <- list(vln.geom(scale = "width", adjust = adjust, 
            trim = TRUE), theme(axis.text.x = element_text(angle = 45, 
            hjust = 1)))
        jitter <- geom_jitter(height = 0, size = pt.size)
        log.scale <- scale_y_log10()
        axis.scale <- ylim

这句子写的真美啊。

那我们就要来试一下了。记得退出debug模式哦。

undebug(VlnPlot)
p<-VlnPlot(pbmc3k.final, "CD4",slot = "data")
#ggplot对象中记录了一张图的所有信息,为了方便演示,我们只取数据出来。

head(p$data)  # 从图中抠数据,学会了吗?

CD4        ident
AAACATACAACCAC  1.370958e-05 Memory CD4 T
AAACATTGAGCTAC -5.646982e-06            B
AAACATTGATCAGC  3.631284e-06 Memory CD4 T
AAACCGTGCTTCCG  6.328626e-06   CD14+ Mono
AAACCGTGTATGCG  4.042683e-06           NK
AAACGCACTGGTAC -1.061245e-06 Memory CD4 T

原汁原味的啊:

ggplot(p$data,aes(ident,CD4)) + geom_violin() + theme_bw()

啥也没有:

加个抖动吧:

ggplot(p$data,aes(ident,CD4)) + geom_violin()  + geom_jitter()+ theme_bw()

这下有点了。所以我们看到的点有左右的区分其实是抖出来的,本身数据的点应该是在一条直线上。然而,小提琴呢?

ggplot(p$data,aes(ident,CD4)) + geom_violin(scale = "width", adjust =1, trim = TRUE)  + geom_jitter()+ theme_bw()

这里我们用seurat内部绘制小提琴图的方式还原了我们问题:为什么CD14+ Mono和 Memory CD4 T 有怎么多的点,却没有小提琴呢?经过上面演示我们知道,其实默认的情况下,我们的数据是都没有小提琴的。所以,当务之急是抓紧时间看看geom_violin的帮助文档。

?geom_violin
?geom_violin
?geom_violin

好了,我们知道一个关键的参数scale = "width"导致了这种局面,其他没有出现小提琴的应该是零值比例太多。

作为好奇,我们看看改一下adjust会有什么改变。

ggplot(p$data,aes(ident,CD4)) + geom_violin(scale = "width", adjust =.5, trim = TRUE)  + geom_jitter()+ theme_bw()

腰变细了,好玩。

既然已经基本锁定问题,我们如何画出都有小提琴的小提琴图呢?也许可以用的方法之一就是,数据过滤。

VlnPlot(subset(pbmc3k.final,CD4 > 0 ), "CD4")+ theme_bw()

什么?改一下腰围?

VlnPlot(subset(pbmc3k.final,CD4 > 0 ), "CD4",adjust = .2)+ theme_bw()

Seurat小提琴图为什么有的只有点儿?那是因为还有更多的点没忽视。

(0)

相关推荐

  • cowplot

    cowplot提供了plot_grid()函数用于组合图形: plot_grid(plot.mpg, plot.diamonds, labels = c("A", "B& ...

  • 成功解决AttributeError: ‘JointGrid‘ object has no attribute ‘annotate‘

    成功解决AttributeError: 'JointGrid' object has no attribute 'annotate' 解决问题 Traceback (most recent call ...

  • R绘图笔记 | 小提琴图与漂亮的云雨图绘制

    关于绘图图,前面介绍了一些: R绘图笔记 | 一般的散点图绘制 R绘图笔记 | 柱状图绘制 R绘图笔记 | 直方图和核密度估计图的绘制 R绘图笔记 | 二维散点图与统计直方图组合 R绘图笔记 | 散点 ...

  • R绘图:ggeconodist,基于ggplot2的另类箱图

    R绘图系列回顾 R语言学习系列之"多变的热图" 蚂蚁金服在线可视化引擎 G2 R绘图:无与伦比的华丽风行(桑基图) R绘图:相关性分析与作图(单基因相关性) R绘图:相关性分析与作 ...

  • cowplot-组图

    欢迎来到医科研,这里是白介素2的读书笔记,跟我一起聊临床与科研的故事, 生物医学数据挖掘,R语言,TCGA.GEO数据挖掘. cowplot包 cowplot包是ggplot2的简单附加组件.它旨在为 ...

  • ggplot2实现分半小提琴图绘制基因表达谱和免疫得分

    最近看到很多人问下面这个图怎么绘制,看着确实不错.于是我查了一些资料,这个图叫split violin或者half violin,本质上是一种小提琴图.参考代码在https://gist.github ...

  • R语言简单绘制小提琴图

    原始数据: R语言脚本: install.packages("vioplot")#安装vioplot包 library("vioplot") #安装和载入vio ...

  • R|散点图+边际图(柱形图,小提琴图),颜值区UP

    散点图作为一种展示2组连续变量关系的常用可视化方式之一,添加点,线,箭头,线段,注释,甚至函数,公式,方差表都没有问题. ggplot2-annotation|画图点"精",让图自 ...

  • PowerBI中的小提琴图:比盒须图信息更丰富

    之前的文章中介绍过盒须图(三分钟掌握盒须图,轻松了解数据分布),可以在有限的空间内展示丰富信息,今天再来认识一个比它更丰富的图表:小提琴图(Violin Plot). 先来看看小提琴图长什么样, 这个 ...

  • 单细胞小提琴图自己画

    小提琴图在单细胞领域应用非常广泛,能比较好的展现具体的某个基因在不同的单细胞亚群的表达量高低分布情况,如下: 每个细胞亚群都有表达 这个图说明了这个Igkc基因,在基本上每个细胞亚群都有表达,其中在编 ...

  • Graphpad Prism 8.0进阶篇-绘制小提琴图

    下载了GraphPad Prism 8后,迫不及待的体验了一下小提琴图.之前使用传统的柱状图遭遇过的坑,这次一定要一个个爬出来. 以前作图是这样的,这样的(小编都怀疑人生) 再此用了一个动画来展示这个 ...

  • 手把手演示R语言绘制多个基因表达值小提琴图

    白介素2的读书笔记,分享临床科研干货,一起见证时间的力量 [科研绘图点我][付费精品合集][SEER点我] image.png 数据准备 #set your work directory data&l ...

  • 箱线图和小提琴图合在一起更好

    Boxplot 一般我们的箱线图就是这样 # Libraries library(tidyverse) library(hrbrthemes) library(viridis) # create a ...