【问题标题】:ggplot2: have common facet bar in outer facet panel in 3-way plotggplot2:在三向图中的外部小平面面板中有共同的小平面栏
【发布时间】:2016-09-30 22:37:09
【问题描述】:

我有以下代码:

label_rev <- function(labels, multi_line = TRUE, sep = ": ") {
     label_both(rev(labels), multi_line = multi_line, sep = sep)
  }
require(ggplot2)
p <- ggplot(data = mtcars, aes(wt, mpg)) + geom_point()
p + facet_grid(vs + cyl ~ gear, labeller = label_rev)

我得到下图:

这是我的两难选择:我希望 vs:0 的外层只是一个包含三个面(cyl:4、6、8)的面板,而 vs:1 的外层是一个包含三个面的面板(圆柱:4、6、8)。

是否可以使用 ggplot2 做到这一点?

再次提前感谢您的帮助!

【问题讨论】:

  • 第 1 部分是 order nested facet labels 的副本。第 2 部分是我也想要很久的东西......
  • 欺骗的例子惊人地相似!这个问题和欺骗都做出了非常规的选择aes(mpg, wt),而不是更常见的aes(wt, mpg)。骗子使用cyl + am ~ vs 进行刻面,而您使用vs + cyl ~ gear。这是我所见过的最接近的一个相同的骗子,而不仅仅是转发。
  • 无论如何,由于订购问题已得到解答,我建议编辑只询问第 2 部分。我很确定那里也有一个骗局,但我找不到它现在,它可能已经很老了 - 可能是 cowplot 或其他最近的创新使它更容易。
  • 编辑删除欺骗,谢谢!我仍然想找出一种方法来做公共面板。
  • 跨越分面标签没有内置到 ggplot 中(虽然我希望它们是),但是可以通过一些额外的编码来创建它们。看看this SO answer中的最后一个例子。

标签: r ggplot2


【解决方案1】:

现在可以使用ggh4x 包中的facet_nested() 轻松完成此操作

library(ggplot2)
library(ggh4x)
p <- ggplot(data = mtcars, aes(wt, mpg)) + geom_point()
p + 
  facet_nested(vs + cyl ~ am + gear, labeller = label_both) +
  theme(panel.spacing = unit(0,"line")) 

reprex package (v0.3.0) 于 2020-03-25 创建

