使用ggrepel覆盖水平定位

时间:2019-05-07 19:03:21

标签: r ggplot2 ggrepel

我正在处理类似于坡度图的图表,在该图表上,我想在标签的一侧或两侧放置标签,并留出足够的空白空间以使其在两侧都适合。在标签很长的情况下,我使用stringr::str_wrap包裹了换行符。为了防止标签重叠,我将ggrepel::geom_text_repeldirection = "y"结合使用,因此x位置稳定,但y位置相互排斥。我还用hjust = "outward"将左侧文本的右端对齐,反之亦然。

但是,似乎排斥位置在标签的边界框上放置了hjust = "outward",但是标签内的文本 in 中有hjust = 0.5,即文本位于标签的中心界限。到现在为止,我还没有注意到这一点,但是在包裹标签的情况下,第二行居中居中,而我希望这两行都左对齐或右对齐。

这是一个基于mpg数据集的示例。

library(ggplot2)
library(dplyr)
library(ggrepel)

df <- structure(list(long_lbl = c("chevrolet, k1500 tahoe 4wd, auto(l4)", 
                                  "chevrolet, k1500 tahoe 4wd, auto(l4)", "subaru, forester awd, manual(m5)", 
                                  "subaru, forester awd, manual(m5)", "toyota, camry, manual(m5)", 
                                  "toyota, camry, manual(m5)", "toyota, toyota tacoma 4wd, manual(m5)", 
                                  "toyota, toyota tacoma 4wd, manual(m5)", "volkswagen, jetta, manual(m5)", 
                                  "volkswagen, jetta, manual(m5)"), year = c(1999L, 2008L, 1999L, 
                                                                             2008L, 1999L, 2008L, 1999L, 2008L, 1999L, 2008L), mean_cty = c(11, 
                                                                                                                                            14, 18, 20, 21, 21, 15, 17, 33, 21)), class = c("tbl_df", "tbl", 
                                                                                                                                                                                            "data.frame"), row.names = c(NA, -10L))

df_wrap <- df %>%  
  mutate(wrap_lbl = stringr::str_wrap(long_lbl, width = 25))

ggplot(df_wrap, aes(x = year, y = mean_cty, group = long_lbl)) +
  geom_line() +
  geom_text_repel(aes(label = wrap_lbl),
                  direction = "y", hjust = "outward", seed = 57, min.segment.length = 100) +
  scale_x_continuous(expand = expand_scale(add = 10))

hjust的其他值也会发生相同的情况。在查看函数的source时,我看到一行指出了该问题:

hjust = x$data$hjust %||% 0.5,

如果%||%为null,则x$data$hjust分配0.5。据我所知,但看来我设置的hjust并没有延续到这个位置,而是变成了空值。

我错过了什么吗?任何人都可以看到我可以在不重新实现整个算法的情况下覆盖它的地方吗?还是这里有一个错误使我的hjust掉了?

2 个答案:

答案 0 :(得分:6)

TL; DR:可能是一个错误

长答案:

我认为这可能是代码中的错误。我检查了所绘制图的gtable,其中hjust是通过数字正确指定的:

# Assume 'g' is the plot saved under the variable 'g'
gt <- ggplotGrob(g)
# Your number at the end of the geom may vary
textgrob <- gt$grobs[[6]]$children$geom_text_repel.textrepeltree.1578
head(textgrob$data$hjust)
[1] 1 0 1 0 1 0

这让我想到(1)无法通过在gtable中弄乱来固定绘图,以及(2)textrepeltree类grob的绘制时间代码可能包含一些错误。这是有道理的,因为在调整绘图设备的大小时会重新放置标签。因此,当我们查看您提供的链接中的makeContent.textrepeltree()代码时,我们可以看到hjust参数被传递给makeTextRepelGrobs()。让我们看一下相关的形式:

makeTextRepelGrobs <- function(
  ...other_arguments...,
  just = "center",
  ...other_arguments...,
  hjust = 0.5,
  vjust = 0.5
) { ...body...}

我们可以看到hjust是有效的参数,但是也存在一个just参数,该参数不是从makeContent.textrepeltree()传递过来的。

当我们查看函数体时,有以下两行:

  hj <- resolveHJust(just, NULL)
  vj <- resolveVJust(just, NULL)

从网格包中导入resolveH/VJust的位置。 resolveHJust()本质上检查第二个参数是否为NULL,如果为true,则默认为第一个参数,否则返回第二个参数。您可以看到传递给hjust的{​​{1}}没有传递给makeTextRepelGrobs(),这似乎是resolveHJust()参数被意外删除的地方。

接下来的代码是制作实际文本杂记的地方:

hjust

我认为解决方法相对简单:您只需提供 t <- textGrob( ...other_arguments... just = c(hj, vj), ...other_arguments... ) 作为hjust的第二个参数。但是,由于resolveHJust()是ggrepel的内部对象,并且不会导出,因此您必须复制很多额外的代码才能使它起作用。 (不确定是否仅复制makeTextRepelGrobs()就足够了,还没有进行测试)

所有这些让我得出结论,在makeTextRepelGrob()中指定的hjust在绘制时间的最后时刻被geom_text_repel()内部函数丢失了。

答案 1 :(得分:1)

更新(2019年12月12日):

仅供参考,现在在ggrepel的开发版本中已解决此问题,该修复程序也适用于geom_label_repel。请参阅GitHub上的issue #137

library(ggplot2)
library(dplyr)
devtools::install_github("slowkow/ggrepel")


df <- structure(list(long_lbl = c("chevrolet, k1500 tahoe 4wd, auto(l4)", 
                                  "chevrolet, k1500 tahoe 4wd, auto(l4)", "subaru, forester awd, manual(m5)", 
                                  "subaru, forester awd, manual(m5)", "toyota, camry, manual(m5)", 
                                  "toyota, camry, manual(m5)", "toyota, toyota tacoma 4wd, manual(m5)", 
                                  "toyota, toyota tacoma 4wd, manual(m5)", "volkswagen, jetta, manual(m5)", 
                                  "volkswagen, jetta, manual(m5)"), year = c(1999L, 2008L, 1999L, 
                                                                             2008L, 1999L, 2008L, 1999L, 2008L, 1999L, 2008L), mean_cty = c(11, 
                                                                                                                                            14, 18, 20, 21, 21, 15, 17, 33, 21)), class = c("tbl_df", "tbl", 
                                                                                                                                                                                            "data.frame"), row.names = c(NA, -10L))

df_wrap <- df %>%  
  mutate(wrap_lbl = stringr::str_wrap(long_lbl, width = 25))

# With geom_text_repel
ggplot(df_wrap, aes(x = year, y = mean_cty, group = long_lbl)) +
  geom_line() +
  geom_text_repel(aes(label = wrap_lbl),
                  hjust = "outward",
                  direction = "y",
                  seed = 57,
                  min.segment.length = 100) +
  scale_x_continuous(expand = expansion(add = 10))

# With geom_label_repel
ggplot(df_wrap, aes(x = year, y = mean_cty, group = long_lbl)) +
  geom_line() +
  geom_label_repel(aes(label = wrap_lbl),
                  hjust = "outward",
                  direction = "y",
                  seed = 57,
                  min.segment.length = 100) +
  scale_x_continuous(expand = expansion(add = 10))

enter image description here