Làm cách nào để sắp xếp một số lượng ggplots tùy ý bằng cách sử dụng grid.arrange?


93

Điều này được đăng chéo trên nhóm google ggplot2

Tình huống của tôi là tôi đang làm việc trên một hàm xuất ra một số lượng ô tùy ý (tùy thuộc vào dữ liệu đầu vào do người dùng cung cấp). Hàm trả về một danh sách gồm n ô và tôi muốn sắp xếp các ô đó thành hình 2 x 2. Tôi đang vật lộn với các vấn đề đồng thời của:

  1. Làm thế nào tôi có thể cho phép tính linh hoạt được giao một (n) số ô tùy ý?
  2. Làm cách nào tôi cũng có thể chỉ định tôi muốn chúng được bố trí 2 x 2

Chiến lược hiện tại của tôi sử dụng grid.arrangetừ gridExtragói. Nó có lẽ không phải là tối ưu, đặc biệt là vì, và đây là chìa khóa, nó hoàn toàn không hoạt động . Đây là mã mẫu được nhận xét của tôi, thử nghiệm với ba âm mưu:

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)

Như tôi không muốn làm, tôi khiêm tốn thu mình trong góc, háo hức chờ đợi phản hồi nghiêm túc của một cộng đồng khôn ngoan hơn tôi rất nhiều, đặc biệt nếu tôi đang làm việc này khó hơn mức cần thiết.


2
Kudo về một câu hỏi được thực hiện RẤT tốt. Tôi sẽ sử dụng điều này như một ví dụ về cách viết một câu hỏi SO [r] tốt.
JD Dài

1
đặc biệt là "khiêm tốn co ro" phần - không có gì giống như một lăn tốt :-)
Bến Bolker

@JD và @Ben - Tôi rất tự hào, các bạn. Trân trọng. Và tôi thực sự đánh giá cao sự giúp đỡ.
briandk

Câu trả lời:


45

Bạn đã gần tới! Vấn đề là do.callmong đợi args của bạn nằm trong một listđối tượng được đặt tên . Bạn đã đưa chúng vào danh sách, nhưng dưới dạng chuỗi ký tự, không phải các mục danh sách được đặt tên.

Tôi nghĩ rằng điều này sẽ làm việc:

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

như Ben và Joshua đã chỉ ra trong các nhận xét, tôi có thể đã chỉ định tên khi tạo danh sách:

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

hoặc là

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

1
Tôi đã thay đổi mã một vài lần. Xin lỗi vì những chỉnh sửa. nó có ý nghĩa bây giờ? Khi tôi nói chúng là một vector trước đó, tôi đã nhầm. Xin lỗi vì điều đó.
JD Dài

2
Bạn có thể đặt tên cho args trong quá trình tạo danh sách:args.list <- list(x=x, y=y, z=x, nrow=2, ncol=2)
Joshua Ulrich

2
Không chính xác. Của bạn có độ dài thích hợp. Cấu trúc danh sách của bạn khác với cấu trúc danh sách của JD. Sử dụng str () và tên (). Tất cả các phần tử danh sách của bạn đều không có tên, vì vậy do.callđể thành công, cần phải có sự đối sánh vị trí chính xác.
IRTFM

2
@JD Long; Tôi chân thành đồng ý. Và nếu nó không ngăn chặn tất cả các lỗi, bạn vẫn nhận được thông báo lỗi và traceback()thông tin tốt hơn nhiều nếu bạn sử dụng các đối số được đặt tên.
IRTFM

1
Tôi không hoàn toàn theo dõi cuộc thảo luận ở đây; vì đối số đầu tiên grid.arrange()...đối sánh vị trí có thể không liên quan. Mỗi đầu vào phải là một đối tượng lưới (có hoặc không có tên), một tham số được đặt tên cho grid.layouthoặc một tham số được đặt tên cho các đối số còn lại.
baptiste

16

Thử cái này,

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)

3
phiên bản> = 0.9 của gridExtra cung cấp marrangeGrob để làm tất cả điều này tự động bất cứ khi nào nrow * ncol <length (lô)
Baptiste

5
ggsave("multipage.pdf", do.call(marrangeGrob, c(plots, list(nrow=2, ncol=2))))
baptiste

4

Tôi trả lời hơi muộn, nhưng tình cờ tìm ra một giải pháp tại R Graphics Cookbook thực hiện một điều gì đó rất tương tự bằng cách sử dụng một chức năng tùy chỉnh được gọi là multiplot. Có lẽ nó sẽ giúp những người khác tìm thấy câu hỏi này. Tôi cũng đang thêm câu trả lời vì giải pháp có thể mới hơn các câu trả lời khác cho câu hỏi này.

Nhiều biểu đồ trên một trang (ggplot2)

Đây là chức năng hiện tại, mặc dù hãy sử dụng liên kết trên, vì tác giả lưu ý rằng nó đã được cập nhật cho ggplot2 0.9.3, điều này cho thấy nó có thể thay đổi một lần nữa.

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

Một tạo các đối tượng cốt truyện:

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

Và sau đó chuyển chúng đến multiplot:

multiplot(p1, p2, ..., cols = n)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.