Seeking workaround for gtable_add_grob code broken by ggplot 2.2.0

前端 未结 2 1180
我寻月下人不归
我寻月下人不归 2020-11-30 07:34

In plots with multiple facet variables, ggplot2 repeats the facet label for the \"outer\" variable, rather than having a single spanning facet strip across all the levels of

2条回答
  •  误落风尘
    2020-11-30 08:20

    Indeed, ggplot2 v2.2.0 constructs complex strips column by column, with each column a single grob. This can be checked by extracting one strip, then examining its structure. Using your plot:

    library(ggplot2)
    library(gtable)
    library(grid)
    
    # Your data
    df = structure(list(location = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 
     1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 
     1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 
     2L, 2L), .Label = c("SF", "SS"), class = "factor"), species = structure(c(1L, 
     1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
     1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 
     2L, 2L, 2L, 2L, 2L, 2L, 2L), .Label = c("AGR", "LKA"), class = "factor"), 
        position = structure(c(1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 
        2L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 
        1L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 
        2L), .Label = c("top", "bottom"), class = "factor"), density = c(0.41, 
        0.41, 0.43, 0.33, 0.35, 0.43, 0.34, 0.46, 0.32, 0.32, 0.4, 
        0.4, 0.45, 0.34, 0.39, 0.39, 0.31, 0.38, 0.48, 0.3, 0.42, 
        0.34, 0.35, 0.4, 0.38, 0.42, 0.36, 0.34, 0.46, 0.38, 0.36, 
        0.39, 0.38, 0.39, 0.39, 0.39, 0.36, 0.39, 0.51, 0.38)), .Names = c("location", 
       "species", "position", "density"), row.names = c(NA, -40L), class = "data.frame")
    
    # Your ggplot with three facet levels
    p=ggplot(df, aes("", density)) + 
      geom_boxplot(width=0.7, position=position_dodge(0.7)) + 
      theme_bw() +
      facet_grid(. ~ species + location +  position) +
      theme(panel.spacing=unit(0,"lines"),
         strip.background=element_rect(color="grey30", fill="grey90"),
         panel.border=element_rect(color="grey90"),
         axis.ticks.x=element_blank()) +
      labs(x="")
    
    # Get the ggplot grob
    pg = ggplotGrob(p)
    
    # Get the left most strip
    index = which(pg$layout$name == "strip-t-1")
    strip1 = pg$grobs[[index]]
    
    # Draw the strip
    grid.newpage()
    grid.draw(strip1)
    
    # Examine its layout
    strip1$layout
    gtable_show_layout(strip1)
    

    One crude way to get outer strip labels 'spanning' inner labels is to construct the strip from scratch:

    # Get the strips, as a list, from the original plot
    strip = list()
    for(i in 1:8) {
       index = which(pg$layout$name == paste0("strip-t-",i))
       strip[[i]] = pg$grobs[[index]]
    }
    
    # Construct gtable to contain the new strip
    newStrip  = gtable(widths = unit(rep(1, 8), "null"), heights = strip[[1]]$heights)
    
    ## Populate the gtable    
    # Top row
    for(i in 1:2) {
       newStrip = gtable_add_grob(newStrip, strip[[4*i-3]][1], 
               t = 1, l = 4*i-3, r = 4*i)
    }
    
    # Middle row
    for(i in 1:4){
       newStrip = gtable_add_grob(newStrip, strip[[2*i-1]][2], 
             t = 2, l = 2*i-1, r = 2*i)
    }
    
    # Bottom row
    for(i in 1:8) {
       newStrip = gtable_add_grob(newStrip, strip[[i]][3], 
           t = 3, l = i)
    }
    
    # Put the strip into the plot 
    # (It could be better to remove the original strip. 
    # In this case, with a coloured background, it doesn't matter)
    pgNew = gtable_add_grob(pg, newStrip, t = 7, l = 5, r = 19)
    
    # Draw the plot
    grid.newpage()
    grid.draw(pgNew)
    

    OR using vectorised gtable_add_grob (see the comments):

    pg = ggplotGrob(p)
    
    # Get a list of strips from the original plot
    strip = lapply(grep("strip-t", pg$layout$name), function(x) {pg$grobs[[x]]})
    
    # Construct gtable to contain the new strip
    newStrip  = gtable(widths = unit(rep(1, 8), "null"), heights = strip[[1]]$heights)
    
    ## Populate the gtable    
    # Top row
    cols = seq(1, by = 4, length.out = 2)
    newStrip = gtable_add_grob(newStrip, lapply(strip[cols], `[`, 1), t = 1, l = cols, r = cols + 3)
    
    # Middle row
    cols = seq(1, by = 2, length.out = 4)
    newStrip = gtable_add_grob(newStrip, lapply(strip[cols], `[`, 2), t = 2, l = cols, r = cols + 1)
    
    # Bottom row
    newStrip = gtable_add_grob(newStrip, lapply(strip, `[`, 3), t = 3, l = 1:8)
    
    # Put the strip into the plot
    pgNew = gtable_add_grob(pg, newStrip, t = 7, l = 5, r = 19)
    
    # Draw the plot
    grid.newpage()
    grid.draw(pgNew)
    

提交回复
热议问题