6

To make my figure suitable for black-white printing, I mapped one variable with "shape", "lty", "color" together.

ggplot(df, aes(x=time, y=mean, 
               shape=quality, 
               lty=quality,
               color=quality))

I got the figure like, enter image description here I would like to make part of legends as subscribs, with the codes:

labels=c(expression(Pol[(Art)]), expression(Pol['(Aca-)']), expression(Pol['(Aca-)']))

Unfortunately, when I put the "label" in color or shape, it makes the legend quite complex, like,

enter image description here

Is it possible to map "shape", "color","lty" to one varible, and set the subscript, but keep them in one set of legend?

Jellz
  • 435
  • 1
  • 7
  • 14
  • I think you should be supplying labels as an argument to `scale_colour_discrete`, rather than as an additional aesthetic? But a reproducible example would help - we can't see what you're doing – Calum You Mar 06 '18 at 19:40
  • @CalumYou, Thank you very much, `scale_*_discrete` works! – Jellz Mar 07 '18 at 08:25

1 Answers1

7

To change the labels of a categorical scale, you use scale_*_discrete(labels = ...). Here you just need to do that for color, shape, and linetype.

You should avoid using lty = generally; that synonym is permitted for compatibility with base R, but it's not universally supported throughout ggplot2.

I changed your labels to be closer to what I think you meant (the third entry is now "Aca+" instead of a repeat of "Aca-") and to make them left-align better (by adding an invisible "+" to the first one to create the appropriate spacing).

lab1 <- c(expression(Pol[(Art)*phantom("+")]),
          expression(Pol['(Aca-)']), 
          expression(Pol['(Aca+)']))

library(ggplot2)

ggplot(mtcars, 
       aes(wt, mpg, 
           color = factor(cyl), 
           shape = factor(cyl), 
           linetype = factor(cyl))) +
  geom_point() +
  stat_smooth(se = F) +
  scale_color_discrete(labels = lab1) +
  scale_shape_discrete(labels = lab1) +
  scale_linetype_discrete(labels = lab1)

enter image description here

If you find yourself needing to repeat exact copies of a function like this, there's two workarounds:

  1. Relabel the data itself - OR -

  2. Use purrr::invoke_map to iterate over the functions


library(purrr)

ggplot(mtcars, 
       aes(wt, mpg, 
           color = factor(cyl), 
           shape = factor(cyl), 
           linetype = factor(cyl))) +
  geom_point() +
  stat_smooth(se = F) +
  invoke_map(list(scale_color_discrete, 
                  scale_linetype_discrete, 
                  scale_shape_discrete),
             labels = lab1)

Update:

This approach is mostly fine, but now the expression(...) syntax has a superior alternative, the excellent markdown-based {ggtext} package: https://github.com/wilkelab/ggtext

To change to this method, use a (optionally, named) vector of labels that look like this:

library(ggtext)

lab1 <- c(
  `4` = "Pol<sub>(Art)</sub>",
  `6` = "Pol<sub>(Aca-)</sub>", 
  `8` = "Pol<sub>(Aca+)</sub>"
)

And then add this line to your theme:

  ... +
  theme(
    legend..text = element_markdown()
  )

The advantages over the other method are that:

  1. markdown syntax is a lot easier to search for help online and
  2. now those labels can be stored in the actual data as a column, rather than passing them separately to each geom

You can use that new column as your aesthetic mapping [ggplot(..., aes(color = my_new_column, linetype = my_new_column, ...)] instead of having to pass extra labels in each layer using the purrr::invoke method.

Brian
  • 7,900
  • 1
  • 27
  • 41
  • Thank you very much! That works perfectly for me! Very appreciate your kind help :) – Jellz Mar 07 '18 at 08:23
  • 2
    To do mapping from specific label values to the new labels you can use: `lab1 <- c("myTextualLabelFieldValue" = expression(my^{fancy}[label]), ... )` – Tim Jul 23 '19 at 11:23