ggplot2: geom_text resize with the plot and force/fit text within geom_bar

后端 未结 2 1114
傲寒
傲寒 2020-12-30 03:00

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

First question is how can I force a geom_text to fit within a

相关标签:
2条回答
  • 2020-12-30 03:28

    If horizontal bar charts are OK, then the issue is not the size of the labels but the placement. My solution would be

    created by this code:

    library(ggplot2)
    data_gd <- data.frame(x = letters[1:26], 
                          y = runif(26, 100, 99999))
    ymid <- mean(range(data_gd$y))
    ggplot(data = data_gd,
           mapping = aes(x = x, y = y, fill = x)) +
      geom_bar(stat = "identity") +
      geom_text(mapping = aes(label = y, y = y, 
                hjust = ifelse(y < ymid, -0.1, 1.1)), size = 3) +
      coord_flip()
    

    The trick is done in three steps:

    1. coord_flip makes a horizontal bar chart.
    2. The mapping in geom_text uses also hjust depending on the value of y. If the bar is shorter than half of the range of y, the text is printed outside of the bar (right to the y value). If the bar is longer than half of the range of y, the text is printed inside the bar (left to the y value). This makes sure that the text is always printed inside the plot area (if not too long at all).
    3. I have added some additional space between the bar and the text. If you want the text to start or end directly at the y-value you can use hjust = ifelse(y < ymid, 0, 1)).
    0 讨论(0)
  • 2020-12-30 03:39

    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()
    

    0 讨论(0)
提交回复
热议问题