ggplot2: How to integrate legends for scales with parsed labels?

Viewed 670

Problem

In ggplot2, legends for different scales are usually integrated into a single, combined legend whenever possible. This worked fine for me so far. However, when I try parsing the scale labels to include mathematical symbols in the legend, this does not seem to work.

See this example:

# example data
d <- data.frame(x = 1:3, y = rep(0,3), f = c("a[1]", "a[2]", "a[3]"))

# plot
p <- ggplot(data = d, aes(x = x, y = y, color = f, shape = f)) +
  geom_point() +
  guides(
    color = guide_legend(title = "F"),
    shape = guide_legend(title = "F")
  )

The following gives the plot with custom values for shapes/colors and with the legends combined as intended.

# plot + custom shapes/colors
p +
  scale_color_manual(name = "F", values = c("red", "blue", "green")) +
  scale_shape_manual(name = "F", values = c(16, 15, 18))

integrated legends

However, when parsing the labels, the labels come out as expected, but the legends are no longer combined.

# plot + custom shapes/colors + parsed labels
parse.labels <- function(x) parse(text = x)
p +
  scale_color_manual(name = "F", labels = parse.labels, values = c("red", "blue", "green")) +
  scale_shape_manual(name = "F", labels = parse.labels, values = c(16, 15, 18))

separate legends

Note that the result is the same with scale_._discrete instead of scale_._manual. Similarly, specifying identical names for the two scales with guides(shape = guide_legend(title = "F"), color = guide_legend(title = "F")) does not change this behavior.

Question

How can I integrate the two legends while maintaining the parsed labels?

2 Answers

I would suggest this approach using the labels argument in scale_*_discrete() and saving your values for labels in a new vector:

library(ggplot2)
# example data
d <- data.frame(x = 1:3, y = rep(0,3), f = c("a[1]", "a[2]", "a[3]"))
#Labs
lab1 <- c(expression(a[1]),
          expression(a[2]), 
          expression(a[3]))
# plot
ggplot(data = d, aes(x = x, y = y, color = f, shape = f)) +
  geom_point() +
  guides(
    color = guide_legend(title = "F"),
    shape = guide_legend(title = "F")
  )+
  scale_color_discrete(labels = lab1) +
  scale_shape_discrete(labels = lab1)

Output:

enter image description here

Use scales::parse_format() instead of the parse() function from base R, and you should be fine:

library(scales)

ggplot(data = d, aes(x = x, y = y, color = f, shape = f)) +
  geom_point() +
  scale_color_manual(name = "F", 
                     labels = parse_format(),
                     values = c("red", "blue", "green")) +
  scale_shape_manual(name = "F", 
                     labels = parse_format(),
                     values = c(16, 15, 18))

I think this has something to do with how parse returns an expression tagged with automatically-generated srcfile / wholeSrcref attributes by default, while parse_format does not. These additional attributes prevent the two scales from being merged together, since they are not identical.

(Using function(x) parse(x = text, srcfile = NULL) in both scales will also work, same as above, but I find the function from scales to be less verbose.)

Related