ฉันจะจัดเรียง ggplots จำนวนโดยพลการโดยใช้ grid.arrange ได้อย่างไร


93

โพสต์ข้ามในกลุ่ม ggplot2 google

สถานการณ์ของฉันคือฉันกำลังทำงานกับฟังก์ชันที่แสดงจำนวนพล็อตโดยพลการ (ขึ้นอยู่กับข้อมูลอินพุตที่ผู้ใช้ให้มา) ฟังก์ชันจะส่งคืนรายการ n พล็อตและฉันต้องการจัดวางพล็อตเหล่านั้นในรูปแบบ 2 x 2 ฉันกำลังดิ้นรนกับปัญหาที่เกิดขึ้นพร้อมกันของ:

  1. ฉันจะอนุญาตให้มีความยืดหยุ่นในการส่งมอบจำนวนแปลงโดยพลการได้อย่างไร?
  2. ฉันจะระบุได้อย่างไรว่าฉันต้องการให้วาง 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)

ในขณะที่ฉันไม่เคยจะทำฉันก็เบียดตัวเข้ามุมอย่างนอบน้อมรอการตอบรับจากชุมชนที่ฉลาดกว่าฉันอย่างใจจดใจจ่อโดยเฉพาะอย่างยิ่งถ้าฉันทำเรื่องนี้ยากกว่าที่ควรจะเป็น


2
ชื่นชมกับคำถามที่ทำได้ดีมาก ฉันจะใช้สิ่งนี้เป็นตัวอย่างในการเขียนคำถาม SO [r] ที่ดี
JD Long

1
โดยเฉพาะอย่างยิ่งส่วน "การเบียดเสียดอย่างอ่อนน้อมถ่อมตน" - ไม่มีอะไรที่ดีเท่า :-)
Ben Bolker

@JD และ @ เบ็น - ฉันปลื้มผู้ชาย ขอแสดงความนับถือ. และฉันขอขอบคุณสำหรับความช่วยเหลือ
briandk

คำตอบ:


45

คุณเกือบจะอยู่ที่นั่นแล้ว! ปัญหาคือdo.callคาดว่า args ของคุณจะอยู่ในlistอ็อบเจ็กต์ที่มีชื่อ คุณใส่ไว้ในรายการ แต่เป็นสตริงอักขระไม่ได้ตั้งชื่อรายการ

ฉันคิดว่าสิ่งนี้ควรได้ผล:

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

ดังที่เบ็นและโจชัวชี้ให้เห็นในความคิดเห็นฉันสามารถกำหนดชื่อได้เมื่อฉันสร้างรายการ:

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

หรือ

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

1
ฉันเปลี่ยนรหัสสองสามครั้ง ขออภัยสำหรับการแก้ไข ตอนนี้มันสมเหตุสมผลไหม เมื่อฉันบอกว่าพวกมันเป็นเวกเตอร์ก่อนหน้านี้ฉันพูดผิด ขอโทษสำหรับเรื่องนั้น.
JD Long

2
คุณสามารถตั้งชื่อ args ในระหว่างการสร้างรายการ:args.list <- list(x=x, y=y, z=x, nrow=2, ncol=2)
Joshua Ulrich

2
ไม่ตรง ของคุณมีความยาวที่เหมาะสม โครงสร้างของรายการของคุณแตกต่างจากโครงสร้างของรายการของ JD ใช้ str () และชื่อ () องค์ประกอบรายการทั้งหมดของคุณไม่มีชื่อดังนั้นเพื่อdo.callให้ประสบความสำเร็จจำเป็นต้องมีการจับคู่ตำแหน่งที่แน่นอน
IRTFM

2
@JD ยาว; ฉันเห็นด้วยอย่างยิ่ง และหากไม่ได้ป้องกันข้อผิดพลาดทั้งหมดคุณจะยังคงได้รับข้อความแสดงข้อผิดพลาดและtraceback()ข้อมูลที่ดีกว่ามากหากคุณใช้อาร์กิวเมนต์ที่มีชื่อ
IRTFM

1
ฉันไม่ค่อยติดตามการสนทนาที่นี่ เนื่องจากอาร์กิวเมนต์แรกถึงgrid.arrange()คือการ...จับคู่ตำแหน่งอาจไม่เกี่ยวข้อง อินพุตแต่ละรายการต้องเป็นอ็อบเจ็กต์กริด (มีหรือไม่มีชื่อ) พารามิเตอร์ที่ตั้งชื่อสำหรับgrid.layoutหรือพารามิเตอร์ที่มีชื่อสำหรับอาร์กิวเมนต์ที่เหลือ
baptiste

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)

3
version> = 0.9 ของ gridExtra ให้ marrangeGrob ทำทั้งหมดนี้โดยอัตโนมัติเมื่อใดก็ตามที่ nrow * ncol <length (plot)
baptiste

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

4

ฉันตอบรับสายไปนิด แต่สะดุดในการแก้ปัญหาที่ R multiplotกราฟิกตำราที่ไม่สิ่งที่คล้ายกันมากในการใช้ฟังก์ชั่นที่กำหนดเองที่เรียกว่า บางทีมันอาจจะช่วยคนอื่น ๆ ที่พบคำถามนี้ ฉันยังเพิ่มคำตอบเนื่องจากวิธีแก้ปัญหาอาจใหม่กว่าคำตอบอื่น ๆ สำหรับคำถามนี้

กราฟหลายรายการในหน้าเดียว (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)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.