legend aes override in R ggplot2

1.3k Views Asked by At

Problem: Override legend aesthetics. I want the Bev/Steroid legend to show up as a line with the relevant color (as it is now) and the background to be white/null. Then I want the On/Off Study and Response Legends to be the standard rectangle with relevant coloring. I would also like to re-order the legends to be 1. On/Off --> 2. Response --> 3. Bev/Steroids, but this is more of a bonus.


Background: Working with 5 datasets. On/Off dataset for the first geom_segment layer for the length on study and time the subject was alive and off study; Response dataset to mark whether patients had stable disease, partial response, or complete response; Dose information to add geom_point upside down triangles where patients were dosed (geom_label of actual dose removed for example due to potential sensitive info); Bev/Steroid use datasets, these are the geom_segment are on the edges of the On/Off geom_segment.


Steps Taken: Attempted to set key_glyph = "rect" for On/Off and Response legends and key_glyph = "smooth" for the steroid/bev legend. I have attempted many combinations of guide_legend, but to no avail.


Data: 5 different datasets are used in this plot:


  1. Response dataset: response
tibble [127 x 4] (S3: tbl_df/tbl/data.frame)
 $ SUBJECT  : chr [1:127] "01" "01" "01" "01" ... 
 $ RESPONSE : chr [1:127] "Stable Disease" "Partial Response" "Complete Response" "Progressive Disease" ...
 $ STARTRESP: num [1:127] 0 9.23 21.03 62.03 0 ... #continuous start stop time in months
 $ ENDRESP  : num [1:127] 9.23 21.03 62.03 88 1.9 ...

# A tibble: 6 x 4
  SUBJECT RESPONSE            STARTRESP ENDRESP
  <chr>   <chr>                   <dbl>   <dbl>
1 01      Stable Disease           0       9.23
2 01      Partial Response         9.23   21.0 
3 01      Complete Response       21.0    62.0 
4 01      Progressive Disease     62.0    88   
5 02      Stable Disease           0       1.9 
6 02      Progressive Disease      1.9    93.6 
  1. On/Off Dataset: onoff
'data.frame':   112 obs. of  6 variables:
 $ SUBJECT  : chr  "01" "01" "02" "02" ...
 $ RESPONSE : chr  "On Study" "Off Study/Alive" "On Study" "Off Study/Alive" ...
 $ STARTRESP: num  0 88.9 0 93.6 0 ...
 $ ENDRESP  : num  88.9 94.48 93.58 93.81 4.13 ...
 $ ALIVE    : num  0 0 0 0 0 0 0 0 0 0 ... #binary flag to indicate whether subject still alive; used for subsetting
 $ MAX_RESP : num  94.48 94.48 93.81 93.81 5.77 ... #overall survival used for subsetting

SUBJECT        RESPONSE STARTRESP   ENDRESP ALIVE  MAX_RESP
1      01        On Study  0.000000 88.900000     0 94.483871
2      01 Off Study/Alive 88.900000 94.483871     0 94.483871
3      02        On Study  0.000000 93.580645     0 93.806452
4      02 Off Study/Alive 93.580645 93.806452     0 93.806452
5      03        On Study  0.000000  4.129032     0  5.774194
6      03 Off Study/Alive  4.129032  5.774194     0  5.774194 
  1. Dose Info Dataset: dose
 $ SUBJECT  : chr [1:67] "01" "01" "02" "02" ...
 $ TREATMENT: Factor w/ 7 levels #redacted
 $ DATE     : POSIXct[1:67], format: #redacted
 $ DUR      : num [1:67] 0 71 0 59 77 0 0 0 0 0 ... #continuous months duration

  1. Bev Use Dataset: bev
tibble [140 x 5] (S3: tbl_df/tbl/data.frame)
 $ SUBJECT : chr [1:140] "01" "01" "01" "02" ...
 $ ORD     : num [1:140] 1 2 3 1 1 1 2 3 1 2 ...
 $ BEV     : chr [1:140] "Bevacizumab" "Bevacizumab" "Bevacizumab" "Bevacizumab" ...
 $ STARTBEV: num [1:140] 81.48 89.34 93.91 87.7 4.44 ...
 $ ENDBEV  : num [1:140] 88.91 93.42 93.91 88.36 4.87 ...

  1. Steroid Use Dataset: steroid
