Adding multiple legends to ggplot

2020-03-29 08:16发布

问题:

I have following multi-layered plot:

df <- data.frame(number = 1:10, 
                 values = rnorm(10), 
                 decision = factor(rbinom(10,1,.5), levels = c(0,1),
                                   labels=c("No","Yes")),
                 expValues = rnorm(10),
                 bandwidth = runif(10, 0,1))

ggplot(df,aes(x = number, y = values, color=decision)) + aes(group=NA) +
  geom_line(size = 1) + 
  geom_point(size = 3,shape = 16) + 
  geom_smooth(data = df, aes(ymin = values-bandwidth , ymax = values+bandwidth), 
              stat = "identity") +
  geom_point(data=df,
             aes(x=number,y=expValues),shape = "x", size = 5, color = "blue") +
  geom_text(data = data.frame(x = Inf, y = max(df$values), label = "Mean  = 12"), 
            aes(label=label, x =  x, y = y) ,
            hjust = 1, vjust = -0.1, color = "brown", size = 10) +
  geom_hline(yintercept=mean(df$values) ,color="blue", linetype = "dashed") +
  theme(text=element_text(size=20))

I want to add legends for geom_hline and geom_point with shape "x", denoting the for the first it is "Cut Value", for the second "Expected Value".

How can I do that?

Note: I checked THIS post and THIS post for possible solutions but cannot figure out how can I do it for especially geom_hline.

回答1:

To add legends, the first thing to do is to actually map something to the desired aesthetic. For example, right now you've set the point shape as x, but you didn't map it inside of aes so you don't get a legend. You can map to constants as well as to variables to force a legend.

For your geom_point layer, you can just move shape inside aes, mapped to whatever constant you like. Using some value that indicates what it is will make the legend editing easier.

geom_point(data=df, aes(x=number, y=expValues, shape = "Expected value"), size = 5, color = "blue")

For the geom_hline, you'll need a dataset specific to the layer for mapping purposes. Depending on the route you go, you may also need to set show_guide to TRUE in this layer.

geom_hline(data = data.frame(yint = mean(df$values)), aes(...), show_guide = TRUE)

You could make two separate legends. You could also combine the line and shapes into a single legend as in this answer here. Both options will involve setting values in the appropriate scale_xxx_manual and using override.aes in guide_legend.

Here is how you could make a single new legend. Notice I had to add geom_hline before geom_line to make the decision legend look right.

ggplot(df, aes(x = number, y = values, color=decision, group = NA)) +
    geom_hline(data = data.frame(yint = mean(df$values)),
        aes(yintercept = yint, shape = "Cut value"), 
             color="blue", linetype = 2, show_guide = TRUE) +
    geom_line(size = 1) + 
    geom_point(size = 3,shape = 16) + 
    geom_smooth(data = df, aes(ymin = values-bandwidth , ymax = values+bandwidth), 
              stat = "identity") +
    geom_point(data=df, aes(x=number, y=expValues, shape = "Expected value"), 
             size = 5, color = "blue") +
    geom_text(data = data.frame(x = Inf, y = max(df$values), label = "Mean  = 12"), 
        aes(label=label, x =  x, y = y),
        hjust = 1, vjust = -0.1, color = "brown", size = 10) +      
    theme(text=element_text(size=20)) +
    scale_shape_manual(name = "", values = c("x", "x")) +
    guides(shape = guide_legend(override.aes = list(linetype = c(2, 0), 
                                            shape = c(NA, "x"))))

Edit Add a legend for the error band ribbon

I couldn't quite get things working with fill to add a third legend based on the error band ribbon. You could do this as three separate legends, although I don't think the spacing is as nice:

ggplot(df, aes(x = number, y = values, color=decision, group = NA)) +
    geom_hline(data = data.frame(yint = mean(df$values)),
        aes(yintercept = yint, linetype = "Cut value"), 
             color="blue", show_guide = TRUE) +
    geom_line(size = 1) + 
    geom_point(size = 3,shape = 16) + 
    geom_smooth(data = df, aes(ymin = values-bandwidth , ymax = values+bandwidth, fill = "Error band"), 
              stat = "identity") +
    geom_point(data=df, aes(x=number, y=expValues, shape = "Expected value"), 
             size = 5, color = "blue") +
    geom_text(data = data.frame(x = Inf, y = max(df$values), label = "Mean  = 12"), 
        aes(label=label, x =  x, y = y),
        hjust = 1, vjust = -0.1, color = "brown", size = 10) +  
    theme(text=element_text(size=20)) +
    scale_shape_manual(name = "", values = "x") +
    scale_linetype_manual(name = "", values = 2) +
    scale_fill_manual(name = "", values = "grey") +
    guides(shape = guide_legend(override.aes = list(linetype = 0)),
          fill = guide_legend(override.aes = list(linetype = 0)),
          color = guide_legend(override.aes = list(fill = NA)))

Alternatively, with some work inside override.aes, this could be done with the combination of colour and size along with linetype and shape.

ggplot(df, aes(x = number, y = values, color=decision, group = NA)) +
    geom_hline(data = data.frame(yint = mean(df$values)),
        aes(yintercept = yint, shape = "Cut value"), 
             color="blue", linetype = 2, show_guide = TRUE) +
    geom_line(size = 1) + 
    geom_point(size = 3,shape = 16) + 
    geom_smooth(data = df, aes(ymin = values-bandwidth , ymax = values+bandwidth, shape = "Error band"), 
              stat = "identity", show_guide = FALSE) +
    geom_point(data=df, aes(x=number, y=expValues, shape = "Expected value"), 
             size = 5, color = "blue") +
    geom_text(data = data.frame(x = Inf, y = max(df$values), label = "Mean  = 12"), 
        aes(label=label, x =  x, y = y),
        hjust = 1, vjust = -0.1, color = "brown", size = 10) +  
    theme(text=element_text(size=20)) +
    scale_shape_manual(name = "", values = rep("x", 3)) +
    guides(shape = guide_legend(override.aes = list(linetype = c(2, 1, 0), 
                                           size = c(.5, 10, 5),
                                           shape = c(NA, NA, "x"),
                                           colour = c("blue", "grey75", "blue"))))