如何标注部分标签避免重叠并优化优雅的写成 grob?

1引言

在绘制分类数据较多的时候,不可避免的会出现大量重叠的样本名称,一个常见的情况就是绘制基因表达热图的时候基因多了,标记基因名就会重叠看不清,此外如果标记一部分基因名, 如果它们靠的太近,也会出现重叠看不清的情况一个好的解决方法是充分利用剩余的空间来将这些标签放置分配好。比较好的例子就是 ComplexHeatmap 包里的 anno_mark 函数可以指定标出部分基因。下面即示例:

图片

那么如何去自己手动写一个自动绘制标签位置的小工具,方便可以应用在不同的场景呢?我们可以写一个 grob, 关于如何计算调整后的标签的位置, 一个简单的思路就是把空白的绘图区域等份的给每个标签的位置,然后用线将原来的位置的新的位置连接起来即可

当然你也可以使用 ComplexHeatmap 包里的方法, 该包提供了 smartAlign2 函数,来通过原始位置计算调整后的位置,下面看看示例:

library(ComplexHeatmap)

range = c(0, 10)
pos1 = rbind(c(-0.5, 2), c(5, 7))
smartAlign2(pos1, range = range, plot = TRUE)

图片

好了,我们可以先按自己的思路编写代码,然后加入 smartAlign2 选项就比较完美了,然后再增强一下可视化。

2参数

经过不断地 debug,测试,终于完成的差不多了,下面是一些参数:smartLabelAlignGrob <- function(all.label = NULL,
                                mark.label = NULL,
                                x = 0.5,y = 0.5,
                                use.smartAlign2 = FALSE,
                                link.line.length = 0.025,
                                link.label.space = 0.02,
                                mark.scale = c(0.01,0.99),
                                link.label.gp = gpar(fontsize = 10),
                                link.line.gp = gpar(fill = "black",col = "black"),
                                link.circle.start.gp = gpar(fill = "black",col = "black"),
                                link.circle.end.gp = gpar(fill = "black",col = "black"),
                                link.start.type = c("line","circle","arrow"),
                                link.end.type = c("line","circle","arrow"),
                                circle.arrow.size = c(0.01,0.01),
                                pos = c("right","left","top","bottom"),
                                name = NULL,
                                gp = NULL, vp = NULL){

  lst <- list(all.label = all.label,
              mark.label = mark.label,
              x = x,y = y,
              use.smartAlign2 = use.smartAlign2,
              link.line.length = link.line.length,
              link.label.space = link.label.space,
              mark.scale = mark.scale,
              link.label.gp = link.label.gp,
              link.line.gp = link.line.gp,
              link.circle.start.gp = link.circle.start.gp,
              link.circle.end.gp = link.circle.end.gp,
              link.start.type = link.start.type,
              link.end.type = link.end.type,
              circle.arrow.size = circle.arrow.size,
              pos = pos,
              name = name, gp = gp, vp = vp,
              cl = "smartLabelAlignGrob")

  do.call(gTree,lst)
}

3测试

位置标签均匀分布

假如我们画 50 个标签,想要标出其中一些, 你需要提供 所有的标签名称 和 需要标记的标签名称:library(grid)
library(tidyverse)

data(USArrests)
all.label <- rownames(USArrests)
mark.label <- sample(all.label,15,replace = F)


pagelabel <- function(hjust = 1,x = 0.48){
  grid.newpage()
  pushViewport(viewport(width = 0.5,height = 0.9,yscale = c(0.5,50.5)))
  grid.text(x = x,y = unit(1:50,"native"),
            label = all.label,
            hjust = hjust)
}

pagelabel()
grid.draw(smartLabelAlignGrob(all.label = all.label,
                              mark.label = mark.label))

图片

设置起始和终止连接的形状:pagelabel()
grid.draw(smartLabelAlignGrob(all.label = all.label,
                    mark.label = mark.label,
                    link.start.type = "circle",
                    link.end.type = "arrow"))

图片

pagelabel()
grid.draw(smartLabelAlignGrob(all.label = all.label,
                    mark.label = mark.label,
                    link.start.type = "arrow",
                    link.end.type = "arrow"))

图片

pagelabel()
grid.draw(smartLabelAlignGrob(all.label = all.label,
                    mark.label = mark.label,
                    link.start.type = "circle",
                    link.end.type = "circle"))

图片

设置一些颜色,大小:

pagelabel()
grid.draw(smartLabelAlignGrob(all.label = all.label,
                    mark.label = mark.label,
                    link.start.type = "circle",
                    link.end.type = "circle",
                    circle.arrow.size = c(0.01,0.03),
                    link.circle.end.gp = gpar(fill = rainbow(15)),
                    link.circle.start.gp = gpar(fill = rainbow(15)),
                    link.label.gp = gpar(fontface = "bold.italic")))

图片

smartAlign2 调用

设置 use.smartAlign2 = T 即可转换为 smartAlign2 计算的位置:pagelabel()
grid.draw(smartLabelAlignGrob(all.label = all.label,
                    mark.label = mark.label,
                    use.smartAlign2 = T))

图片