$ SUBJECT   : chr [1:668] "01" "01" "01" "01" ...
 $ ORD       : num [1:668] 1 2 3 4 5 6 7 8 9 10 ...
 $ STARTROIDS: num [1:668] 1 1 1 1 1 ...
 $ ENDROIDS  : num [1:668] 1 1 1 1 1 ...
 $ STEROIDS  : chr [1:668] "Steroids" "Steroids" "Steroids" "Steroids" ...

Edit 1: Example datasets added

#onoff dataset
#most subjects will have 2 entries (1. on study/2. off study/alive), unless they passed while on study

SUBJECT <- c("01", "01", "02", "02", "03", "03", "04","04", "05", "05")
RESPONSE <- c("On Study", "Off Study/Alive", "On Study", "Off Study/Alive", "On Study", "Off Study/Alive", "On Study", "Off Study/Alive", "On Study", "Off Study/Alive")
STARTRESP <- c(0, 50, 0, 25, 0, 5, 0, 12, 0, 22)
ENDRESP <- c(50, 70, 25, 50, 5, 10, 12, 18, 22, 35)
ALIVE <- c(0, 0, 0, 0, 0, 0, 0, 0, 1, 1)

onoff <- data.frame(SUBJECT, RESPONSE, STARTRESP, ENDRESP, ALIVE)

#response dataset
#response should generally occur during the "on study" period
SUBJECT <- c("01", "01", "01", "01", "02", "02", "03", "03", "04", "05", "05", "05")
RESPONSE <- c("Stable Disease", "Partial Response", "Complete Response", "Progressive Disease", "Stable Disease", "Partial Response", "Progressive Disease", "Stable Disease", "Progressive Disease", "Stable Disease", "Progressive Disease", "Stable Disease", "Partial Response", "Complete Response", "Progressive Disease")
STARTRESP <- c(0, 4, 8, 48, 0, 6, 24, 0, 4, 0, 11, 0, 6, 13, 18)
ENDRESP <- c(4, 8, 48, 50, 6, 24, 25, 4, 5, 11, 12, 6, 13, 18, 22)

response <- data.frame(SUBJECT, RESPONSE, STARTRESP, ENDRESP)

#steroids dataset
#not all subjects get steroids
SUBJECT <- c("01", "01", "04")
STARTROIDS <- c(10, 56, 4)
ENDROIDS <- c(18, 68, 10)
STEROIDS <- c("Steroids", "Steroids", "Steroids")

steroids <- data.frame(SUBJECT, STARTROIDS , ENDROIDS, STEROIDS)


#bev dataset
#not all subjects get bev
SUBJECT <- c("01", "01", "04")
STARTBEV <- c(12, 60, 6)
ENDBEV <- c(18, 70, 10)
BEV <- c("Bevacizumab", "Bevacizumab", "Bevacizumab")

bev <- data.frame(SUBJECT, STARTBEV , ENDBEV, BEV)


#dose dataset
#added repeat dosing and different levels to make comparable to real world data
SUBJECT <- c("01", "01", "02", "03", "04", "05", "05")
TREATMENT <- c(50000, 10000, 12000, 10000, 10000, 50000)
DUR <- c(0, 12, 0, 0, 0, 34)

dose <- data.frame(SUBJECT, TREATMENT, DUR)



Code:

#Prespecified color palettes
response_color <- c("Stable Disease" = "gold",
                    "Partial Response" = "blue2",
                    "Complete Response" = "green4",
                    "Progressive Disease" = "red1")
                    
        
onoff_study_color <- c("On Study" = "lightblue",
                    "Off Study/Alive" = "gray75")

steroidbev_color <- c("Steroids" = "orangered",
                    "Bevacizumab" = "mediumorchid4")

