1

I'm trying to plot two groups by their data and trendline over time. Ideally, I'd like the legend to show the groups as title with the rate and trendline below. Right now the legend is only showing for the group and not the trendline. This is my dummy code:

week <- c(1,2,3,4,5,1,2,3,4,5)
hits <- c(262,698,239,195,122,160,746,283,557,239)
rate <- c(116, 134, 154, 185, 167, 216, 178, 242, 267, 234)
lower <- c(94, 111, 123, 145, 138, 196, 147, 204, 216, 176)
upper <- c(126, 167, 176, 225, 191, 245, 199, 287, 292, 267)
group <- c("A","A","A","A","A","B","B","B","B","B")
df <- data.frame(week = week, hits=hits, rate=rate, lower=lower, upper=upper,group=group)


ggplot(df, aes(x = week)) + 
  geom_line(aes(y = rate, color = group)) +
  geom_ribbon(aes(ymin = lower, ymax = upper, fill = group), 
              alpha = 0.2) +
  geom_smooth(aes(y = rate, color = group), method = 'lm',  
              size = 0.5, se = FALSE, linetype = "dashed",
              show.legend = TRUE) +
  scale_color_manual(values = c("red", "black")) +
  scale_fill_manual(values = c("red", "black")) 

enter image description here

Edit So ideally the legend would look like this:

enter image description here

AstraOK
  • 49
  • 9

2 Answers2

1

Unfortunately, I only found a solution doing this manually using the library ggnewscale:

Manual approach

df %>%
  ggplot(aes(x = week)) + 
  geom_line(data = . %>% filter(group == 'A'), 
            aes(y = rate, color = 'Rate', linetype = 'Rate')) +
  geom_ribbon(data = . %>% filter(group == 'A'), 
              aes(ymin = lower, ymax = upper, fill = 'Rate'),
              alpha = 0.2) +
  geom_smooth(data = . %>% filter(group == 'A'), 
              aes(y = rate, color = 'Rate', linetype = 'Linear Trend'), method = 'lm',  
              size = 0.5, se = FALSE) +
  scale_linetype_manual(values = c(Rate = 'solid', 'Linear Trend' = 'dashed'), name ='Group A', guide = guide_legend(order = 1)) +
  scale_color_manual(values = c(Rate = 'red', 'Linear Trend' = 'red'), name = 'Group A', guide = guide_legend(order = 1)) +
  scale_fill_manual(values = c(Rate = 'red', 'Linear Trend' = 'white'), name = 'Group A', guide = guide_legend(order = 1)) +
  new_scale('linetype') +
  new_scale_color() +
  new_scale_fill() +
  geom_line(data = . %>% filter(group == 'B'), 
            aes(y = rate, color = 'Rate', linetype = 'Rate')) +
  geom_ribbon(data = . %>% filter(group == 'B'), 
              aes(ymin = lower, ymax = upper, fill = 'Rate'),
              alpha = 0.2) +
  geom_smooth(data = . %>% filter(group == 'B'), 
              aes(y = rate, color = 'Rate', linetype = 'Linear Trend'), method = 'lm',  
              size = 0.5, se = FALSE) +
  scale_linetype_manual(values = c(Rate = 'solid', 'Linear Trend' = 'dashed'), name ='Group B') +
  scale_color_manual(values = c(Rate = 'black', 'Linear Trend' = 'black'), name = 'Group B') +
  scale_fill_manual(values = c(Rate = 'black', 'Linear Trend' = 'white'), name = 'Group B') +
  theme_classic() +
  theme(legend.title = element_text(face="bold"))

First, this plots the data of group A and manually sets the legend entries. Then, new scales are created using new_scale('linetype'), new_scale_color() and new_scale_fill() from ggnewscale. After that, create the plots for group B and again, manually set the legend entries.

I assume there must be a better solution to this, in the meantime, I hope this helps you out!

Resulting plot

resulting plot

leomfn
  • 155
  • 1
  • 11
0

I believe you want to have multiple legends for the same aesthetic

The instructions in the linked blog are somewhat dated (they refer to show.guide instead of show.legend), but following the idea there, you could do the following:

ggplot(df, aes(x = week)) + 
  geom_line(aes(y = rate, color = group), show.legend=TRUE) + 
  geom_ribbon(aes(ymin = lower, ymax = upper, fill = group), 
              alpha = 0.2 ) + 
  geom_smooth(mapping=aes(y = rate, color = group), method = 'lm',  
              size = 0.5, se = FALSE, linetype = "dashed", show.legend=FALSE) +
 
  scale_fill_manual(values = c("red", "black"),  name='Group',
  guide=guide_legend(override.aes = list(colour=c("red", "black")))) +
 
  scale_color_manual(values = c("red", "black"),  name='Trend',
  guide=guide_legend(override.aes = list(colour=c("red", "black"), linetype='dashed')))  
  

The two final lines are key; basically you need to manually create the legends you want. Result:

enter image description here

This question is also relevant.

Otto Kässi
  • 2,943
  • 1
  • 10
  • 27
  • 1
    Thanks so much for the answer! Ideally I'd like both, and for it to be divided by group and type of line, though I'm not sure if my data structure will allow this? – AstraOK Oct 21 '21 at 15:43