3

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

user63230
  • 4,095
  • 21
  • 43

4 Answers4

3

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)

Allan Cameron
  • 147,086
  • 7
  • 49
  • 87
  • thank you, this is exactly what I wanted. One follow question if you dont mind. If I wanted to break up the legend into two, I thought I break up the `scale_colour_manual` and `scale_shape_manual` into two separate calls but it didnt work something like so: `scale_colour_manual(values = c("wt name" = "#F8766D"),name = "legend2") + scale_shape_manual(values = c("wt name" = 19 ), name = "legend2")`. Can two be added in this way or do you have to use `guide_legend`? – user63230 Sep 03 '20 at 13:55
  • @user63230 just give them different names like "legend1" and "legend2". They should split automatically. – Allan Cameron Sep 03 '20 at 13:57
  • i tried that and I get an error `Scale for 'colour' is already present.`? – user63230 Sep 03 '20 at 13:59
  • @user63230 that's weird. I'll add a complete reprex. Can you see if that works for you? – Allan Cameron Sep 03 '20 at 14:25
  • thanks but I wanted `legend1` to be a line color `#F8766D` with symbol `19`, `legend2` with line color `"#B79F00"` with symbol `8` if that makes sense? so two legends but only one in each. You have two legends with two in both, thanks – user63230 Sep 03 '20 at 14:49
  • @user63230 so you want two legends describing different values for the same aesthetics? Would using version 1 but with greater spacing between the keys not give essentially the same effect? Do you want each legend to have a title as well as a key name even though there is only one element in each legend? – Allan Cameron Sep 03 '20 at 15:17
  • 1
    @user63230 what you're asking is possible but doesn't make a lot of sense in terms of how aesthetics are mapped to legends, so requires a bit of hacking of scales and guides. However, I think my update is what you are looking for. – Allan Cameron Sep 03 '20 at 15:32
  • excellent thats what I want except the line is missing in `legend2`? – user63230 Sep 03 '20 at 15:59
  • @user63230 that's because you wanted a separate legend, and we had to use a custom `shape` legend to create it - this can't have a line in it. So to further complicate things, we now need to add another legend and merge it to get a line here. In this case, I have added a `size` scale and merged its legend with the shape legend to get the desired line. – Allan Cameron Sep 03 '20 at 16:15
  • Excellent answer, appreciate that! – user63230 Sep 03 '20 at 16:16
3

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

Wimpel
  • 26,031
  • 1
  • 20
  • 37
2

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"))))
NotThatKindODr
  • 729
  • 4
  • 14
1

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

Rui Barradas
  • 70,273
  • 8
  • 34
  • 66