#ggplot code
     ggplot() +
     
     theme_classic() +
     
     scale_colour_manual(name = "On/Off Study", values = onoff_study_color) +
     
     geom_vline(xintercept = 6, colour = "gray69", linetype = "longdash",
                show.legend = FALSE) +
     
     geom_segment(data = onoff, aes(x = STARTRESP, xend = ENDRESP, 
                                               y = SUBJECT, yend = SUBJECT,
                                               colour = RESPONSE),
                  size = 5, show.legend = TRUE, 
                  key_glyph = "label") +
     
     new_scale_color() + #from the 'ggnewscale' package
     
     scale_colour_manual(name = "Response", values = response_color) +
     
     geom_segment(data = response, aes(x = STARTRESP, xend = ENDRESP, 
                                              y = SUBJECT, yend = SUBJECT,
                                              colour = RESPONSE),
                  size = 2, show.legend = TRUE,
                  key_glyph = "label") +
     
     geom_point(data = response, aes(x = STARTRESP, y = SUBJECT, 
                                            colour = RESPONSE),
                shape = 21, size = 2.5, fill = "white", show.legend = FALSE) +
     
     geom_point(data = response, aes(x = ENDRESP, y = SUBJECT, 
                                            colour = RESPONSE),
                shape = 21, size = 2.5, fill = "white", show.legend = FALSE) +
     
     new_scale_color() + #from the 'ggnewscale' package
     
     scale_colour_manual(name = "Steroid/Bevacizumab Use", values = steroidbev_color) +
     
     geom_segment(data = steroids, aes(x = STARTROIDS, xend = ENDROIDS, 
                                                  y = SUBJECT, yend = SUBJECT,
                                                  colour = STEROIDS),
                  
                  size = 1, position = position_nudge(y = 0.15), linetype = 1, show.legend = TRUE,
                  key_glyph = "smooth") +
     
     geom_segment(data = bev, aes(x = STARTBEV, xend = ENDBEV, 
                                             y = SUBJECT, yend = SUBJECT,
                                             colour = BEV),
                  size = 1, position = position_nudge(y = -0.15), linetype = 1, show.legend = TRUE) +
     
     geom_point(data = dose, aes(x = DUR, y = SUBJECT, 
                                            fill = TREATMENT),
                shape = 25, size = 4, position = position_nudge(y = 0.2), show.legend = FALSE) +
     
     labs(title = "Example Swims",
          caption = "Subjects with OS of >18 mos.; Arrow indicates subject is still alive") +
     
     scale_x_continuous(name = "Duration in Months", breaks = seq(0,90,10)) +
     
     ylab(label = "Subject ID") +
     
    scale_fill_discrete() +
     
     geom_text(aes(x=DUR, y=SUBJECT, label = TREATMENT), data = dose,
               nudge_y = 0.4, nudge_x = 0, size = 3) +
     
     theme(legend.key = element_rect(fill = "white", colour = "white")) +
     
     guides(fill = FALSE)

Resulting plot:

Example Swim Plot

Edit 2: Code using gtable:

I tried to revise the code using gtable, which I am not familiar with. I was able to get the legend formatted the way that I wanted, however, now the layers are messed up and the legend is smack dab in the middle of the plot. Any advice on how to properly layer and reposition the legend?

