在facet_wrap图中添加“floating”轴标签

44

我和这位用户有同样的问题 - 我有一个“参差不齐”的分面图,底部行比其他行少一些面板,并且我希望在每列底部有x轴刻度。

针对该问题的建议解决方法是设置scales="free_x"。(在ggplot 0.9.2.1中;我相信我要寻找的行为在早期版本中是默认的。)但是在我这种情况下,这是一个糟糕的解决方案:我的实际轴标签会相当长,因此将它们放在每一行的下方会占用太多空间。结果类似于这样:

 x <- gl(3, 1, 15, labels=paste("this is a very long axis label ", letters[1:5]))
 y <- rnorm(length(x))
 l <- gl(5, 3, 15)
 d <- data.frame(x=x, y=y, l=l)
 ggplot(d, aes(x=x, y=y)) + geom_point() + facet_wrap(~l, scales="free_x") + 
   theme(axis.text.x=element_text(angle=90, hjust=1))

enter image description here

这里的评论中,Andrie建议可以在grid中手动完成,但我不知道如何开始。


这样对吗?:您希望仅在面板的底部行上具有x轴标签,并且您希望这些标签延伸到第二行第三列的空面板空间中。 - metasequoia
2
问题现在已经在ggplot2的开发版本中得到了修复。请参阅ggplot2问题#1607:X轴不会出现在具有不均匀数量的facet_wrap图下方 - Paul Rougieux
1个回答

61
如果我没记错的话,有人问如何将所有标签添加到最后一列下的同一行以及如何将这些最后的标签提升到下一行。所以这里是两种情况的函数: 编辑:由于这类似于print.ggplot的替代品(请参见getAnywhere(print.ggplot)),因此我已经添加了一些代码来保留功能。 编辑2:我进一步改进了它:不再需要指定nrowncol,也可以打印具有所有面板的图形。
library(grid)
# pos - where to add new labels
# newpage, vp - see ?print.ggplot
facetAdjust <- function(x, pos = c("up", "down"), 
                        newpage = is.null(vp), vp = NULL)
{
  # part of print.ggplot
  ggplot2:::set_last_plot(x)
  if(newpage)
    grid.newpage()
  pos <- match.arg(pos)
  p <- ggplot_build(x)
  gtable <- ggplot_gtable(p)
  # finding dimensions
  dims <- apply(p$panel$layout[2:3], 2, max)
  nrow <- dims[1]
  ncol <- dims[2]
  # number of panels in the plot
  panels <- sum(grepl("panel", names(gtable$grobs)))
  space <- ncol * nrow
  # missing panels
  n <- space - panels
  # checking whether modifications are needed
  if(panels != space){
    # indices of panels to fix
    idx <- (space - ncol - n + 1):(space - ncol)
    # copying x-axis of the last existing panel to the chosen panels 
    # in the row above
    gtable$grobs[paste0("axis_b",idx)] <- list(gtable$grobs[[paste0("axis_b",panels)]])
    if(pos == "down"){
      # if pos == down then shifting labels down to the same level as 
      # the x-axis of last panel
      rows <- grep(paste0("axis_b\\-[", idx[1], "-", idx[n], "]"), 
                   gtable$layout$name)
      lastAxis <- grep(paste0("axis_b\\-", panels), gtable$layout$name)
      gtable$layout[rows, c("t","b")] <- gtable$layout[lastAxis, c("t")]
    }
  }
  # again part of print.ggplot, plotting adjusted version
  if(is.null(vp)){
    grid.draw(gtable)
  }
  else{
    if (is.character(vp)) 
      seekViewport(vp)
    else pushViewport(vp)
    grid.draw(gtable)
    upViewport()
  }
  invisible(p)
}

这是它的外观

d <- ggplot(diamonds, aes(carat, price, fill = ..density..)) +
  xlim(0, 2) + stat_binhex(na.rm = TRUE) + theme(aspect.ratio = 1) + 
  facet_wrap(~ color)
facetAdjust(d)

enter image description here

facetAdjust(d, "down")

enter image description here

编辑3:

这是一种替代方案,上面的方案也不错。

当使用ggsavefacetAdjust时会出现一些问题。需要一个ggplot类的绘图,因为在ggsave源代码的两个部分中都有print(plot)default_name(plot)。如果没有手动提供文件名(根据?ggsave,它似乎不应该工作),则需要给定一个类别。因此,给定一个文件名,有一个解决方法(可能在某些情况下会产生副作用):

首先,让我们考虑实现浮动轴的单独函数。通常,它会返回一个gtable对象,但是我们使用class(gtable) <- c("facetAdjust", "gtable", "ggplot")。这样,就允许使用ggsaveprint(plot)按要求工作(请参见下面的print.facetAdjust)。

facetAdjust <- function(x, pos = c("up", "down"))
{
  pos <- match.arg(pos)
  p <- ggplot_build(x)
  gtable <- ggplot_gtable(p); dev.off()
  dims <- apply(p$panel$layout[2:3], 2, max)
  nrow <- dims[1]
  ncol <- dims[2]
  panels <- sum(grepl("panel", names(gtable$grobs)))
  space <- ncol * nrow
  n <- space - panels
  if(panels != space){
    idx <- (space - ncol - n + 1):(space - ncol)
    gtable$grobs[paste0("axis_b",idx)] <- list(gtable$grobs[[paste0("axis_b",panels)]])
    if(pos == "down"){
      rows <- grep(paste0("axis_b\\-[", idx[1], "-", idx[n], "]"), 
                   gtable$layout$name)
      lastAxis <- grep(paste0("axis_b\\-", panels), gtable$layout$name)
      gtable$layout[rows, c("t","b")] <- gtable$layout[lastAxis, c("t")]
    }
  }
  class(gtable) <- c("facetAdjust", "gtable", "ggplot"); gtable
}

这个打印函数与ggplot2:::print.ggplot只有几行不同:

print.facetAdjust <- function(x, newpage = is.null(vp), vp = NULL) {
  if(newpage)
    grid.newpage()
  if(is.null(vp)){
    grid.draw(x)
  } else {
    if (is.character(vp)) 
      seekViewport(vp)
    else pushViewport(vp)
    grid.draw(x)
    upViewport()
  }
  invisible(x)
}

例子:

d <- ggplot(diamonds, aes(carat, price, fill = ..density..)) +
  xlim(0, 2) + stat_binhex(na.rm = TRUE) + theme(aspect.ratio = 1) + 
  facet_wrap(~ color)
p <- facetAdjust(d) # No output
print(p) # The same output as with the old version of facetAdjust()
ggsave("name.pdf", p) # Works, a filename is necessary

非常好!你应该等一天,我打算提供悬赏。 - Drew Steen
7
@DrewSteen,哎呀...好吧,我会把这个函数当作一个奖励来考虑。我肯定会自己使用它。 - Julius Vainora
4
@Julius,这真的很棒!感谢你。你能不能请Hadley把这个功能内置在ggplot2中呢?我认为很多人都会喜欢这个... - Dominik
1
我在github.com/hadley/ggplot2上发布了一个问题:X轴在具有不均匀数量的facet_wrap图中不显示 - Paul Rougieux
2
这个问题现在已经在ggplot2的开发版本中得到了修复。我也会在上面的问题下方发布这个评论。 - Paul Rougieux
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接