Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I access dimensions of labels plotted by `geom_text` in `ggplot2`?

Tags:

r

ggplot2

As far as I can see ggplot2 knows the dimensions of labels plotted by geom_text. Otherwise the check_overlap option would not work.

Where are these dimensions stored and how can I access them?


Illustrative example

library(ggplot2)
df <- data.frame(x = c(1, 2), 
                 y = c(1, 1), 
                 label = c("label-one-that-might-overlap-another-label", 
                           "label-two-that-might-overlap-another-label"), 
                 stringsAsFactors = FALSE)

With check_overlap = FALSE (the default), the labels overplot each other.

ggplot(df, aes(x, y)) + 
  geom_text(aes(label = label)) + 
  xlim(0, 3)                              

enter image description here

With check_overlap = TRUE, the second label is not plotted, because ggplot finds an overlap.

ggplot(df, aes(x, y)) + 
  geom_text(aes(label = label), check_overlap = TRUE) + 
  xlim(0, 3)

enter image description here

How does ggplot2 know that those labels overlap? How can I access that information?

like image 764
symbolrush Avatar asked Apr 15 '19 10:04

symbolrush


People also ask

Which of the function will display the data labels in ggplot2?

Method 1: Using geom_text() This method is used to add Text labels to data points in ggplot2 plots.

What is Geom_text in Ggplot?

geom_text.Rd. Text geoms are useful for labeling plots. They can be used by themselves as scatterplots or in combination with other geoms, for example, for labeling points or for annotating the height of bars. geom_text() adds only text to the plot.

How do I change the size of text in Geomation?

The default font size of geom_text() is 3.88. You can change the size using the size argument in geom_text() for a single plot.

How do I change data labels in ggplot2?

To alter the labels on the axis, add the code +labs(y= "y axis name", x = "x axis name") to your line of basic ggplot code. Note: You can also use +labs(title = "Title") which is equivalent to ggtitle .


1 Answers

Text in the grid package doesn't really have a size until it is time to draw the text. Below, we'll make a helper function to measure text, but it doesn't really make any sense to do this unless you know the device and size of the plotting area in advance. (For those in the know, during the makeContent() stage of drawing).

library(grid)

label <- c("label-one-that-might-overlap-another-label", 
           "label-two-that-might-overlap-another-label")

measure_size <- function(txt, gp = gpar(), to = "mm") {
  if (is.grob(txt)) {
    grobs <- lapply(seq_along(txt$label), function(i) {
      g <- txt
      # Subset grob per label
      g$label <- g$label[[i]]
      g$gp[]  <- lapply(g$gp, function(x) {x[pmin(i, length(x))]})
      g$rot   <- g$rot[pmin(i, length(g$rot))]
      g
    })
  } else {
    grobs <- lapply(txt, function(t) textGrob(t, gp = gp))
  }
  
  heights <- do.call(unit.c, lapply(grobs, grobHeight))
  widths  <- do.call(unit.c, lapply(grobs, grobWidth))
  
  cbind(
    height = convertHeight(heights, to, valueOnly = TRUE),
    weight = convertWidth(widths,   to, valueOnly = TRUE)
  )
}

We can now try to guess the size of the text as best as we can, but as one might expect, it depends a lot on graphical parameters of text what the actual size is. Notice for example that changing the font also changes the size of the text.

measure_size(label)
#>      height   weight
#> [1,]  3.175 79.13109
#> [2,]  3.175 78.65566

measure_size(label, gp = gpar(fontfamily = "Garamond"))
#>        height   weight
#> [1,] 2.645833 69.67223
#> [2,] 2.645833 69.69704

Now applying the same trick to ggplot2's text layer.

library(ggplot2)
#> Warning: package 'ggplot2' was built under R version 4.1.1

df <- data.frame(x = c(1, 2), 
                 y = c(1, 1), 
                 label = label)

p <- ggplot(df, aes(x, y)) + 
  geom_text(aes(label = label)) + 
  xlim(0, 3) 

textgrob <- layer_grob(p)[[1]]
measure_size(textgrob)
#>        height   weight
#> [1,] 2.645979 72.83233
#> [2,] 2.645979 72.39411

Created on 2021-12-13 by the reprex package (v2.0.1)

I worked a lot with text recently myself and found that the {systemfonts}/{textshaping} packages accurately return the size of the text in pixels, which is of course device/resolution dependent.

systemfonts::string_width("My label")
#> [1] 46
textshaping::text_width("My label")
#> [1] 46
like image 122
teunbrand Avatar answered Sep 17 '22 21:09

teunbrand