ggplot2:如何动态包装/调整/重新缩放X轴标签,以避免重叠。

3
我正在尝试解决x轴标签重叠的问题,希望能实现自动换行。我知道这个问题已经被问了很多次,也有一些好的答案。然而,我没有看到任何一个解决方案可以在图形被调整大小时重新包装标签。
SO上的三个不同答案让我相信这是可行的。
  1. 这个解决方案编写了一个定制的geom,用于将bar的标签size适应于条形图的宽度,当您调整绘图大小时可以动态调整。

  2. 这个解决方案依赖于一个名为ggtextggplot2扩展包。该解决方案允许对绘图的title进行动态换行,当您调整绘图大小时,基于创建一个element_textbox()实现。

  3. 这个解决方案依赖于另一个名为ggfittext的扩展。它展示了如何动态调整标签内部的size以适应条形图的尺寸,当您调整绘图大小时。本质上,它解决了与上述解决方案(1)相同的问题,但更加强大。事实上,这是让我感到有希望的特性,它依赖于一个通用解决方案geom_fit_text()来适应矩形内的文本,而不仅仅是geom_bar()

一些演示数据以供使用

1. 仅为展示当 x 轴标签重叠时的典型输出而设置。

  library(tidyverse)
  
  my_mtcars <-
    mtcars[15:20,] %>% 
    rownames_to_column("cars")
  
  my_mtcars %>%
    ggplot(aes(x = cars, y = mpg, fill = cars)) + 
    geom_bar(stat = "identity")

reprex package(v0.3.0)于2021-01-29创建


当我们使用 ggfittext 时,可以看到标签在柱形内部缩小以适应柱形的大小。
  library(tidyverse)
  library(ggfittext)
#> Warning: package 'ggfittext' was built under R version 4.0.3
  
  my_mtcars <-
    mtcars[15:20,] %>% 
    rownames_to_column("cars")
  
  my_mtcars %>%
    ggplot(aes(x = cars, y = mpg, fill = cars)) + 
    geom_bar(stat = "identity") +
    geom_bar_text(aes(label = cars), 
      color = "blue", 
      vjust = 1, 
      size = 7 * ggplot2::.pt, 
      min.size = 0,
      padding.x = grid::unit(0, "pt"),
      padding.y = grid::unit(0, "pt"))
#> Warning: Ignoring unknown aesthetics: label

这段内容是由reprex package (v0.3.0)于2021-01-29创建的。


3. ggfittext具有reflow参数,可以促进文本换行

  library(tidyverse)
  library(ggfittext)
#> Warning: package 'ggfittext' was built under R version 4.0.3
  
  my_mtcars <-
    mtcars[15:20,] %>% 
    rownames_to_column("cars")
  
  my_mtcars %>%
    ggplot(aes(x = cars, y = mpg, fill = cars)) + 
    geom_bar(stat = "identity") +
    geom_bar_text(aes(label = cars), 
      color = "blue", 
      vjust = 1, 
      size = 7 * ggplot2::.pt, 
      min.size = 0,
      padding.x = grid::unit(0, "pt"),
      padding.y = grid::unit(0, "pt"),
      reflow = TRUE ## <--------------- added this
      )
#> Warning: Ignoring unknown aesthetics: label

这段内容是由reprex package (v0.3.0)在2021年01月29日创建的。


我的问题

我不知道如何做到这一点,但我们是否可以让ggfittext为我们做一些艰苦的工作,动态地包装/调整/重新缩放x轴标签?在我看来,这种方式很天真,柱形图中的文本已经以正确的方式呈现,我们是否可以以某种方式“复制”这种呈现方式到轴标签中?


可能与ggfittext的github上的问题相关:https://github.com/wilkox/ggfittext/issues/14 - teunbrand
2个回答

3
我们是否可以将ggfittext文本放置在y轴下方?我们关闭剪切并设置ooblimits以适应我们的数据。可能需要调整axis.text.x的大小,以更好地与x轴标题对齐。
library(tidyverse)
#> Warning: package 'tidyr' was built under R version 4.0.3
#> Warning: package 'readr' was built under R version 4.0.3
#> Warning: package 'dplyr' was built under R version 4.0.3
library(ggfittext)
#> Warning: package 'ggfittext' was built under R version 4.0.3

