Skip to content

guide_legend() fails to render vector shapes when overriding aesthetics #6624

@nebetbastet

Description

@nebetbastet

Hi,

Here is my code:

rm(list=ls())
shape_events=c(EV = 4, CC = 17, WW = 19, ZZ = 19)
library(ggplot2)
library(dplyr)

res<- structure(list(USUBJID = c("D", "D",
                                 "D", "D", "D", "D",
                                 "D", "D", "B", "B",
                                 "B", "B", "B", "B",
                                 "B", "B", "B", "B",
                                 "B", "B", "B", "B",
                                 "B", "B", "E", "E",
                                 "E", "E", "E", "E",
                                 "E", "E", "E", "E",
                                 "E", "E", "E", "E",
                                 "E", "E", "A", "A",
                                 "A", "A", "A", "A",
                                 "A", "A", "C", "C",
                                 "C", "C", "C", "C",
                                 "C", "C", "C", "C"
), TRTSTART = c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
                1), TRTEND = c(43, 43, 43, 43, 43, 43, 43, 43, 98, 98, 98, 98,
                               98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 100, 100, 100,
                               100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
                               30, 30, 30, 30, 30, 30, 30, 30, 43, 43, 43, 43, 43, 43, 43, 43,
                               43, 43), FUPEND = c(85, 85, 85, 85, 85, 85, 85, 85, 92, 92, 92,
                                                   92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, NA, NA, NA,
                                                   NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,
                                                   NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
ARM = structure(c(4L, 2L, 4L, 2L, 4L, 4L, 2L, 2L, 3L, 2L,
                  3L, 2L, 3L, 2L, 3L, 3L, 3L, 3L, 3L, 2L, 2L, 2L, 2L, 2L, 3L,
                  2L, 3L, 2L, 3L, 2L, 3L, 3L, 3L, 3L, 3L, 2L, 2L, 2L, 2L, 2L,
                  1L, 2L, 1L, 2L, 1L, 1L, 2L, 2L, 1L, 1L, 2L, 2L, 1L, 2L, 1L,
                  1L, 2L, 2L), levels = c("Y3", "Follow-up", "Y2", "Y1",
                                          "CC", "WW", "WW", "ZZ", "EV"), class = "factor"),
EVENTDUR = c(43, 43, 43, 43, 1, 22, 1, 22, 22, 22, 84, 84,
             98, 98, 43, 64, 85, 22, 1, 43, 64, 85, 22, 1, 46, 46, 85,
             85, 100, 100, 1, 23, 44, 65, 86, 1, 23, 44, 65, 86, 2, 2,
             39, 39, 1, 22, 1, 22, 2, 23, 2, 23, 43, 43, 1, 22, 1, 22),
EVENTCD = structure(c(8L, 8L, 10L, 10L, 9L, 9L, 9L, 9L, 6L,
                      6L, 8L, 8L, 10L, 10L, 9L, 9L, 9L, 9L, 9L, 9L, 9L, 9L, 9L,
                      9L, 7L, 7L, 8L, 8L, 10L, 10L, 9L, 9L, 9L, 9L, 9L, 9L, 9L,
                      9L, 9L, 9L, 5L, 5L, 8L, 8L, 9L, 9L, 9L, 9L, 6L, 6L, 6L, 6L,
                      10L, 10L, 9L, 9L, 9L, 9L), levels = c("Y3", "Follow-up",
                                                            "Y2", "Y1", "CC", "WW", "WW", "ZZ", "EV",
                                                            "EOT"), class = "factor"), ORDER = structure(c(1L, 1L, 1L,  1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L,    3L, 3L, 3L, 3L, 3L, 3L, 3L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 5L, 5L, 5L, 5L, 5L, 5L, 5L, 5L, 5L, 5L), 
levels = c("1", "2", "3", "4", "5"), class = "factor"), 
Legend_Group = structure(c(10L,                                                                                                                                                          10L, 1L, 2L, 5L, 5L, 5L, 5L, 11L, 11L, 10L, 10L, 3L, 2L, 5L, 5L, 5L, 5L, 5L, 5L, 5L, 5L, 5L, 5L, 9L, 9L, 10L, 10L, 3L, 2L, 5L, 5L, 5L, 5L, 5L, 5L, 5L, 5L, 5L, 5L, 6L, 6L, 10L,10L, 5L, 5L, 5L, 5L, 11L, 11L, 11L, 11L, 4L, 2L, 5L, 5L,                                                                               5L, 5L), levels = c("Y1", "Follow-up", "Y2", "Y3", "EV",                                                                                                                                                                "CC", "CC beyond CC-period", "CC", "WW", "ZZ", "WW" ), class = "factor")), row.names = c("47", "471", "44", "441",
                                                                                                                                                                                                                                                                       "45", "46", "451", "461", "11", "111", "17", "171", "10", "101",
                                                                                                                                                                                                                                                                       "12", "13", "14", "15", "16", "121", "131", "141", "151", "161",
                                                                                                                                                                                                                                                                       "70", "701", "69", "691", "63", "631", "64", "65", "66", "67",
                                                                                                                                                                                                                                                                       "68", "641", "651", "661", "671", "681", "6", "610", "9", "91",
                                                                                                                                                                                                                                                                       "7", "8", "710", "83", "40", "41", "401", "411", "39", "391",
                                                                                                                                                                                                                                                                       "42", "43", "421", "431"), class = "data.frame")




