To jest opublikowane w grupie google ggplot2
Moja sytuacja jest taka, że pracuję nad funkcją, która generuje dowolną liczbę wykresów (w zależności od danych wejściowych dostarczonych przez użytkownika). Funkcja zwraca listę n wykresów i chciałbym ułożyć te wykresy w układzie 2 x 2. Borykam się z jednoczesnymi problemami:
- Jak mogę pozwolić na przydzielenie elastyczności dowolnej (n) liczbie działek?
- Jak mogę również określić, czy chcę je rozłożyć na 2 x 2
Moja obecna strategia korzysta grid.arrange
z gridExtra
pakietu. Prawdopodobnie nie jest optymalny, zwłaszcza, że i to jest kluczowe, całkowicie nie działa . Oto mój skomentowany przykładowy kod, eksperymentujący z trzema wykresami:
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)
Jak to zwykle robię, pokornie kulę się w kącie, niecierpliwie czekając na mądrą opinię społeczności znacznie mądrzejszej ode mnie. Zwłaszcza jeśli utrudniam to, niż powinno.
Odpowiedzi:
Jesteś prawie na miejscu! Problem polega na tym, że
do.call
oczekuje , że twoje argumenty będą w nazwanymlist
obiekcie. Umieściłeś je na liście, ale jako ciągi znaków, a nie nazwane elementy listy.Myślę, że to powinno działać:
args.list <- c(plot.list, 2,2) names(args.list) <- c("x", "y", "z", "nrow", "ncol")
jak Ben i Joshua wskazali w komentarzach, podczas tworzenia listy mogłem przypisać imiona:
args.list <- c(plot.list,list(nrow=2,ncol=2))
lub
args.list <- list(x=x, y=y, z=x, nrow=2, ncol=2)
źródło
args.list <- list(x=x, y=y, z=x, nrow=2, ncol=2)
do.call
aby zakończyło się sukcesem, musiałoby być dokładne dopasowanie pozycyjne.traceback()
informacje, jeśli użyjesz nazwanych argumentów.grid.arrange()
jest...
pozycyjny dopasowanie jest prawdopodobnie nieistotne. Każde wejście musi być obiektem siatki (z nazwą lub bez), nazwanym parametrem dlagrid.layout
lub nazwanym parametrem dla pozostałych argumentów.Spróbuj tego,
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)
źródło
ggsave("multipage.pdf", do.call(marrangeGrob, c(plots, list(nrow=2, ncol=2))))
Odpowiadam trochę późno, ale natknąłem się na rozwiązanie w książce kucharskiej R Graphics, które robi coś bardzo podobnego za pomocą niestandardowej funkcji o nazwie
multiplot
. Być może pomoże to innym, którzy znajdą to pytanie. Dodam również odpowiedź, ponieważ rozwiązanie może być nowsze niż inne odpowiedzi na to pytanie.Wiele wykresów na jednej stronie (ggplot2)
Oto aktualna funkcja, ale użyj powyższego linku, ponieważ autor zauważył, że została zaktualizowana dla ggplot2 0.9.3, co oznacza, że może się ponownie zmienić.
# 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)) } } }
Tworzy się obiekty działki:
p1 <- ggplot(...) p2 <- ggplot(...) # etc.
A następnie przekazuje je do
multiplot
:multiplot(p1, p2, ..., cols = n)
źródło