这是在ggplot2 google群组上的交叉发布
我的情况是我working on a function输出任意数量的图(取决于用户提供的输入数据)。该函数返回一个n个图的列表,我想将这些图以2 x 2的形式排列。我正在努力解决同时出现的问题:
我当前的策略使用grid.arrange
包中的gridExtra
。它可能不是最佳的,特别是因为,这是关键,它完全不起作用。这是我评论的示例代码,试验了三个图:
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)
正如我常常做的那样,我谦卑地蜷缩在角落里,急切地等待着一个比我更聪明的社区的睿智反馈。特别是如果我做得比你需要的更难。
答案 0 :(得分:45)
你几乎就到了!问题是do.call
期望您的args位于命名的list
对象中。您已将它们放在列表中,但作为字符串,而不是命名列表项。
我认为这应该有效:
args.list <- c(plot.list, 2,2)
names(args.list) <- c("x", "y", "z", "nrow", "ncol")
Ben和Joshua在评论中指出,我可以在创建列表时指定名称:
args.list <- c(plot.list,list(nrow=2,ncol=2))
或
args.list <- list(x=x, y=y, z=x, nrow=2, ncol=2)
答案 1 :(得分:16)
试试这个,
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)
答案 2 :(得分:4)
我回答得有点迟了,但在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)