Як я можу організувати довільну кількість ggplots за допомогою grid.arrange?


93

Це розміщено в перекладі на групі Google ggplot2

Моя ситуація полягає в тому, що я працюю над функцією, яка виводить довільну кількість графіків (залежно від вхідних даних, наданих користувачем). Функція повертає список з 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 Довгий

1
особливо "покірно притулившись" частина - нічого, як хороший грой :-)
Бен Болкер

@JD і @Ben - я лестощі, хлопці. З повагою. І я дуже ціную допомогу.
briandk

Відповіді:


45

Ви ВІНШЕ там! Проблема полягає в тому, do.callщо ви очікуєте, що ваші аргументи будуть в названому 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 Довгий

2
Ви можете назвати аргументи під час створення списку:args.list <- list(x=x, y=y, z=x, nrow=2, ncol=2)
Джошуа Ульріх

2
Не зовсім. Ваш належної довжини. Структура вашого списку відрізняється від структури списку JD. Використовуйте str () та імена (). Усі елементи вашого списку безіменні, тому do.callщоб досягти успіху, потрібно було б точно відповідати позиції.
IRTFM

2
@JD Лонг; Я від душі погоджуюсь. І якщо це не запобігає всім помилкам, ви все одно отримаєте набагато кращі повідомлення про помилки та traceback()інформацію, якщо будете використовувати названі аргументи.
IRTFM

1
Я не дуже слідкую за обговореннями тут; так як перший аргумент grid.arrange()є ...позиційним відповідність, ймовірно , не має значення. Кожен вхід повинен бути або об’єктом сітки (з іменем або без нього), іменованим параметром для grid.layoutабо іменованим параметром для решти аргументів.
баптист

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
версія> = 0,9 gridExtra забезпечує marrangeGrob робити це все автоматично, коли nrow * ncol <length (сюжети)
baptiste

5
ggsave("multipage.pdf", do.call(marrangeGrob, c(plots, list(nrow=2, ncol=2))))
баптист

4

Я відповідаю трохи пізно, але натрапив на рішення в Cookbook Book R Graphics, яке робить щось дуже схоже за допомогою спеціальної функції під назвою 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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.