cols_segment=NULL
cols_events=c("EV" = "black","CC" = "#a114a8","WW" = "#4634eb","ZZ" = "#E61A33")



  ### Graphical parameters #####
  arms<-unique(res$ARM)
  yores<-arms[arms!="Follow-up"]

  # colors
  cols_yores<- c(Y1 = "#CCCCFF", Y2 = "#33CCFF", Y3 = "#6666FF")
  cols_segment<-c(cols_yores,"Follow-up" = "grey")

  cols<-c(cols_segment,cols_events)

  # shapes
  shape_yores<-rep(NA,length(arms)) %>% setNames(arms)
  shape_override <- c(shape_yores, shape_events)

  # segments
  shape_segments<-rep(1,length(arms)) %>% setNames(arms)
  segment_events<-rep(NA,length(shape_events)) %>% setNames(names(shape_events))
  segment_override<-c(shape_segments,segment_events)

  # stroke
  stroke_segments<-rep(NA,length(arms)) %>% setNames(arms)
  stroke_events<-rep(2,length(shape_events)) %>% setNames(names(shape_events))
  stroke_override<-c(stroke_segments,stroke_events)

  # size
  size_segments<-rep(2,length(arms)) %>% setNames(arms)
  size_events<-rep(5,length(shape_events)) %>% setNames(names(shape_events))
  size_override<-c(size_segments,size_events)


  # Ordonner pour que les infusions soient à la fin (donc CCoix devant les ronds et carrés)
  res$EVENTCD<-droplevels(res$EVENTCD)
  levels<-levels(res$EVENTCD) %>% .[.!="EV"] %>% c(.,"EV")
  res$EVENTCD<- factor(res$EVENTCD,levels=levels)
  res<-res[order(res$EVENTCD),]

  geometrics<-unique(c(as.character(res$ARM), levels))
  cols<-cols[geometrics] %>% .[!is.na(names(.))]
  shape_override<-shape_override[geometrics]  %>% .[!is.na(names(.))]
  segment_override<-segment_override[geometrics]  %>% .[!is.na(names(.))]
  stroke_override<-segment_override[geometrics]  %>% .[!is.na(names(.))]
  size_override<-size_override[geometrics]  %>% .[!is.na(names(.))]

  # Formatting ######
  # je suis obligée de faire ça pour avoir une légende "fusionnée"
  # ajouter EV et ZZ dans les "levels" de ARM
  res$ARM<-factor(res$ARM, levels=names(cols))
  res$EVENTCD<-factor(res$EVENTCD, levels=c(names(cols),"EOT"))
  res <- res[order(res$ORDER),]

  # Separating follow up and non follow up data
  # for bars
  res_bar<-res[(res$USUBJID %in% res$USUBJID[res$EVENTCD == "EOT"]),] %>%
    .[.$ARM != "Follow-up",]
  # for arrows
  res_arrow<-res[(!res$USUBJID %in% res$USUBJID[res$EVENTCD == "EOT"]),]%>%
    .[.$ARM != "Follow-up",]

  # max days
  maxDays<- max(res$TRTEND + res$FUPEND, na.rm = T) + 10
  if (!is.finite(maxDays)) {
    maxDays<-max(res$TRTEND, na.rm=TRUE) + 10

  }

  p<-ggplot(res, aes(y = forcats::fct_inorder(ORDER),
                     group = forcats::fct_inorder(ORDER))) +
    geom_segment(data=res[res$ARM!="Follow-up",],
                 aes(x = TRTSTART, xend = TRTEND,   y = forcats::fct_inorder(ORDER), color = ARM),
                 linewidth = 2) +
    geom_segment(data=res[res$ARM=="Follow-up",],
                 aes(x = TRTEND, xend = TRTEND + FUPEND,   y = forcats::fct_inorder(ORDER), color = ARM),
                 linewidth = 2) +
    geom_segment(data = res_arrow,
                 aes(x = TRTEND, xend = TRTEND + 5, y = forcats::fct_inorder(ORDER), color = ARM),
                 arrow = arrow(type = "closed", length = unit(0.2, "inches")),
                 linewidth = 2, show.legend = FALSE) +  # Exclude arrow from legend
    geom_segment(data =res_bar,
                 aes(x = TRTEND, xend = TRTEND, y = forcats::fct_inorder(ORDER), color = ARM),
                 arrow = arrow(type = "open", length = unit(0.1, "inches"), angle = 90),
                 linewidth = 3, show.legend = FALSE) +  # Exclude arrow from legend
    geom_point(data = res[res$ARM!="Follow-up"&res$EVENTCD != "EOT"&!is.na(res$EVENTCD),],
               aes(x = EVENTDUR, y = forcats::fct_inorder(ORDER),
                   color = EVENTCD, shape = EVENTCD),
               size = 5, stroke = 2) +
    # Colors
    scale_color_manual(name = "Trial periods and events",
                       values = cols) +
    # Shape
    scale_shape_manual(name = "Trial periods and events",
                       values = shape_override)+
    # One legend for shape and colors
    guides(color = guide_legend(override.aes = list(stroke = stroke_override,
                                                    shape = shape_override,
                                                    linetype = segment_override,
                                                    size = size_override)),
           shape = "none") +
    # y-axis
    scale_y_discrete(name = "",
                     labels = res$USUBJID) +  # Display usubjid
    # X-axis
    scale_x_continuous(name = "Time (Days)") +  # x-axis by cycle (21 days)
    # Minimal theme
    theme_minimal() +
    # Personnalisation des axes et de la légende
    theme(title = element_text(size = 14, face = "bold"),
          axis.text.y = element_text(size = 12, face = "bold"),  # Taille de l'axe Y
          legend.position = "right",  # Placer la légende à droite
          legend.box = "vertical",  # Légende empilée verticalement
          plot.caption = element_text(margin = margin(t = 15),
                                      face = "italic",
                                      size = 8,
                                      hjust = 0),
          plot.caption.position = "plot")