my_mtcars <-
  mtcars[15:20,] %>% 
  rownames_to_column("cars")

my_mtcars %>%
  ggplot(aes(x = cars, y = mpg, fill = cars)) + 
  geom_bar(stat = "identity") +
  geom_fit_text(aes(label = cars, y = -4),
                reflow = TRUE, height = 50,
                show.legend = FALSE) +
  scale_y_continuous(oob = scales::oob_keep,
                     limits = c(0, NA)) +
  coord_cartesian(clip = "off") +
  theme(axis.text.x = element_text(colour = "transparent", size = 18))

这段内容是关于编程的,创建于2021年01月29日,使用了reprex package (v0.3.0)

编辑:从grob中获取标签

library(tidyverse)
library(ggfittext)

my_mtcars <-
  mtcars[15:20,] %>% 
  rownames_to_column("cars")

p <- my_mtcars %>%
  ggplot(aes(x = cars, y = mpg, fill = cars)) + 
  geom_bar(stat = "identity") +
  geom_fit_text(aes(label = cars, y = -1),
                reflow = TRUE, height = 50,
                show.legend = FALSE) +
  scale_y_continuous(oob = scales::oob_keep,
                     limits = c(0, NA)) +
  coord_cartesian(clip = "off") +
  theme(axis.text.x = element_text(colour = "transparent", size = 18))

grob <- grid::makeContent(layer_grob(p, 2)[[1]])$children

sizes <- vapply(grob, function(x){x$gp$fontsize}, numeric(1))
labels <- unname(vapply(grob, function(x){x$label}, character(1)))
print(labels)
#> [1] "Cadillac\nFleetwood"  "Lincoln\nContinental" "Chrysler\nImperial"  
#> [4] "Fiat 128"             "Honda Civic"          "Toyota\nCorolla"

本文于2021年01月29日由reprex包(v0.3.0)创建


这真的很好,谢谢。然而,y = -4 表示一种限制。虽然 -4 对于此图表有效,但不同的图表可能具有任何 y 轴比例,而 -4 将无法使用。是否有一种方法可以独立设置垂直位置而不受 y 值的影响? - Emman
我有另一个想法......是否可能黑客渲染标签,提取单词换行(例如,“Fiat 128”,“Honda\nCivic”)和文本大小,然后将其传递给scale_x_discrete() - Emman
我喜欢这个想法,但限制在于 ggfittext 直到实际渲染时间和所有绝对尺寸都已知才会被呈现。你需要以某种方式拦截它们的 "makeContent.fittexttree" 方法,你可以通过调试器来完成,但这比手动设置“y”位置还不够通用。而且,如果你调整设备窗口的大小,那么你拦截的值就不再是正确的了。 - teunbrand
如果只是想要换行文本,你可以尝试使用stringr::str_wrap(),这似乎比深入图形设备要容易得多。 - teunbrand
当然这样做会简单得多,但是我该如何将str_wrap()的“宽度”(以字符数定义)与不断变化的柱形图“宽度”相匹配呢?适合给定柱形图宽度的字符数取决于字体大小。我不知道如何去计算这个,特别是考虑到柱形图宽度因数据而异,从一个绘图到另一个绘图也会改变。还要考虑可能需要使用facet_grid()等情况。这就是为什么我希望依靠其他函数来完成这项工作...... ggfittext似乎很适合这个任务。 - Emman
显示剩余11条评论

-1
如何仅更改文本的角度或大小?
角度:
my_mtcars %>%
  ggplot(aes(x = cars, y = mpg, fill = cars)) + 
  geom_bar(stat = "identity")+
  theme(axis.text.x = element_text(angle = 45, vjust = 0.5, hjust=1))

大小:

my_mtcars %>%
  ggplot(aes(x = cars, y = mpg, fill = cars)) + 
  geom_bar(stat = "identity")+
  theme(axis.text.x = element_text(size = 4)) 

当然我们可以,我也知道这样的解决方案。我在帖子一开始就提到了这个概述来表明我已经意识到了这一点。但我的问题是不同的。 - Emman
啊,抱歉,我没有进入参考资料。 - Paul Tansley

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