aplot包:让你画出更复杂的图
ggtree
的开发催生了好几个包,包括tidytree
, treeio
等一系列之外,还有诸如图上嵌图的ggimage
,当时一开始也是为了能够在进化树上加物种图片而开发的,还包括今天要介绍的aplot
包。这个包的关键在于能够对齐图之间的坐标轴,这本来也是进化树与热图、柱状图之类的对齐的需求,而最早开发了xlim2()
和ylim2()
两个函数,在开发ggtree
的过程中,我时常会想能不能让ggtree
的东西尽量能够用于ggplot2
,后来由此就又衍生出了aplot
包,最早见于《拼图?我掐指一算,发现事情没那么简单!》这一篇文章。
aplot初体验
其实像complexHeatmap
之类复杂的图,无非就是在拼图。只要坐标能对齐,各种相关信息就可以在主图的上下左右展示了。
所以我在aplot
里,提供了insert_top
, insert_bottom
, insert_left
和insert_right
四个函数,以一个图为主图,然后在周围加注释图。
library(ggplot2)
library(aplot)
p <- ggplot(mtcars, aes(mpg, disp)) + geom_point()
p2 <- ggplot(mtcars, aes(mpg)) +
geom_density(fill='steelblue', alpha=.5) +
ggtree::theme_dendrogram()
p3 <- ggplot(mtcars, aes(x=1, y=disp)) +
geom_boxplot(fill='firebrick', alpha=.5) +
theme_void()
假如有以上三张图,图一为散点图,图二为密度图,图三是数据分布的箱式图。
ap <- p %>%
insert_top(p2, height=.3) %>%
insert_right(p3, width=.1)
我们以第一张图为主图,在上面插入第二张图,在右边插入第三张图。
然后我们在R命令行里打ap
或者print(ap)
,图就画出来如下:
如果你想保存这张图到文件,ggsave()
也是支持的。使用你熟悉的语句,搞定。
ggsave(filename="aplot.png", plot=ap)
和树关联?
假如进化树你用得不多,那层次聚类你肯定是躲不过的,而类本身就是基于数据来聚的,也就是你天然会把聚类和原始数据(或相关数据,或衍生数据)放在一起出图。所以这很重要。假设有这么两张图,一张是树,一张是数据,用以前的方法,拼在一起会是怎么样?
library(ggtree)
set.seed(2020-03-27)
x <- rtree(10)
d <- data.frame(taxa=x$tip.label, value = abs(rnorm(10)))
p <- ggtree(x) + geom_tiplab(align = TRUE) + xlim(NA, 3)
library(ggstance)
p2 <- ggplot(d, aes(value, taxa)) + geom_colh() +
scale_x_continuous(expand=c(0,0))
library(patchwork)
p | p2
出图如下:
这显然不是我们想要的,因为数据和树是不对应的,这也就是为什么大家不用ggplot2
去画带聚类结果的热图。第一热图你会用ggplot2
画,第二层次聚类结果你可能也会用ggplot2
来画,但你没法拼在一起,因为你必须把你的热图按照层次聚类结果来重排重画。这不是一般人能干得出来的。
来自aplot的呼唤
还是上面两张图:
p2 %>% insert_left(p)
一条语句把树放到柱状图的左边,结果出图如下:
见证奇迹的时候到了,这个柱状图被重画了,它的顺序和树完美对应了。
一个单细胞例子的演示
例子来自于:https://davemcg.github.io/post/lets-plot-scrna-dotplots/,数据被打包到aplot
包里。
例子有点长,因为要画好几张图。
library(readr)
library(tidyr)
library(dplyr)
library(ggplot2)
library(ggtree)
file <- system.file("extdata", "scRNA_dotplot_data.tsv.gz", package="aplot")
gene_cluster <- readr::read_tsv(file)
dot_plot <- gene_cluster %>%
mutate(`% Expressing` = (cell_exp_ct/cell_ct) * 100) %>%
filter(count > 0, `% Expressing` > 1) %>%
ggplot(aes(x=cluster, y = Gene, color = count, size = `% Expressing`)) +
geom_point() +
cowplot::theme_cowplot() +
theme(axis.line = element_blank()) +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1)) +
ylab(NULL) +
theme(axis.ticks = element_blank()) +
scale_color_gradientn(colours = viridis::viridis(20), limits = c(0,4), oob = scales::squish, name = 'log2 (count + 1)') +
scale_y_discrete(position = "right")
mat <- gene_cluster %>%
select(-cell_ct, -cell_exp_ct, -Group) %>% # drop unused columns to faciliate widening
pivot_wider(names_from = cluster, values_from = count) %>%
data.frame() # make df as tibbles -> matrix annoying
row.names(mat) <- mat$Gene # put gene in `row`
mat <- mat[,-1] #drop gene column as now in rows
clust <- hclust(dist(mat %>% as.matrix())) # hclust with distance matrix
ggtree_plot <- ggtree::ggtree(clust)
v_clust <- hclust(dist(mat %>% as.matrix() %>% t()))
ggtree_plot_col <- ggtree(v_clust) + layout_dendrogram()
labels= ggplot(gene_cluster, aes(cluster, y=1, fill=Group)) + geom_tile() +
scale_fill_brewer(palette = 'Set1',name="Cell Type") +
theme_void()
画好了这几张图之后,我们画出来看看。
library(patchwork)
ggtree_plot | dot_plot | (ggtree_plot_col / labels)
这里大家可以看到dot_plot
所画的点图,根本就是杂乱的,而我们画的时候,也是没有聚类的,就按照矩阵,随便画画。眼尖的你可能还发现细胞类型的条型,也是乱的。
然后我们用aplot
来拼:
dot_plot %>%
insert_left(ggtree_plot, width=.2) %>%
insert_top(labels, height=.02) %>%
insert_top(ggtree_plot_col, height=.1)
就这么简单的代码,奇迹再次出现,点图被重排了,不管是行还是列,都按照聚类的结果重新排好。细胞类型也被重排了,一切都如同许愿般的美好。出图如下:
一点小插曲
有给我这个公众号投过稿的小伙伴,我会拉入一个biobabble作者群
里,基本上我要是写新包的话,总会先在群里讲一下。后来发现群里一个小伙伴,总是在抄我的代码,关键是没跟我说,也没致谢。发现之后,我跟他说,你这么个抄法没意思啊。他回我一句:”我是临摹着写“。意思是看我的代码,写他自己的呗。我没再说什么,半夜心有不平,在手机上发了一条公众号图片:
然后放了很久,没有任何动静,那怕在代码里注释写一下,受我代码的激发之类的,都让我好受一点点。完全抄完就是他的。本着对年轻人nice一点,把这事放一段时间,在观察到没有动静之后,我把他T出了群。想想之前一直要套我一些实现细节以及经常在群里说要当我门外弟子这些话,感觉还是挺可笑的。
本来我写到上面就完了,懒得爆光他,徒增无谓的口舌而已。奈何我晚上失眠,翻看了一下代码,翻出了端倪。原来是到处抄之集大成者,简直是毒瘤,还是爆光一下吧。
往期精彩