ggplot2:geom_text调整大小,并在geom_bar中强制/拟合文本 [英] ggplot2: geom_text resize with the plot and force/fit text within geom_bar

查看:370
本文介绍了ggplot2:geom_text调整大小,并在geom_bar中强制/拟合文本的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这实际上是两个问题合二为一(不确定是否违反SO规则,但是无论如何).

This is actually two questions in one (not sure if goes against SO rules, but anyway).

第一个问题是如何强制geom_text放入geom_bar中? (动态地根据绘制的值)

First question is how can I force a geom_text to fit within a geom_bar? (dynamically according to the values plotted)

环顾四周,我发现的解决方案是更改标签的大小.这当然有效,但并非在所有情况下都适用.您可以更改特定图的大小以使文本适合条形图,但是当数据更改时,您可能需要再次手动更改文本的大小.我的现实生活中的问题是,我需要生成相同的图以不断变化的数据(每天),因此我无法真正手动调整每个图的大小.

Looking around, the solutions I found were changing the size of the label. That certainly works, but not for every case. You can change the size for a specific plot to make the text fit within the bar, but when the data changes, you may need to manually change the size of the text again. My real-life problem is that I need to generate the same plot for constantly changing data (daily), so I cannot really manually adjust the size for each plot.

我尝试根据数据设置标签的大小.这有点奏效,但不是很完美,但是在很多情况下都可以奏效.

I tried setting the size of the label as a function of the data. It kinda works, not perfectly, but works for many cases.

但这是另一个问题,即使标签适合条形图,调整图的大小也会使所有内容弄乱.仔细研究一下,我还在 ggplot文档中找到

But here's another problem, even when the label fits within the bar, resizing the plot messes everything up. Looking into it, I also found in the ggplot documentation that

标签确实有高度和宽度,但是它们是物理单位,而不是数据 单位.他们在该图上占据的空间量在 数据单位:调整图的大小时,标签保持不变,但是 轴的大小会改变.

labels do have height and width, but they are physical units, not data units. The amount of space they occupy on that plot is not constant in data units: when you resize a plot, labels stay the same size, but the size of the axes changes.

以下是我的第二个问题:是否可以更改此默认行为并使标签随图重新调整大小?

Which takes me to my second question: Is it possible to change this default behaviour and let/make the labels resize with the plot?

还让我完善我的第一个问题.是否有可能通过物理单位和数据单位之间的巧妙关系,强制geom_text放入geom_bar中,从而动态设置文本的大小?

And also let me refine my first question. Is it possible to force a geom_text to fit within a geom_bar, dynamically setting the size of the text using a clever relation between physical units and data units?

因此,为了遵循良好的做法,这是我可复制的示例:

So, to follow good practice, here's my reproducible example:

set.seed(1234567)
data_gd <- data.frame(x = letters[1:5], 
                      y = runif(5, 100, 99999))

ggplot(data = data_gd,
       mapping = aes(x = x, y = y, fill = x)) +
    geom_bar(stat = "identity") +
    geom_text(mapping = aes(label = y, y = y/2))

此代码生成以下图:

如果我只是简单调整图的大小,则"标签保持相同的大小,但是轴的大小更改",从而使标签适合条形图(现在也许标签太小了.)

If I simply resize the plot, "labels stay the same size, but the size of the axes changes" thereby making the labels fit into the bars (now perhaps labels are even too small).

所以,这是我的第二个问题.标签也可以调整大小,并保持相对于长条的宽高比是很好的.有什么想法可以实现这一目标,或者是否有可能实现?

So, this is my second question. It would be nice that the labels resize as well and keep the aspect ration in relation to the bars. Any ideas how to accomplish this or if it is possible at all?

好的,但是回到如何在条形图中放置标签,最简单的解决方案是设置标签的大小.

Ok, but going back to how to fit the labels within the bars, the simplest solution is to set the size of the labels.

ggplot(data = data_gd,
       mapping = aes(x = x, y = y, fill = x)) +
    geom_bar(stat = "identity") +
    geom_text(mapping = aes(label = y, y = y/2), size = 3)

同样,它的工作原理如下所示,但是它对数据的更改而言既不可维护也不可靠.

Again, this works as shown below, but it is not maintainable /nor robust to changes in the data.

例如,使用完全相同的代码生成具有不同数据的绘图将产生灾难性的结果.

For example, the very same code to generate the plot with different data yields catastrophic results.

data_gd <- data.frame(x = letters[1:30], 
                      y = runif(30, 100, 99999))
ggplot(data = data_gd,
       mapping = aes(x = x, y = y, fill = x)) +
    geom_bar(stat = "identity") +
    geom_text(mapping = aes(label = y, y = y/2), size = 3)

我可以继续进行示例,将标签的大小设置为x轴上类别数的函数,依此类推.但是您明白了这一点,也许您当中的一位ggplot2专家可以给我一些想法.

And I can go on with the examples, setting the size of the labels as a function of the number of categories on x-axis and so on. But you get the point, and perhaps one of you ggplot2 experts can give me ideas.

推荐答案

一个选项可能是编写一个geom,该geom使用textGrob和自定义的drawDetails方法来适合分配的空间,该空间由钢筋宽度设置.

one option might be to write a geom that uses a textGrob with a custom drawDetails method to fit within the allocated space, set by the bar width.

library(grid)
library(ggplot2)

fitGrob <- function(label, x=0.5, y=0.5, width=1){
  grob(x=x, y=y, width=width, label=label, cl = "fit")
}
drawDetails.fit <- function(x, recording=FALSE){
  tw <- sapply(x$label, function(l) convertWidth(grobWidth(textGrob(l)), "native", valueOnly = TRUE))
  cex <- x$width / tw
  grid.text(x$label, x$x, x$y, gp=gpar(cex=cex), default.units = "native")
}


`%||%` <- ggplot2:::`%||%`

GeomFit <- ggproto("GeomFit", GeomRect,
                   required_aes = c("x", "label"),

                   setup_data = function(data, params) {
                     data$width <- data$width %||%
                       params$width %||% (resolution(data$x, FALSE) * 0.9)
                     transform(data,
                               ymin = pmin(y, 0), ymax = pmax(y, 0),
                               xmin = x - width / 2, xmax = x + width / 2, width = NULL
                     )
                   },
                   draw_panel = function(self, data, panel_scales, coord, width = NULL) {
                     bars <- ggproto_parent(GeomRect, self)$draw_panel(data, panel_scales, coord)
                     coords <- coord$transform(data, panel_scales)    
                     width <- abs(coords$xmax - coords$xmin)
                     tg <- fitGrob(label=coords$label, y = coords$y/2, x = coords$x, width = width)

                     grobTree(bars, tg)
                   }
)

geom_fit <- function(mapping = NULL, data = NULL,
                     stat = "count", position = "stack",
                     ...,
                     width = NULL,
                     binwidth = NULL,
                     na.rm = FALSE,
                     show.legend = NA,
                     inherit.aes = TRUE) {

  layer(
    data = data,
    mapping = mapping,
    stat = stat,
    geom = GeomFit,
    position = position,
    show.legend = show.legend,
    inherit.aes = inherit.aes,
    params = list(
      width = width,
      na.rm = na.rm,
      ...
    )
  )
}


set.seed(1234567)
data_gd <- data.frame(x = letters[1:5], 
                      y = runif(5, 100, 99999))

ggplot(data = data_gd,
       mapping = aes(x = x, y = y, fill = x, label=round(y))) +
  geom_fit(stat = "identity") +
  theme()

这篇关于ggplot2:geom_text调整大小,并在geom_bar中强制/拟合文本的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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