【讨论】:

    【解决方案2】:

    我冒昧地编辑和概括了 Sandy Muspratt 在此给出的函数,以便它允许双向嵌套构面,以及如果在 facet_grid() 中指定了 labeller=label_parsed,则表达式作为构面标题。

    library(ggplot2)
    library(grid)
    library(gtable)
    library(plyr)    
    
    ## 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
      stripr = lapply(grep("strip-r", pg$layout$name), function(x) {pg$grobs[[x]]})
    
      stript = lapply(grep("strip-t", pg$layout$name), function(x) {pg$grobs[[x]]})
    
      # Number of strips
      NumberOfStripsr = sum(grepl(pattern = "strip-r", pg$layout$name))
      NumberOfStripst = sum(grepl(pattern = "strip-t", pg$layout$name))
    
      # Number of columns
      NumberOfCols = length(stripr[[1]])
      NumberOfRows = length(stript[[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
      Nlabelr = vector("list", NumberOfCols)
      mapr = vector("list", NumberOfCols)
      for(i in 1:NumberOfCols) {
    
        for(j in 1:NumberOfStripsr) {
          Nlabelr[[i]][j] = getGrob(grid.force(stripr[[j]]$grobs[[i]]), gPath("GRID.text"), grep = TRUE)$label
        }
    
        mapr[[i]][1] = TRUE
        for(j in 2:NumberOfStripsr) {
          mapr[[i]][j] = as.character(Nlabelr[[i]][j]) != as.character(Nlabelr[[i]][j-1])#Nlabelr[[i]][j] != Nlabelr[[i]][j-1]
        }
      }
    
      # Map the boundaries of the new strips
      Nlabelt = vector("list", NumberOfRows)
      mapt = vector("list", NumberOfRows)
      for(i in 1:NumberOfRows) {
    
        for(j in 1:NumberOfStripst) {
          Nlabelt[[i]][j] = getGrob(grid.force(stript[[j]]$grobs[[i]]), gPath("GRID.text"), grep = TRUE)$label
        }
    
        mapt[[i]][1] = TRUE
        for(j in 2:NumberOfStripst) {
          mapt[[i]][j] = as.character(Nlabelt[[i]][j]) != as.character(Nlabelt[[i]][j-1])#Nlabelt[[i]][j] != Nlabelt[[i]][j-1]
        }
      }
    
    
      ## Construct gtable to contain the new strip
      newStripr  = gtable(heights = unit.c(rep(unit.c(unit(1, "null"), PanelSpacing), NumberOfStripsr-1), unit(1, "null")), 
                         widths = stripr[[1]]$widths)
      ## Populate the gtable  
      seqTop = list()
      for(i in NumberOfCols:1) {  
        Top = which(mapr[[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*NumberOfStripsr-1))
        newStripr = gtable_add_grob(newStripr, lapply(stripr[(seqTop[[i]]+1)/2], function(x) x[[1]][[i]]), l = i, t = seqTop[[i]], b = seqBottom)
      }
    
      mapt <- mapt[NumberOfRows:1]
      Nlabelt <- Nlabelt[NumberOfRows:1]
      ## Do the same for top facets
      newStript  = gtable(heights = stript[[1]]$heights,
                          widths = unit.c(rep(unit.c(unit(1, "null"), PanelSpacing), NumberOfStripst-1), unit(1, "null")))
      seqTop = list()
      for(i in NumberOfRows:1) {  
        Top = which(mapt[[i]] == TRUE)
        seqTop[[i]] = if(i == NumberOfRows) 2*Top - 1 else  sort(unique(c(seqTop[[i+1]], 2*Top - 1)))  
        seqBottom = c(seqTop[[i]][-1] -2, (2*NumberOfStripst-1))
        # newStript = gtable_add_grob(newStript, lapply(stript[(seqTop[[i]]+1)/2], function(x) x[[1]][[i]]), l = i, t = seqTop[[i]], b = seqBottom)
        newStript = gtable_add_grob(newStript, lapply(stript[(seqTop[[i]]+1)/2], function(x) x[[1]][[(NumberOfRows:1)[i]]]), t = (NumberOfRows:1)[i], l = seqTop[[i]], r = seqBottom)
      }
    
      ## Put the strip into the plot
      # Get the locations of the original strips
      posr = subset(pg$layout, grepl("strip-r", pg$layout$name), t:r)
      post = subset(pg$layout, grepl("strip-t", pg$layout$name), t:r)
    
      ## Use these to position the new strip
      pgNew = gtable_add_grob(pg, newStripr, t = min(posr$t), l = unique(posr$l), b = max(posr$b))
      pgNew = gtable_add_grob(pgNew, newStript, l = min(post$l), r = max(post$r), t=unique(post$t))
      grid.draw(pgNew)
    
      return(pgNew)
    }
    
    
    # Initial plot
    p <- ggplot(data = mtcars, aes(wt, mpg)) + geom_point() +
      facet_grid(vs + cyl ~ am + gear, labeller = label_both) +
      theme_bw() +
      theme(panel.spacing=unit(.2,"lines"),
            strip.background=element_rect(color="grey30", fill="grey90"))
    
    ## Draw the plot
    grid.newpage()
    grid.draw(OverlappingStripLabels(p))
    

    这是一个例子:

    【讨论】:

      【解决方案3】:

      基于this answer,但完全不同,足以保证自己的答案。给定一个在右边距上有多个方面的 ggplot,这个答案提供了一个函数 OverlappingStripLabels(),它从 ggplot 获取信息来重建条带,以便标签重叠。它使用gtablegrid 函数来执行此操作。

      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))
      

      【讨论】:

      • 很棒的功能,谢谢!但是您能否对其进行修改以允许以下内容:(1)两种方式嵌套的构面,以及(2)作为构面标签的表达式。即使用facet_grid(vs + cyl ~ gear + am, labeller = label_parsed)之类的东西
      猜你喜欢
      • 1970-01-01
      • 2010-12-13
      • 1970-01-01
      • 2016-12-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-31
      相关资源
      最近更新 更多