Ggplot: how to combine the linetype and shape in one legend

ggplot2
tidyverse
rstudio

#1

Hello everyone,

I am using ggplot to plot the following graph (one example attached). What I want to achieve is to have one legend to show linetype 4 & shape 1 combined as "group 1" and linetype 1 & shape 2 combined as "group 2". Also, the legend name will be something such as "example legend"

I tried scale_shape_manual and scale_linetype_manual. However, I didn't find the right way. The code is listed as below (the graph has no legend which I want to add correctly)

Thanks.

y1 <- x+0.01
y2 <- x+10
df1 <- data.frame(x, y1)
df2 <- data.frame(x, y2)

graph_1 <- subset(df1,x>=0 & x<=5)
graph_2 <- subset(df1,x>=6 & x<=10)
graph_3 <- subset(df1,x>=11 & x<=15)
graph_4 <- subset(df2,x>=0 & x<=5)
graph_5 <- subset(df2,x>=6 & x<=10)
graph_6 <- subset(df2,x>=11 & x<=15)

win.graph(width = 13,height = 6,pointsize = 8)
ggplot() + geom_point(aes(x, y1),data = graph_1,shape =1) + geom_smooth(aes(x, y1),data = graph_1,method = 'loess',linetype=4) +geom_point(aes(x, y1),data = graph_2,shape =1) + geom_smooth(aes(x, y1),data = graph_2,method = 'loess',linetype=4) + geom_point(aes(x, y1),data = graph_3,shape =1) + geom_smooth(aes(x, y1),data = graph_3,method = 'loess',linetype=4)+ geom_point(aes(x, y2),data = graph_4,shape =2) + geom_smooth(aes(x, y2),data = graph_4,method = 'loess',linetype=1) +geom_point(aes(x, y2),data = graph_5,shape =2) + geom_smooth(aes(x, y2),data = graph_5,method = 'loess',linetype=1) + geom_point(aes(x, y2),data = graph_6,shape =2) + geom_smooth(aes(x, y2),data = graph_6,method = 'loess',linetype=1)


#2

I think you can make your task easier by preparing your data a little more before it gets to ggplot.

For instance, you've split out the data into six tables and mapped to those. It'll probably be simpler in most situations to instead put that grouping into your source data and tell ggplot to map that variable into different groups.
https://ggplot2.tidyverse.org/reference/aes_group_order.html

Same goes for the linetype and shape, which you're manually mapping to y1 or y2. If you make your data frame "tidy" by using tidyr::gather(), you can put the distinction you're making between y1 and y2 into its own variable, and map that to linetype directly.
https://tidyr.tidyverse.org/articles/tidy-data.html

Here's one way to get the same results more directly:

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

df <-
  tibble(x = 0:15, y1 = x + 0.01, y2 = x + 10) %>%
  gather(key = y_grp, value = y, y1:y2) %>%
  mutate(group = paste(y_grp, 
                       case_when(x >= 0 & x <= 5   ~ 1,
                                          x <= 10  ~ 2,
                                          TRUE     ~ 3)))

ggplot(df, aes(x, y, group = group, 
               lty = y_grp, shape = y_grp)) +
  geom_point() + 
  geom_smooth(method = "loess") +
# These lines match the shapes and linetypes to yours. 
  scale_shape_manual(values = c(1,2)) +
  scale_linetype_manual(values = c(4,1))

#3

Thanks Jonspring. Your code works in R, however it has 48 warnings in the software (any problem there?). Is there any way to achieve the legend in the split data?

Thanks


#4

The 48 warnings all have to do with using the loess method in the geom_smooth. (As the warning message says, you can type warnings() to see the list. I suspect they're due to the small number of points in each group.)
If we replace method = "loess" with method = "lm", the warnings all go away.

It's possible to construct a common legend between two data frames (some links below), but in my experience it usually makes more sense to combine them in the data so that ggplot can do the work for you.


#5

many thanks. I found a way to achieve it due to the legend is the same for group 1 and group 2. I used that graph_1 and graph_4 as the legend. However, in this case, I didn't find a way to combine that linetype and shape in one legend. Do you have any good idea to achieve it?