ggplot2: have common facet bar in outer facet panel in 3-way plot

前端 未结 3 503
天命终不由人
天命终不由人 2020-12-11 16:30

I have the following code:

label_rev <- function(labels, multi_line = TRUE, sep = \": \") {
     label_both(rev(labels), multi_line = multi_line, sep = se         


        
3条回答
  •  渐次进展
    2020-12-11 17:17

    Based on this answer, but sufficiently different to warrant an answer of its own. Given a ggplot with multiple facets on the right margin, this answer provides a function, OverlappingStripLabels(), that takes information from the ggplot to reconstruct the strip so that the labels are overlapping. It uses gtable and grid functions to do so.

    library(ggplot2)
    library(grid)
    library(gtable)
    library(plyr)
    
    # Initial plot
    plot = ggplot(data = mtcars, aes(wt, mpg)) + geom_point() +
       facet_grid(vs + cyl ~ gear, labeller = label_both) + 
       theme_bw() +
       theme(panel.spacing=unit(.2,"lines"),
             strip.background=element_rect(color="grey30", fill="grey90"))
    
    
    ## The function to get overlapping strip labels
    OverlappingStripLabels = function(plot) {
    
    # Get the ggplot grob
    pg = ggplotGrob(plot)
    
    ### Collect some information about the strips from the plot
    # Get a list of strips
    strip = lapply(grep("strip-r", pg$layout$name), function(x) {pg$grobs[[x]]})
    
    # Number of strips
    NumberOfStrips = sum(grepl(pattern = "strip-r", pg$layout$name))
    
    # Number of columns
    NumberOfCols = length(strip[[1]])
    
    # Panel spacing
    plot_theme <- function(p) {
       plyr::defaults(p$theme, theme_get())
    }
    PanelSpacing = plot_theme(plot)$panel.spacing
    
    # Map the boundaries of the new strips
    Nlabel = vector("list", NumberOfCols)
    map = vector("list", NumberOfCols)
    for(i in 1:NumberOfCols) {
    
      for(j in 1:NumberOfStrips) {
       Nlabel[[i]][j] = getGrob(grid.force(strip[[j]]$grobs[[i]]), gPath("GRID.text"), grep = TRUE)$label
      }
    
    map[[i]][1] = TRUE
    for(j in 2:NumberOfStrips) {
       map[[i]][j] = Nlabel[[i]][j] != Nlabel[[i]][j-1]
       }
    }
    
    ## Construct gtable to contain the new strip
    newStrip  = gtable(heights = unit.c(rep(unit.c(unit(1, "null"), PanelSpacing), NumberOfStrips-1), unit(1, "null")), 
                       widths = strip[[1]]$widths)
    
    ## Populate the gtable  
    seqTop = list()
    for(i in NumberOfCols:1) {  
       Top = which(map[[i]] == TRUE)
       seqTop[[i]] = if(i == NumberOfCols) 2*Top - 1 else  sort(unique(c(seqTop[[i+1]], 2*Top - 1)))  
       seqBottom = c(seqTop[[i]][-1] -2, (2*NumberOfStrips-1))
       newStrip = gtable_add_grob(newStrip, lapply(strip[(seqTop[[i]]+1)/2], function(x) x[[1]][[i]]), l = i, t = seqTop[[i]], b = seqBottom)
    }
    
    ## Put the strip into the plot
    # Get the locations of the original strips
    pos = subset(pg$layout, grepl("strip-r", pg$layout$name), t:r)
    
    ## Use these to position the new strip
    pgNew = gtable_add_grob(pg, newStrip, t = min(pos$t), l = unique(pos$l), b = max(pos$b))
    
    return(pgNew)
    }
    
    ## Draw the plot
    grid.newpage()
    grid.draw(OverlappingStripLabels(plot))
    

提交回复
热议问题