使用ggrepel覆盖水平定位 [英] Override horizontal positioning with ggrepel

查看:163
本文介绍了使用ggrepel覆盖水平定位的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm working on a chart similar to a slopegraph, where I'd like to put labels along one or both sides with ample blank space to fit them on both sides. In cases where labels are very long, I've wrapped them using stringr::str_wrap to place linebreaks. To keep labels from overlapping, I'm using ggrepel::geom_text_repel with direction = "y" so the x-positions are stable but the y-positions are repelled away from one another. I've also got hjust = "outward" to align the left-side text at its right end and vice versa.

但是,似乎排斥位置将标签的边界框放置为hjust = "outward",但是该标签具有hjust = 0.5的文本之内,即文本在其边界内居中.到现在为止,我还没有注意到这一点,但是在包装标签的情况下,第二行居中居中,而我希望这两行都左对齐或右对齐.

However, it seems that the repel positioning places the label's bounding box with an hjust = "outward", but the text within that label has hjust = 0.5, i.e. text is centered within its bounds. Until now, I'd never noticed this, but with wrapped labels, the second line is awkwardly centered, whereas I'd expect to see both lines left-aligned or right-aligned.

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

Here's an example built off the mpg dataset.

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的其他值也会发生相同的情况.查看该函数的,我看到一条线指出了这个问题:

The same thing happens with other values of hjust. Looking at the function's source, I see a line that points to this issue:

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

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

where %||% assigns 0.5 if x$data$hjust is null. That's as far as I understand, but it seems that the hjust I've set isn't being carried over to this positioning and is instead coming up null.

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

Have I missed something? Can anyone see where I might override this without reimplementing the whole algorithm? Or is there a bug here that drops my hjust?

推荐答案

TL; DR:可能是错误

TL;DR: probably a bug

长答案:

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

I think it might be a bug in the code. I checked the gtable of the plot you made, wherein the hjust was specified numerically and correctly:

# 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().让我们看一下相关的形式:

Which got me thinking that (1) the plot can't be fixed by messing around in the gtable and (2) the drawtime code for the textrepeltree class of grobs may contain some errors. This makes sense, since the labels are repositioned when the plot device is resized. So when we look at the makeContent.textrepeltree() code in the link you provided, we can see that the hjust parameter is passed on to makeTextRepelGrobs(). Let's have a look at the relevant formals:

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

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

We can see that hjust is a valid argument, but there also exists a just argument, which is an argument that is not passed on from makeContent.textrepeltree().

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

When we look at the function body there are these two lines:

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

其中resolveH/VJust是从网格包中导入的. resolveHJust()本质上检查第二个参数是否为NULL,如果为true,则默认为第一个参数,否则返回第二个参数.您会看到传递给makeTextRepelGrobs()hjust未被传递给resolveHJust(),这似乎是hjust参数意外删除的地方.

Where resolveH/VJust are imported from the grid package. The resolveHJust() essentially checks whether the second argument is NULL and if that is true, default to the first argument, otherwise return the second argument. You can see that the hjust that was passed on to makeTextRepelGrobs() does not get passed to resolveHJust(), and this seems to be the point where your hjust parameter is dropped unexpectedly.

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

Further down the code is where the actual text grobs are made:

  t <- textGrob(
    ...other_arguments...
    just = c(hj, vj),
    ...other_arguments...
  )

我认为解决方法相对简单:您只需提供hjust作为resolveHJust()的第二个参数即可.但是,由于makeTextRepelGrobs()是ggrepel的内部文件,并且不会导出,因此您必须复制很多额外的代码才能使它工作. (不确定是否仅复制makeTextRepelGrob()就足够了,还没有进行测试)

I imagine that the fix would be relatively straightforward: you would just have to supply hjust as the second argument to resolveHJust(). However, since that makeTextRepelGrobs() is internal to ggrepel and does not get exported, you would have to copy a lot of extra code to get this to work. (Not sure if only copying the makeTextRepelGrob() would be sufficient, haven't tested this)

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

All of this leaves me to conclude that the hjust that you specified in geom_text_repel() gets lost at the last moment of drawtime by the makeTextRepelGrobs() internal function.

这篇关于使用ggrepel覆盖水平定位的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