grid.arrange를 사용하여 임의의 수의 ggplot을 정렬하려면 어떻게해야합니까?


93

이것은 ggplot2 Google 그룹에 교차 게시되었습니다.

내 상황은 사용자가 제공 한 입력 데이터에 따라 임의의 수의 플롯을 출력하는 함수작업하고 있다는 것 입니다. 이 함수는 n 개의 플롯 목록을 반환하며이 플롯을 2 x 2 형식으로 배치하고 싶습니다. 다음과 같은 동시 문제로 어려움을 겪고 있습니다.

  1. 임의의 (n) 수의 플롯에 유연성을 부여하려면 어떻게해야합니까?
  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
특히 일부 "겸손에 옹기종기 모일 필요 없다"- 아무것도 :-) 좋은의 아첨처럼
벤 Bolker

@JD와 @Ben-나는 기뻐요, 여러분. 진정으로. 그리고 도움을 주셔서 정말 감사합니다.
briandk 2011-07-13

답변:


45

당신은 거의 거기에 있습니다! 문제는 do.callargs가 명명 된 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
코드를 몇 번 변경했습니다. 수정해서 죄송합니다. 이제 이해가 되나요? 이전에 벡터라고 말했을 때 잘못 이해했습니다. 미안합니다.
JD Long

2
목록을 만드는 동안 인수의 이름을 지정할 수 있습니다. args.list <- list(x=x, y=y, z=x, nrow=2, ncol=2)
Joshua Ulrich

2
정확히. 당신은 적절한 길이입니다. 목록의 구조가 JD 목록의 구조와 다릅니다. str () 및 names ()를 사용하십시오. 모든 목록 요소의 이름이 지정되지 않았으므로이 do.call성공하려면 정확한 위치 일치가 필요했습니다.
IRTFM 2011

2
@JD Long; 진심으로 동의합니다. 그리고 모든 오류를 방지하지 못 traceback()하더라도 명명 된 인수를 사용하면 훨씬 더 나은 오류 메시지와 정보를 얻을 수 있습니다.
IRTFM 2011

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 of gridExtra는 nrow * ncol <length (plots) 때마다이 모든 작업을 자동으로 수행하도록 marrangeGrob을 제공합니다
baptiste

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

4

나는 조금 늦게 대답하고 있지만 R Graphics Cookbook에서라는 사용자 정의 함수를 사용하여 매우 유사한 것을 수행하는 솔루션을 발견했습니다 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.