p1 <- ggplot() +
  
        theme_classic() +
  
        scale_colour_manual(name = "On/Off Study", values = onoff_study_color,
                      guide_legend(override.aes = list(color = onoff_study_color, 
                                                               fill = onoff_study_color, 
                                                               alpha = 1,
                                                               shape = 22))) +
  
        theme(legend.key = element_rect(fill = "white", colour = "white")) +

  
        geom_vline(xintercept = 6, colour = "gray69", linetype = "longdash",
             show.legend = FALSE) +
  
        geom_segment(data = subj_18mos_onoff, aes(x = STARTRESP, xend = ENDRESP, 
                                                  y = SUBJECT, yend = SUBJECT,
                                                  colour = RESPONSE),
               size = 5, show.legend = TRUE) +
        
        labs(title = "Phase 1 GBM Swim Plots",
             subtitle = "Data from study Pro00031169",
             caption = "Subjects with OS of >18 mos.; Arrow indicates subject is still alive") +
        
        scale_x_continuous(name = "Duration in Months", breaks = seq(0,90,10)) +
        
        ylab(label = "Subject ID") 
  
  #new_scale_color() +
  
  p2 <- ggplot() +
  
        theme_classic() +
  
        scale_colour_manual(name = "Response", values = response_color, 
                             guide = guide_legend(override.aes = list(color = response_color, 
                                                                     fill = response_color, 
                                                                     alpha = 1,
                                                                     shape = 22))) +
      
        geom_segment(data = subj_18mos_resp, aes(x = STARTRESP, xend = ENDRESP, 
                                                        y = SUBJECT, yend = SUBJECT,
                                                        colour = RESPONSE),
                     size = 2, show.legend = TRUE) +
        
        geom_point(data = subj_18mos_resp, aes(x = STARTRESP, y = SUBJECT, 
                                                colour = RESPONSE),
                   shape = 21, size = 2.5, fill = "white", show.legend = FALSE) +
        
        geom_point(data = subj_18mos_resp, aes(x = ENDRESP, y = SUBJECT, 
                                                colour = RESPONSE),
                   shape = 21, size = 2.5, fill = "white", show.legend = FALSE) 
  
  #new_scale_color() +
  
  p3 <- ggplot() +
  
        theme_classic() +
  
        scale_colour_manual(name = "Steroid/Bevacizumab Use", values = steroidbev_color,
                              guide = guide_legend(override.aes = list(color = steroidbev_color, 
                                                                     fill = steroidbev_color, 
                                                                     alpha = 1,
                                                                     shape = 95,
                                                                     size = 1.5))) +
      
        geom_segment(data = subj_18mos_steroids, aes(x = STARTROIDS, xend = ENDROIDS, 
                                                      y = SUBJECT, yend = SUBJECT,
                                                      colour = STEROIDS),
                     
                     size = 1, position = position_nudge(y = 0.15), linetype = 1, show.legend = TRUE) +
        
        geom_segment(data = subj_18mos_bev, aes(x = STARTBEV, xend = ENDBEV, 
                                                 y = SUBJECT, yend = SUBJECT,
                                                 colour = BEV),
                     size = 1, position = position_nudge(y = -0.15), linetype = 1, show.legend = TRUE) +
        
        geom_point(data = subj_18mos_dose, aes(x = DUR, y = SUBJECT, 
                                                fill = TREATMENT),
                   shape = 25, size = 4, position = position_nudge(y = 0.2), show.legend = FALSE) +
        
        scale_fill_discrete() +
      
        geom_text(aes(x=DUR, y=SUBJECT, label = TREATMENT), data = subj_18mos_dose,
                  nudge_y = 0.4, nudge_x = 0, size = 3) +
        
        guides(fill = FALSE)
  
  #extract gtables
  g1 <- ggplotGrob(p1)
  g2 <- ggplotGrob(p2)
  g3 <- ggplotGrob(p3)
  
  #get legends
  leg1 = g1$grobs[[which(g1$layout$name == "guide-box")]]
  leg2 = g2$grobs[[which(g2$layout$name == "guide-box")]] 
  leg3 = g3$grobs[[which(g3$layout$name == "guide-box")]] 

  # Join them into one legend
  leg = rbind(leg1, leg2, leg3, size = "first")
  
  # Drop the legends from the two gtables
  pos = subset(g1$layout, grepl("guide-box", name), l)
  g1 = g1[, -pos$l]
  g2 = g2[, -pos$l]
  g3 = g3[, -pos$l]
  
  # Get the location of the plot panel in g1.
  # These are used later when transformed elements of g2 are put back into g1
  pp <- c(subset(g1$layout, name == "panel", se = t:r))
  #pp2 <- c(subset(g2$layout, name == "panel", se = t:r))
  #pp3 <- c(subset(g3$layout, name == "panel", se = t:r))

  # Overlap panel for second plot on that of the first plot
  g1 <- gtable_add_grob(g1, g2$grobs[[which(g2$layout$name == "panel")]], pp$t, pp$l, pp$b, pp$l)
  g1 <- gtable_add_grob(g1, g3$grobs[[which(g3$layout$name == "panel")]], pp$t, pp$l, pp$b, pp$l)
  
  grid.draw(g1)
  grid.draw(leg)
  
  SwimmerPlot_18mos_full <- grid.grab()
  
  SwimmerPlot_18mos_full
  
#ggsave("SwimmerPlot_18mos_full.png", plot = SwimmerPlot_18mos_full, width = 10, 
#      height = 10, dpi = 320)

Resulting Plot:gtable code plot

0

There are 0 best solutions below