【问题标题】:How can I arrange an arbitrary number of ggplots using grid.arrange?如何使用 grid.arrange 安排任意数量的 ggplots?
【发布时间】:2011-07-13 15:10:44
【问题描述】:

这是在 ggplot2 谷歌组中交叉发布的

我的情况是我working on a function 输出任意数量的图(取决于用户提供的输入数据)。该函数返回 n 个图的列表,我想将这些图以 2 x 2 的形式排列。我正在努力解决以下同时出现的问题:

  1. 如何灵活处理任意 (n) 个地块?
  2. 如何指定我希望它们以 2 x 2 的形式布置

我当前的策略使用来自gridExtra 包的grid.arrange。这可能不是最佳的,尤其是因为这是关键,它完全不起作用。这是我注释的示例代码,尝试了三个图:

library(ggplot2)
library(gridExtra)

x <- qplot(mpg, disp, data = mtcars)
y <- qplot(hp, wt, data = mtcars)
z <- qplot(qsec, wt, data = mtcars)

# A normal, plain-jane call to grid.arrange is fine for displaying all my plots
grid.arrange(x, y, z)

# But, for my purposes, I need a 2 x 2 layout. So the command below works acceptably.
grid.arrange(x, y, z, nrow = 2, ncol = 2)

# The problem is that the function I'm developing outputs a LIST of an arbitrary
# number plots, and I'd like to be able to plot every plot in the list on a 2 x 2
# laid-out page. I can at least plot a list of plots by constructing a do.call()
# expression, below. (Note: it totally even surprises me that this do.call expression
# DOES work. I'm astounded.)
plot.list <- list(x, y, z)
do.call(grid.arrange, plot.list)

# But now I need 2 x 2 pages. No problem, right? Since do.call() is taking a list of
# arguments, I'll just add my grid.layout arguments to the list. Since grid.arrange is
# supposed to pass layout arguments along to grid.layout anyway, this should work.
args.list <- c(plot.list, "nrow = 2", "ncol = 2")

# Except that the line below is going to fail, producing an "input must be grobs!"
# error
do.call(grid.arrange, args.list)

按照我的习惯,我谦虚地蜷缩在角落里,热切地等待一个比我聪明得多的社区的明智反馈。尤其是如果我让这变得比需要的更难的话。

【问题讨论】:

  • 对一个非常出色的问题表示敬意。我将以此为例说明如何编写一个好的 SO [r] 问题。
  • 尤其是“谦逊地挤在一起”的部分——没有什么比得上一个好卑鄙的人了 :-)
  • @JD 和@Ben - 我很受宠若惊,伙计们。真挚地。我非常感谢您的帮助。

标签: r ggplot2


【解决方案1】:

你快到了!问题是 do.call 期望您的 args 位于名为 list 的对象中。您已将它们放在列表中,但作为字符串,而不是命名列表项。

我认为这应该可行:

args.list <- c(plot.list, 2,2)
names(args.list) <- c("x", "y", "z", "nrow", "ncol")

正如 Ben 和 Joshua 在 cmets 中指出的那样,我可以在创建列表时指定名称:

args.list <- c(plot.list,list(nrow=2,ncol=2))

args.list <- list(x=x, y=y, z=x, nrow=2, ncol=2)

【讨论】:

  • 我更改了几次代码。抱歉编辑。现在有意义吗?当我之前说它们是矢量时,我说错了。对此感到抱歉。
  • 您可以在创建列表时命名参数:args.list &lt;- list(x=x, y=y, z=x, nrow=2, ncol=2)
  • 不完全是。你的长度合适。你的list结构和JD的list结构不一样。使用 str() 和 names()。您的所有列表元素都未命名,因此要使 do.call 成功,需要进行精确的位置匹配。
  • @JD Long;我由衷地同意。即使它不能阻止所有错误,如果您使用命名参数,您仍然可以获得更好的错误消息和traceback() 信息。
  • 我不太关注这里的讨论;因为grid.arrange() 的第一个参数是...,所以位置匹配可能无关紧要。每个输入必须是网格对象(有或没有名称)、grid.layout 的命名参数或其余参数的命名参数。
【解决方案2】:

试试这个,

require(ggplot2)
require(gridExtra)
plots <- lapply(1:11, function(.x) qplot(1:10,rnorm(10), main=paste("plot",.x)))

params <- list(nrow=2, ncol=2)

n <- with(params, nrow*ncol)
## add one page if division is not complete
pages <- length(plots) %/% n + as.logical(length(plots) %% n)

groups <- split(seq_along(plots), 
  gl(pages, n, length(plots)))

pl <-
  lapply(names(groups), function(g)
         {
           do.call(arrangeGrob, c(plots[groups[[g]]], params, 
                                  list(main=paste("page", g, "of", pages))))
         })

class(pl) <- c("arrangelist", "ggplot", class(pl))
print.arrangelist = function(x, ...) lapply(x, function(.x) {
  if(dev.interactive()) dev.new() else grid.newpage()
   grid.draw(.x)
   }, ...)

## interactive use; open new devices
pl

## non-interactive use, multipage pdf
ggsave("multipage.pdf", pl)

【讨论】:

  • version >= 0.9 of gridExtra 提供了 marrangeGrob 来自动执行所有这些操作,只要 nrow*ncol
  • ggsave("multipage.pdf", do.call(marrangeGrob, c(plots, list(nrow=2, ncol=2))))
【解决方案3】:

我回答得有点晚了,但在 R Graphics Cookbook 中偶然发现了一个解决方案,该解决方案使用名为 multiplot 的自定义函数执行了非常相似的操作。也许它会帮助其他找到这个问题的人。我也在添加答案,因为该解决方案可能比该问题的其他答案更新。

Multiple graphs on one page (ggplot2)

这里是当前函数,虽然请使用上面的链接,因为作者指出它已经更新为 ggplot2 0.9.3,这表明它可能会再次改变。

# Multiple plot function
#
# ggplot objects can be passed in ..., or to plotlist (as a list of ggplot objects)
# - cols:   Number of columns in layout
# - layout: A matrix specifying the layout. If present, 'cols' is ignored.
#
# If the layout is something like matrix(c(1,2,3,3), nrow=2, byrow=TRUE),
# then plot 1 will go in the upper left, 2 will go in the upper right, and
# 3 will go all the way across the bottom.
#
multiplot <- function(..., plotlist=NULL, file, cols=1, layout=NULL) {
  require(grid)

  # Make a list from the ... arguments and plotlist
  plots <- c(list(...), plotlist)

  numPlots = length(plots)

  # If layout is NULL, then use 'cols' to determine layout
  if (is.null(layout)) {
    # Make the panel
    # ncol: Number of columns of plots
    # nrow: Number of rows needed, calculated from # of cols
    layout <- matrix(seq(1, cols * ceiling(numPlots/cols)),
                    ncol = cols, nrow = ceiling(numPlots/cols))
  }

 if (numPlots==1) {
    print(plots[[1]])

  } else {
    # Set up the page
    grid.newpage()
    pushViewport(viewport(layout = grid.layout(nrow(layout), ncol(layout))))

    # Make each plot, in the correct location
    for (i in 1:numPlots) {
      # Get the i,j matrix positions of the regions that contain this subplot
      matchidx <- as.data.frame(which(layout == i, arr.ind = TRUE))

      print(plots[[i]], vp = viewport(layout.pos.row = matchidx$row,
                                      layout.pos.col = matchidx$col))
    }
  }
}

创建绘图对象:

p1 <- ggplot(...)
p2 <- ggplot(...)
# etc.

然后将它们传递给multiplot:

multiplot(p1, p2, ..., cols = n)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多