其它设置也可以:pagelabel()
grid.draw(smartLabelAlignGrob(all.label = all.label,
                    mark.label = mark.label,
                    link.start.type = "circle",
                    link.end.type = "circle",
                    circle.arrow.size = c(0.01,0.03),
                    link.circle.end.gp = gpar(fill = rainbow(15)),
                    link.label.gp = gpar(fontface = "bold.italic"),
                    use.smartAlign2 = T))

图片

调整方向

当然你也可以调整方向:pagelabel(hjust = 0,x = 0.52)
grid.draw(smartLabelAlignGrob(all.label = all.label,
                    mark.label = mark.label,
                    pos = "left"))

图片

pagelabel(hjust = 0,x = 0.52)
grid.draw(smartLabelAlignGrob(all.label = all.label,
                    mark.label = mark.label,
                    pos = "left",
                    link.start.type = "circle",
                    link.end.type = "circle",
                    circle.arrow.size = c(0.01,0.03),
                    link.circle.end.gp = gpar(fill = rainbow(15)),
                    link.label.gp = gpar(fontface = "bold.italic")))

图片

上下方向:pagelabel <- function(hjust = 1,y = 0.48){
  grid.newpage()
  pushViewport(viewport(width = 0.9,height = 0.9,xscale = c(0.5,50.5)))
  grid.text(x = unit(1:50,"native"),y = y,
            label = all.label,
            hjust = hjust,
            rot = 90)
}

pagelabel()
grid.draw(smartLabelAlignGrob(all.label = all.label,
                    mark.label = mark.label,
                    pos = "top"))

图片

pagelabel()
grid.draw(smartLabelAlignGrob(all.label = all.label,
                    mark.label = mark.label,
                    pos = "top",
                    link.start.type = "circle",
                    link.end.type = "circle"))

图片

pagelabel(hjust = 0,y = 0.52)
grid.draw(smartLabelAlignGrob(all.label = all.label,
                    mark.label = mark.label,
                    pos = "bottom"))

图片

pagelabel(hjust = 0,y = 0.52)
grid.draw(smartLabelAlignGrob(all.label = all.label,
                    mark.label = mark.label,
                    pos = "bottom",
                    link.start.type = "circle",
                    link.end.type = "circle"))

图片

pagelabel(hjust = 0,y = 0.52)
grid.draw(smartLabelAlignGrob(all.label = all.label,
                    mark.label = mark.label,
                    pos = "bottom",
                    link.start.type = "circle",
                    link.end.type = "circle",
                    circle.arrow.size = c(0.01,0.03),
                    link.circle.end.gp = gpar(fill = rainbow(15)),
                    link.label.gp = gpar(fontface = "bold.italic")))

图片

调用 smartAlign2 :pagelabel(hjust = 0,y = 0.52)
grid.draw(smartLabelAlignGrob(all.label = all.label,
                    mark.label = mark.label,
                    pos = "bottom",
                    link.start.type = "circle",
                    link.end.type = "circle",
                    circle.arrow.size = c(0.01,0.025),
                    link.circle.end.gp = gpar(fill = rainbow(15)),
                    link.label.gp = gpar(fontface = "bold.italic"),
                    use.smartAlign2 = T))

图片

4应用

既然是 grob 就可以应用在 ggplot 上面, 我们可以使用 annotation_custom 来插入到图里。下面举个例子, 顺便使用 ggh4x 来给热图添加聚类树, 然后你需要获取聚类后所有标签的顺序才好正确的注释:

# ==============================================================================
library(ggplot2)
library(ggh4x)

# get long data
scale.data <- t(scale(t(USArrests)))
df.long <- reshape2::melt(scale.data)

clusters.y <- hclust(dist(USArrests), "ave")

# ==============================================================================
# create anno grobs
all.label <- rownames(USArrests)[clusters.y$order]
mark.label <- sample(all.label,15,replace = F)

anno.grob <- smartLabelAlignGrob(all.label = all.label,
                                 mark.label = mark.label,
                                 pos = "right",
                                 link.start.type = "circle",
                                 link.end.type = "circle",
                                 circle.arrow.size = c(0.01,0.025),
                                 link.circle.end.gp = gpar(fill = rainbow(15)),
                                 link.label.gp = gpar(fontface = "bold.italic"))

然后画图,设置一下 clip = “off” 关闭剪裁选项:ggplot(df.long) +
  geom_tile(aes(x = Var2,y = Var1,fill = value)) +
  theme_bw() + xlab("") + ylab("") +
  scale_fill_gradient2(low = "blue",mid = "white",high = "red",midpoint = 0) +
  scale_y_dendrogram(hclust = clusters.y) +
  theme(legend.position = "top",
        plot.margin = margin(r = 5,unit = "cm")) +
  coord_cartesian(clip = "off") +
  annotation_custom(grob = anno.grob,
                    xmin = 1,
                    xmax = 8.5)

图片

可以看到注释的位置是正确的,使用 smartAlign2 看看,就不展示代码了:

图片

5结尾

路漫漫其修远兮,吾将上下而求索。


欢迎加入生信交流群。加我微信我也拉你进 微信群聊 老俊俊生信交流群 (微信交流群需收取 20 元入群费用,一旦交费,拒不退还!(防止骗子和便于管理)) 。QQ 群可免费加入, 记得进群按格式修改备注哦。

声明:文中观点不代表本站立场。本文传送门:https://eyangzhen.com/372396.html

(0)
联系我们
联系我们
分享本页
返回顶部