Search code examples
rggplot2geom-hline

How can I efficiently create a manual legend when using a loop to create multiple geom_hline objects in a facet?


I have scripted the below example to illustrate my question.

I wanted to create my own custom legend and colour scheme for each geom_hline I plot. I could achieve this by using this answer Construct a manual legend for a complicated plot.

However, I am creating a function that will create any number horizontal lines on the plot with a loop. This means I needed to dynamically feeding variables to the aes_string function. However, aes_string(yintercept = colname, colour = colname) does not work. as I would get the following error'geom_hline Error: Discrete value supplied to continuous scale'.

This led me to create the below solution which involves creating an extra column for each line I wish to plot containing a name that can be picked up by the vector in scale_colour_manual. I find this cumbersome and inefficient.

This works as intended but I have 2 questions:

  1. Why does aes_string(yintercept = colname, colour = colname_colour) work and aes_string(yintercept = colname, colour = colname) does not.

  2. There must be a more efficient way to achieve the output I have got, what am I missing?

output of sample code: https://i.sstatic.net/RYd32.jpg

mean_wt <- data.frame(cyl = c(4, 6, 8)
, wt = c(2.28, 3.11, 4.00)
, wt2 = c(3.28, 4.11, 5.00)
, wt_col = c("a", "a", "a")
, wt_col2 = c("b", "b", "b"))

hline_listA <- list()
for(i in 2:3){
  colname <- mean_wt[,i]
  colname_colour <- mean_wt[,i+2]
  grob <- geom_hline(data =mean_wt
, aes_string(yintercept = colname, colour = colname_colour) )
  hline_listA[[i-1]] <- grob
}
ggplot() +
  geom_point(data = mtcars, aes(mpg, wt)) +
  hline_listA +
  facet_wrap(~ cyl, scales = "free", nrow = 1) +
  scale_colour_manual(name = "legend", values = c( 
    "a" = "seagreen1"  
    , "b" = "darkorange" ))

Solution

  • I cannot imagine a situation were I would use a loop to do anything in ggplot. The usual way to achieve what you want is to adjust the data frame in a way ggplot can work with it. Here is a much shorter solution with calling geom_line only once.

    library(ggplot2)
    mean_wt <- data.frame(cyl = c(4, 6, 8), 
                          wt = c(2.28, 3.11, 4.00, 3.28, 4.11, 5.00),
                          wt_col = c("a", "a", "a", "b", "b", "b"))
    
    ggplot() +
      geom_point(data = mtcars, aes(mpg, wt)) +
      geom_hline(data = mean_wt,aes(yintercept = wt, color = wt_col)) +
      facet_wrap(~ cyl, scales = "free", nrow = 1) +
      scale_colour_manual(name = "legend", 
                          values = c("a" = "seagreen1",
                                     "b" = "darkorange" ))
    

    enter image description here