p

which renders as
Image

As you can see, the cross doesn't appear in the legend... :/

Now, if I replace only the second line of my code and that I give a solid shape to "EV":

shape_events=c(EV = 15, CC = 17, WW = 19, ZZ = 19)

I get that:

Image It appears in the legend.

It doesn't make sense to me, so I guess it's a bug.

Here is my SessionInfo :


R version 4.5.1 (2025-06-13 ucrt)
Platform: x86_64-w64-mingw32/x64
Running under: Windows 11 x64 (build 26100)

Matrix products: default
  LAPACK version 3.12.1

locale:
[1] LC_COLLATE=French_France.utf8  LC_CTYPE=French_France.utf8    LC_MONETARY=French_France.utf8
[4] LC_NUMERIC=C                   LC_TIME=French_France.utf8    

time zone: Europe/Paris
tzcode source: internal

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] dplyr_1.1.4   ggplot2_4.0.0

loaded via a namespace (and not attached):
 [1] labeling_0.4.3     RColorBrewer_1.1-3 R6_2.6.1           tidyselect_1.2.1   farver_2.1.2      
 [6] magrittr_2.0.3     gtable_0.3.6       glue_1.8.0         tibble_3.3.0       pkgconfig_2.0.3   
[11] generics_0.1.4     lifecycle_1.0.4    cli_3.6.5          S7_0.2.0           scales_1.4.0      
[16] grid_4.5.1         vctrs_0.6.5        withr_3.0.2        compiler_4.5.1     forcats_1.0.0     
[21] rstudioapi_0.17.1  tools_4.5.1        pillar_1.11.0      rlang_1.1.6  

Thank you in advance for your help and correction!

EDIT : No problem with ggplot2 version 3.5.0

Image

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions