ggplot shape not matching legend

Viewed 711

Looking to manually add a legend (I dont want to make the dataframe long in this case) but the legend shape doesnt match the graph:

library(ggplot2)
mtcars$time <- 1:nrow(mtcars)
ggplot(mtcars) +
  
  #wt
  geom_line(aes(x = time, y = wt, color = "wt name"), size = 1.5) +
  geom_point(aes(x = time, y = wt, color = "wt name"), shape = 19, size = 4) +
  
  #drat
  geom_line(aes(x = time, y = drat   , color = "drat name"), size = 1.5) +
  geom_point(aes(x = time, y = drat   , color = "drat name"), shape = 8, size = 4) +
  
  scale_colour_manual(name = "legend", values = c("wt name"     = "#F8766D",  
                                                    "drat name"   = "#B79F00"))

results in:

enter image description here

Other questions suggest using scale_shape_manual but I cant get it to work:

ggplot(mtcars) +
  
  #wt
  geom_line(aes(x = time, y = wt, color = "wt name"), size = 1.5) +
  geom_point(aes(x = time, y = wt, color = "wt name"), size = 4) +
  
  #drat
  geom_line(aes(x = time, y = drat   , color = "drat name"), size = 1.5) +
  geom_point(aes(x = time, y = drat   , color = "drat name"), size = 4) +
  
  scale_colour_manual(name = "legend", values = c("wt name"     = "#F8766D",  
                                                    "drat name"   = "#B79F00")) +
  scale_shape_manual(values = c("wt name" = 19,
                                "drat name" = 8 ))

gives this which ignores the shapes:

enter image description here

does anyone know what to change for this simple problem please? Thanks

4 Answers

If you wish to have a custom legend rather than reshaping your data (which would be the standard way to use ggplot), you need to map the shape as an aesthetic (i.e. put shape = inside the aes call, and give it the name you wish to appear on the legend). You also need to ensure that both scales are given the same name = if you want them to be merged in the legend:

library(ggplot2)

mtcars$time <- 1:nrow(mtcars)

ggplot(mtcars, aes(x = time)) +
  geom_line(aes(y = wt, color = "wt name"), size = 1.5) +
  geom_point(aes(y = wt, color = "wt name", shape = "wt name"), size = 4) +
  geom_line(aes(y = drat, color = "drat name"), size = 1.5) +
  geom_point(aes(y = drat, color = "drat name", shape ="drat name"), size = 4) +
  scale_colour_manual(values = c("wt name" = "#F8766D", "drat name" = "#B79F00"),
                      name = "legend") +
  scale_shape_manual(values = c("wt name" = 19, "drat name" = 8 ), 
                     name = "legend")

enter image description here

Or if you want them separated, give them different names:

library(ggplot2)

mtcars$time <- 1:nrow(mtcars)

ggplot(mtcars, aes(x = time)) +
  geom_line(aes(y = wt, color = "wt name"), size = 1.5) +
  geom_point(aes(y = wt, color = "wt name", shape = "wt name"), size = 4) +
  geom_line(aes(y = drat, color = "drat name"), size = 1.5) +
  geom_point(aes(y = drat, color = "drat name", shape ="drat name"), size = 4) +
  scale_colour_manual(values = c("wt name" = "#F8766D", "drat name" = "#B79F00"),
                      name = "legend2") +
  scale_shape_manual(values = c("wt name" = 19, "drat name" = 8 ), 
                     name = "legend1")

And, further to requests in the comments, if you want to have two legends but a single color and shape in each you will need to hack the breaks and guide, since this is not a standard use of aesthetic mapping.

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

mtcars$time <- 1:nrow(mtcars)

ggplot(mtcars, aes(x = time)) +
  geom_line(aes(y = wt, color = "wt name"), size = 1.5) +
  geom_point(aes(y = wt, color = "wt name", shape = "wt name"), size = 4) +
  geom_line(aes(y = drat, color = "drat name", size = "wt name")) +
  geom_point(aes(y = drat, color = "drat name", shape ="drat name"), size = 4) +
  scale_size_manual(values = c("wt name" = 1.5), breaks = "wt name", 
                    name = "legend2") +
  scale_colour_manual(values = c("wt name" = "#F8766D", "drat name" = "#B79F00"),
                      name = "legend1", breaks = "drat name") +
  scale_shape_manual(values = c("wt name" = 19, "drat name" = 8 ), 
                     name = "legend2", breaks = "wt name") +
  guides(colour = guide_legend(override.aes = list(shape = 8, color = "#B79F00")),
         shape = guide_legend(override.aes = list(shape = 19, color = "#F8766D")))

Created on 2020-09-03 by the reprex package (v0.3.0)

possibly easier to melt your data to long first... Also, no need to rename, you cean just set the legend's labels to your desire

library( data.table )
DT <- setDT(copy(mtcars), keep.rownames = TRUE)
#melt
plotdata <- melt(DT, id.vars = "time", measure.vars = c("wt","drat"))
#legend labels
legend_labels <- c("wt name", "drat name" )
ggplot( plotdata, aes( x = time, y = value, group = variable, shape = variable, colour = variable )) +
  geom_line( size = 1.5 ) +
  geom_point( size = 4 ) + 
  scale_colour_manual(name = "legend", labels = legend_labels, values = c("#F8766D", "#B79F00") ) + 
  scale_shape_manual( name = "legend", labels = legend_labels, values = c(19,8) )

enter image description here

Here, two things have changed. I added shape = "" to each point layer. Then the guide layer helps merge the two legends

ggplot(mtcars) +
  
  #wt
  geom_line(aes(x = time, y = wt, color = "wt name"), size = 1.5) +
  geom_point(aes(x = time, y = wt, color = "wt name", shape = "wt name"), size = 4) +
  
  #drat
  geom_line(aes(x = time, y = drat   , color = "drat name"), size = 1.5) +
  geom_point(aes(x = time, y = drat   , color = "drat name", shape = "drat name"), size = 4) +
  
  scale_colour_manual(name = "legend", values = c("wt name"     = "#F8766D",  
                                                  "drat name"   = "#B79F00")) +
  scale_shape_manual(values = c("wt name" = 19,
                                "drat name" = 8 )) +
  guides(shape = FALSE,
         colour = guide_legend(override.aes = list(shape = c(19, 8),
                                                   colour = c("#F8766D", "#B79F00"))))

This type of problems generaly has to do with reshaping the data. The format should be the long format and the data is in wide format. See this post on how to reshape the data from long to wide format. Here is a tidyverse solution.

library(dplyr)
library(tidyr)
library(ggplot2)

mtcars %>%
  select(wt, drat) %>%
  rename(c("wt name" = "wt", "drat name" = "drat")) %>%
  mutate(time = row_number()) %>%
  pivot_longer(
    cols = ends_with("name"),
    names_to = "variable",
    values_to = "value"
  ) %>%
  ggplot(aes(time, value, color = variable, shape = variable)) +
  geom_line(size = 1.5) +
  geom_point(size = 4) +
  scale_colour_manual(name = "legend",
                      values = c("wt name" = "#F8766D", "drat name" = "#B79F00")) +
  scale_shape_manual(name = "legend",
                     values = c("wt name" = 19, "drat name" = 8 ))

enter image description here

Related