如何访问`ggplot2`中使用`geom_text`绘制标签的尺寸?

9
据我所知,ggplot2通过geom_text绘制的标签的尺寸是已知的。否则,check_overlap选项将无法工作。 这些尺寸存储在哪里,我如何访问它们?
说明性示例
library(ggplot2)
df <- data.frame(x = c(1, 2), 
                 y = c(1, 1), 
                 label = c("label-one-that-might-overlap-another-label", 
                           "label-two-that-might-overlap-another-label"), 
                 stringsAsFactors = FALSE)

使用check_overlap = FALSE(默认值),标签会重叠。

ggplot(df, aes(x, y)) + 
  geom_text(aes(label = label)) + 
  xlim(0, 3)                              

enter image description here

使用check_overlap = TRUE,第二个标签不会被绘制,因为ggplot发现了重叠。
ggplot(df, aes(x, y)) + 
  geom_text(aes(label = label), check_overlap = TRUE) + 
  xlim(0, 3)

enter image description here

ggplot2 如何知道这些标签重叠?我如何访问该信息?


基于geom_text的底层代码在此和先前在该软件包的GH上的讨论在此check_overlap参数被传递给grid::textGrob,因此我怀疑您无法在ggplot2中找到任何内容... - Z.Lin
@Z.Lin:感谢您的评论。 您有没有想法或经验,可以设计和实现自定义的grob,并了解如何在当前的geom_text实现中通过它传递。 当我查看grid :: textGrob源代码时,我不理解它。 - symbolrush
很抱歉,我不行。我偶尔会玩一下ggplot对象,但grid就是完全不同的故事了。你实际的使用场景是什么?也许可以找到一个非grid的解决方案。 - Z.Lin
@Z.Lin 我的实际用例在这里中描述,第一个可能的解决方法在这里 - symbolrush
将箭头放在线的中间会有帮助吗?这样做可能更容易实现。类似于最近的这个问题,也许可以这样做? - Z.Lin
@Z.Lin:这可能是一个有价值的解决方法。感谢您提供的链接和想法。 - symbolrush
2个回答

3

在grid包中的文本在绘制之前并没有确定的大小。下面,我们将创建一个帮助函数来测量文本的大小,但是如果您事先不知道绘图区域的设备和大小,这样做实际上并没有意义。(对于那些了解的人,在绘制的makeContent()阶段)。

library(grid)

label <- c("label-one-that-might-overlap-another-label", 
           "label-two-that-might-overlap-another-label")

measure_size <- function(txt, gp = gpar(), to = "mm") {
  if (is.grob(txt)) {
    grobs <- lapply(seq_along(txt$label), function(i) {
      g <- txt
      # Subset grob per label
      g$label <- g$label[[i]]
      g$gp[]  <- lapply(g$gp, function(x) {x[pmin(i, length(x))]})
      g$rot   <- g$rot[pmin(i, length(g$rot))]
      g
    })
  } else {
    grobs <- lapply(txt, function(t) textGrob(t, gp = gp))
  }
  
  heights <- do.call(unit.c, lapply(grobs, grobHeight))
  widths  <- do.call(unit.c, lapply(grobs, grobWidth))
  
  cbind(
    height = convertHeight(heights, to, valueOnly = TRUE),
    weight = convertWidth(widths,   to, valueOnly = TRUE)
  )
}

我们现在可以尽最大努力猜测文本的大小,但正如人们所期望的那样,文本的实际大小很大程度上取决于其图形参数。例如请注意,更改字体也会改变文本的大小。
measure_size(label)
#>      height   weight
#> [1,]  3.175 79.13109
#> [2,]  3.175 78.65566

measure_size(label, gp = gpar(fontfamily = "Garamond"))
#>        height   weight
#> [1,] 2.645833 69.67223
#> [2,] 2.645833 69.69704

现在将同样的技巧应用到 ggplot2 的文本层。

library(ggplot2)
#> Warning: package 'ggplot2' was built under R version 4.1.1

df <- data.frame(x = c(1, 2), 
                 y = c(1, 1), 
                 label = label)

p <- ggplot(df, aes(x, y)) + 
  geom_text(aes(label = label)) + 
  xlim(0, 3) 

textgrob <- layer_grob(p)[[1]]
measure_size(textgrob)
#>        height   weight
#> [1,] 2.645979 72.83233
#> [2,] 2.645979 72.39411

本内容由 reprex软件包 (v2.0.1) 于2021-12-13创建

最近我自己做了很多文本方面的工作,发现{systemfonts}/{textshaping}软件包能够精确地返回文本在像素上的大小,这当然取决于设备/分辨率。

systemfonts::string_width("My label")
#> [1] 46
textshaping::text_width("My label")
#> [1] 46

-1

如果你只是想避免标签重叠,ggrepel包非常好用。

library(ggplot2)
library(ggrepel)
df <- data.frame(x = c(1, 2), 
                 y = c(1, 1), 
                 label = c("label-one-that-might-overlap-another-label", 
                           "label-two-that-might-overlap-another-label"), 
                 stringsAsFactors = FALSE)
ggplot(df, aes(x, y)) + 
  geom_text_repel(aes(label = label), check_overlap = F) + 
  xlim(0, 3) 

以上代码生成了如下图所示的图形。 在此输入图像描述

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接