Cameron
Cameron

Reputation: 320

Creating completely customized legends in ggplot2

I am wondering if it is possible to create a completely customized legend for a ggplot figure, that does not take into consideration any of the shapes, sizes, column names, etc. used in the given figure. It seems strange that this may not be the case, given that complex data sets can be difficult to rename, and that adding text to ggplot figure axes, annotations and labels is so straightforward. Can this perhaps be added to a ggplot object, in the same way you might add a theme? Let's say I wanted to have a legend that was titled "Different fruit", the three variables were "Apple", "Banana" and "Mango," and the three symbols associated with them were a filled black circle, a hollow circle, and a hollow triangle.

Here is some data I was playing around with:


library(ggplot2)
library(dplyr)

data_dna <- DNase

plot_dna <- ggplot(data_dna, aes(x = conc, y = density)) + geom_point()

plot_dna + theme(legend.position = c(2, 2), legend.text = element_text(size = 15))


This doesn't even succesfully produce a legend at all. Please let me know if you have any leads on this.

Upvotes: 1

Views: 478

Answers (1)

teunbrand
teunbrand

Reputation: 37993

Here is a hacky solution I came up with. It draws an invisible geom layer that initialises a dummy aesthetic and associated scale. Not very flexible, but should do the trick.

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

data_dna <- DNase

plot_dna <- ggplot(data_dna, aes(x = conc, y = density)) + geom_point()

dummy_guide <- function(
    labels = NULL,  
    ..., 
    title = NULL, 
    key   = draw_key_point,
    guide_args = list()
) {
  # Capture arguments
  aesthetics <- list(...)
  n <- max(lengths(aesthetics), 0)
  labels <- labels %||%  seq_len(n)
  
  # Overrule the alpha = 0 that we use to hide the points
  aesthetics$alpha <- aesthetics$alpha %||% rep(1, n)
  
  # Construct guide
  guide_args$override.aes <- guide_args$override.aes %||% aesthetics
  guide <- do.call(guide_legend, guide_args)
  
  # Allow dummy aesthetic
  update_geom_defaults("point", list(dummy = "x"))
  
  dummy_geom <- geom_point(
    data = data.frame(x = rep(Inf, n), y = rep(Inf, n), 
                      dummy = factor(labels)),
    aes(x, y, dummy = dummy), alpha = 0, key_glyph = key
  )
  dummy_scale <- discrete_scale(
    "dummy", "dummy_scale", palette = scales::identity_pal(), name = title,
    guide = guide
  )
  list(dummy_geom, dummy_scale)
}

Adding this without any arguments creates nothing.

plot_dna + dummy_guide()

By default, it creates keys as points.

plot_dna + dummy_guide(
  labels = c("Apple", "Banana", "Mango"), 
  shape  = c(16, 21, 24),
  title  = "Different Fruit"
)

You can change to key to something else.

plot_dna + dummy_guide(
  labels = c("Foo", "Bar", "Baz"), 
  fill   = c("blue", "green", "red"),
  colour = NA,
  title  = "Metasyntactic variables",
  key = draw_key_polygon
)

Created on 2022-02-03 by the reprex package (v2.0.1)

Upvotes: 3

Related Questions