Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a legend for geom_grob shapes?

Tags:

r

ggplot2

grob

I'm trying to define custom shapes to use in ggplot2 by passing grobs created with the grid package to geom_grob. So, for example, I might create the following two grobs (a square and a triangle):

library(grid)
library(ggplot2)
library(dplyr)
library(ggpp)

devratio <- dev.size()[2]/dev.size()[1]

square_ang <- seq(pi/4, 9*pi/4, length.out = 5)
square <- grid.polygon(
  x = unit(0.5 + 0.2 * devratio * sin(square_ang), 'npc'),
  y = unit(0.5 + 0.2 * cos(square_ang), 'npc'),
  gp = gpar(lwd = 2, fill = 'blue', col = 'blue')
  )

triangle_ang <- seq(0, 2*pi, length.out = 4)
triangle <- grid.polygon(
  x = unit(0.5 + 0.18 * devratio * sin(triangle_ang), 'npc'),
  y = unit(0.5 + 0.18 * cos(triangle_ang), 'npc'),
  gp = gpar(lwd = 2, fill = 'blue', col = 'blue')
  )

Within geom_grob there is an argument show.legend, but after setting that to TRUE nothing shows up.

ggplot(tibble(x = 1:6, y = 1:6, 
       shape = rep(list(square, triangle), each = 3))) +
  geom_grob(aes(x, y, label = shape), show.legend = TRUE)

plot1

I thought maybe ggplot2 doesn't consider label to be an aesthetic, so I tried adding an artificial color specification. That got the legend to appear, but the legend key is blank.

ggplot(tibble(x = 1:6, y = 1:6, 
       shape = rep(list(square, triangle), each = 3),
       col = rep(c('col1', 'col2'), each = 3))) +
  geom_grob(aes(x, y, label = shape, color = col), 
            show.legend = TRUE)

plot2

Is it possible to have a legend showing the custom grobs?

like image 442
js4032 Avatar asked Dec 22 '25 22:12

js4032


1 Answers

At least for your shapes I think the easiest approach would be to go on with your second approach and fake a legend using the color or ... aesthetic. The reason that no keys show up is that default draw_key function of geom_grob or GeomGrob returns a nullGrob, i.e. display nothing. But you can override that using the key_glyph argument:

library(grid)
library(ggplot2)
library(ggpp)
library(dplyr)
library(gtable)

ggplot(tibble(
  x = 1:6, y = 1:6,
  shape = rep(list(square, triangle), each = 3),
  col = rep(c("col1", "col2"), each = 3)
)) +
  geom_grob(aes(x, y, label = shape, color = col),
    show.legend = TRUE, key_glyph = "point"
  ) +
  guides(
    color = guide_legend(
      override.aes = list(
        shape = c(15, 17),
        color = "blue",
        size = 4
      )
    )
  )

For more general shapes, a second option (and alternative to the approach by @M--) would be to manually add a legend using guide_custom introduced in ggplot2 >= 3.5.0 where I use gtable to setup the layout for the legend grobs:

square_text <- textGrob(
  x = unit(0, "npc"),
  label = "Square",
  gp = gpar(fontsize = 8),
  hjust = 0
)
triangle_text <- textGrob(
  x = unit(0, "npc"),
  label = "Triangle",
  gp = gpar(fontsize = 8),
  hjust = 0
)

legend_key_height <- 20
# Setup the gtable layout
legend <- gtable(
  # Widths of columns.
  widths = unit(
    c(legend_key_height / devratio, 1.5), c("pt", "cm")
  ),
  heights = unit(rep(legend_key_height, 2), "pt")
)

# Add the grobs
legend <- gtable_add_grob(
  legend,
  grobs = list(square, triangle, square_text, triangle_text),
  t = c(1, 2, 1, 2),
  l = c(1, 1, 2, 2),
  clip = "off"
)

ggplot(tibble(
  x = 1:6, y = 1:6,
  shape = rep(list(square, triangle), each = 3)
)) +
  geom_grob(aes(x, y, label = shape)) +
  guides(
    custom = guide_custom(
      legend
    )
  ) +
  theme(
    legend.box.spacing = unit(0, "pt")
  )

like image 92
stefan Avatar answered Dec 24 '25 13:12

stefan



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!