Parcelas lado a lado con ggplot2

339

Me gustaría colocar dos parcelas lado a lado usando el paquete ggplot2 , es decir, hacer el equivalente de par(mfrow=c(1,2)).

Por ejemplo, me gustaría que las siguientes dos gráficas se muestren juntas con la misma escala.

x <- rnorm(100)
eps <- rnorm(100,0,.2)
qplot(x,3*x+eps)
qplot(x,2*x+eps)

¿Tengo que ponerlos en el mismo marco de datos?

qplot(displ, hwy, data=mpg, facets = . ~ year) + geom_smooth()
Christopher DuBois
fuente
Creo que podrías hacer esto con celosía. ¿Es ggplot2 un requisito difícil?
JD Long
8
No. Pero ya había invertido tiempo en ajustar los qplots, por lo que era exactamente lo que me gustaba. :-) Y estoy tratando de jugar con ggplot.
Christopher DuBois
1
Echa un vistazo a stackoverflow.com/questions/31798162/…
Boern el
1
Para una buena visión general, vea la viñeta para el paquete de huevos : Diseño de múltiples parcelas en una página
Henrik

Respuestas:

505

Cualquier ggplots de lado a lado (o n plot en una cuadrícula)

La función grid.arrange()en el gridExtrapaquete combinará múltiples parcelas; así es como pones dos lado a lado.

require(gridExtra)
plot1 <- qplot(1)
plot2 <- qplot(1)
grid.arrange(plot1, plot2, ncol=2)

Esto es útil cuando las dos parcelas no se basan en los mismos datos, por ejemplo, si desea trazar diferentes variables sin usar reshape ().

Esto trazará la salida como un efecto secundario. Para imprimir el efecto secundario en un archivo, especifique un controlador de dispositivo (como pdf, png, etc.), por ejemplo,

pdf("foo.pdf")
grid.arrange(plot1, plot2)
dev.off()

o, usar arrangeGrob()en combinación con ggsave(),

ggsave("foo.pdf", arrangeGrob(plot1, plot2))

Esto es el equivalente a hacer dos trazados distintos usando par(mfrow = c(1,2)). Esto no solo ahorra tiempo en la organización de los datos, sino que es necesario cuando se desean dos trazados diferentes.


Apéndice: uso de facetas

Las facetas son útiles para hacer tramas similares para diferentes grupos. Esto se señala a continuación en muchas respuestas a continuación, pero quiero resaltar este enfoque con ejemplos equivalentes a los gráficos anteriores.

mydata <- data.frame(myGroup = c('a', 'b'), myX = c(1,1))

qplot(data = mydata, 
    x = myX, 
    facets = ~myGroup)

ggplot(data = mydata) + 
    geom_bar(aes(myX)) + 
    facet_wrap(~myGroup)

Actualizar

La plot_gridfunción en el cowplotvale la pena echarle un vistazo como una alternativa a grid.arrange. Vea la respuesta de @ claus-wilke a continuación y esta viñeta para un enfoque equivalente; pero la función permite controles más precisos sobre la ubicación y el tamaño de la trama, según esta viñeta .

David LeBauer
fuente
2
Cuando ejecuté su código usando objetos ggplot, sidebysideplot es nulo. Si desea guardar la salida en un archivo, use gridArrange. Ver stackoverflow.com/questions/17059099/…
Jim
@ Jim gracias por señalar eso. He revisado mi respuesta. Avíseme si queda alguna pregunta.
David LeBauer
1
¿Está grid.aarange depricated ahora?
Atticus29
?grid.arrangeme hace pensar que esta función ahora se llama arreglaGrob. Pude hacer lo que quería haciendo a <- arrangeGrob(p1, p2)y luego print(a).
blakeoft
@blakeoft ¿miraste los ejemplos? grid.arrangesigue siendo una función válida, no en desuso. ¿Intentaste usar la función? Lo que sucede, si no es lo que esperabas.
David LeBauer
159

Una desventaja de las soluciones basadas en ellas grid.arrangees que dificultan etiquetar las parcelas con letras (A, B, etc.), como lo requieren la mayoría de las revistas.

Escribí el paquete cowplot para resolver este (y algunos otros) problemas, específicamente la función plot_grid():

library(cowplot)

iris1 <- ggplot(iris, aes(x = Species, y = Sepal.Length)) +
  geom_boxplot() + theme_bw()

iris2 <- ggplot(iris, aes(x = Sepal.Length, fill = Species)) +
  geom_density(alpha = 0.7) + theme_bw() +
  theme(legend.position = c(0.8, 0.8))

plot_grid(iris1, iris2, labels = "AUTO")

ingrese la descripción de la imagen aquí

El objeto que plot_grid()regresa es otro objeto ggplot2, y puede guardarlo ggsave()como de costumbre:

p <- plot_grid(iris1, iris2, labels = "AUTO")
ggsave("plot.pdf", p)

Alternativamente, puede usar la función de diagrama de trama save_plot(), que es una envoltura delgada ggsave()que facilita obtener las dimensiones correctas para diagramas combinados, por ejemplo:

p <- plot_grid(iris1, iris2, labels = "AUTO")
save_plot("plot.pdf", p, ncol = 2)

(El ncol = 2argumento dice save_plot()que hay dos trazados uno al lado del otro y save_plot()hace que la imagen guardada sea dos veces más ancha).

Para obtener una descripción más detallada de cómo organizar las parcelas en una cuadrícula, vea esta viñeta. También hay una viñeta que explica cómo hacer tramas con una leyenda compartida.

Un punto frecuente de confusión es que el paquete cowplot cambia el tema predeterminado ggplot2. El paquete se comporta de esa manera porque fue escrito originalmente para usos internos de laboratorio, y nunca usamos el tema predeterminado. Si esto causa problemas, puede usar uno de los siguientes tres enfoques para solucionarlos:

1. Establezca el tema manualmente para cada trama. Creo que es una buena práctica especificar siempre un tema en particular para cada argumento, tal como lo hice con+ theme_bw() en el ejemplo anterior. Si especifica un tema en particular, el tema predeterminado no importa.

2. Revierta el tema predeterminado nuevamente al valor predeterminado de ggplot2. Puede hacer esto con una línea de código:

theme_set(theme_gray())

3. Llame a las funciones de la trama sin adjuntar el paquete. Tampoco puede invocar library(cowplot)o, require(cowplot)y en su lugar, llamar a funciones de diagrama de trama, anteponiendo cowplot::. Por ejemplo, el ejemplo anterior que usa el tema predeterminado ggplot2 sería:

## Commented out, we don't call this
# library(cowplot)

iris1 <- ggplot(iris, aes(x = Species, y = Sepal.Length)) +
  geom_boxplot()

iris2 <- ggplot(iris, aes(x = Sepal.Length, fill = Species)) +
  geom_density(alpha = 0.7) +
  theme(legend.position = c(0.8, 0.8))

cowplot::plot_grid(iris1, iris2, labels = "AUTO")

ingrese la descripción de la imagen aquí

Actualizaciones:

  • A partir de cowplot 1.0, el tema predeterminado ggplot2 ya no se cambia.
  • A partir de ggplot2 3.0.0, las parcelas se pueden etiquetar directamente, ver, por ejemplo, aquí.
Claus Wilke
fuente
en la gráfica de salida está eliminando el tema de fondo de ambas parcelas? ¿hay alguna alternativa?
VAR121
@ VAR121 Sí, es una línea de código. Explicado al final de la primera sección de la viñeta introductoria: cran.rstudio.com/web/packages/cowplot/vignettes/…
Claus Wilke
¿Es posible tener la misma escala y para todas las parcelas con este paquete?
Herman Toothrot
Tendrás que configurar las escalas y manualmente para que coincidan. O considera hacer facetas.
Claus Wilke
¿Podría establecer un ggtitle () para cada gráfico antes de usar grid.arrange ()?
Seanosapien
49

Puede usar la siguiente multiplotfunción del libro de cocina R de Winston Chang

multiplot(plot1, plot2, cols=2)

multiplot <- function(..., plotlist=NULL, cols) {
    require(grid)

    # Make a list from the ... arguments and plotlist
    plots <- c(list(...), plotlist)

    numPlots = length(plots)

    # Make the panel
    plotCols = cols                          # Number of columns of plots
    plotRows = ceiling(numPlots/plotCols) # Number of rows needed, calculated from # of cols

    # Set up the page
    grid.newpage()
    pushViewport(viewport(layout = grid.layout(plotRows, plotCols)))
    vplayout <- function(x, y)
        viewport(layout.pos.row = x, layout.pos.col = y)

    # Make each plot, in the correct location
    for (i in 1:numPlots) {
        curRow = ceiling(i/plotCols)
        curCol = (i-1) %% plotCols + 1
        print(plots[[i]], vp = vplayout(curRow, curCol ))
    }

}
David LeBauer
fuente
24

Usando el paquete de patchwork , simplemente puede usar el +operador:

library(ggplot2)
library(patchwork)

p1 <- ggplot(mtcars) + geom_point(aes(mpg, disp))
p2 <- ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear))


p1 + p2

labor de retazos

Deena
fuente
Solo para completar, el patchwork ahora también está en CRAN. Espero que estés contento con mi pequeña edición
Tjebo
18

Sí, creo que necesita organizar sus datos adecuadamente. Una forma sería esta:

X <- data.frame(x=rep(x,2),
                y=c(3*x+eps, 2*x+eps),
                case=rep(c("first","second"), each=100))

qplot(x, y, data=X, facets = . ~ case) + geom_smooth()

Estoy seguro de que hay mejores trucos en plyr o remodelar: todavía no estoy realmente al día con todos estos paquetes poderosos de Hadley.

Dirk Eddelbuettel
fuente
16

Usando el paquete de remodelación puede hacer algo como esto.

library(ggplot2)
wide <- data.frame(x = rnorm(100), eps = rnorm(100, 0, .2))
wide$first <- with(wide, 3 * x + eps)
wide$second <- with(wide, 2 * x + eps)
long <- melt(wide, id.vars = c("x", "eps"))
ggplot(long, aes(x = x, y = value)) + geom_smooth() + geom_point() + facet_grid(.~ variable)
Thierry
fuente
12

También hay un paquete de múltiples paneles que vale la pena mencionar. Ver también esta respuesta .

library(ggplot2)
theme_set(theme_bw())

q1 <- ggplot(mtcars) + geom_point(aes(mpg, disp))
q2 <- ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear))
q3 <- ggplot(mtcars) + geom_smooth(aes(disp, qsec))
q4 <- ggplot(mtcars) + geom_bar(aes(carb))

library(magrittr)
library(multipanelfigure)
figure1 <- multi_panel_figure(columns = 2, rows = 2, panel_label_type = "none")
# show the layout
figure1

figure1 %<>%
  fill_panel(q1, column = 1, row = 1) %<>%
  fill_panel(q2, column = 2, row = 1) %<>%
  fill_panel(q3, column = 1, row = 2) %<>%
  fill_panel(q4, column = 2, row = 2)
figure1

# complex layout
figure2 <- multi_panel_figure(columns = 3, rows = 3, panel_label_type = "upper-roman")
figure2

figure2 %<>%
  fill_panel(q1, column = 1:2, row = 1) %<>%
  fill_panel(q2, column = 3, row = 1) %<>%
  fill_panel(q3, column = 1, row = 2) %<>%
  fill_panel(q4, column = 2:3, row = 2:3)
figure2

Creado el 06/07/2018 por el paquete reprex (v0.2.0.9000).

Tung
fuente
9

ggplot2 se basa en gráficos de cuadrícula, que proporcionan un sistema diferente para organizar trazados en una página. El par(mfrow...)comando no tiene un equivalente directo, ya que los objetos de cuadrícula (llamados grobs ) no se dibujan necesariamente de inmediato, sino que pueden almacenarse y manipularse como objetos R normales antes de convertirse en una salida gráfica. Esto permite una mayor flexibilidad que el sorteo ahora. modelo de gráficos base, pero la estrategia es necesariamente un poco diferente.

Escribí grid.arrange()para proporcionar una interfaz simple lo más cerca posible par(mfrow). En su forma más simple, el código se vería así:

library(ggplot2)
x <- rnorm(100)
eps <- rnorm(100,0,.2)
p1 <- qplot(x,3*x+eps)
p2 <- qplot(x,2*x+eps)

library(gridExtra)
grid.arrange(p1, p2, ncol = 2)

ingrese la descripción de la imagen aquí

Se detallan más opciones en esta viñeta .

Una queja común es que las parcelas no están necesariamente alineadas, por ejemplo, cuando tienen etiquetas de eje de diferente tamaño, pero esto es por diseño: grid.arrange no hace ningún intento de colocar objetos especiales en ggplot2 y los trata por igual a otros grobs (por ejemplo, gráficas de celosía). ) Simplemente coloca grobs en un diseño rectangular.

Para el caso especial de los objetos ggplot2, escribí otra función, ggarrangecon una interfaz similar, que intenta alinear los paneles de trazado (incluidos los trazados facetados) e intenta respetar las relaciones de aspecto cuando el usuario las define.

library(egg)
ggarrange(p1, p2, ncol = 2)

Ambas funciones son compatibles con ggsave(). Para obtener una descripción general de las diferentes opciones y un contexto histórico, esta viñeta ofrece información adicional .

bautista
fuente
9

Actualización: esta respuesta es muy antigua. gridExtra::grid.arrange()ahora es el enfoque recomendado. Dejo esto aquí en caso de que pueda ser útil.


Stephen Turner publicó la arrange()función en el blog Getting Genetics Done (consulte la publicación para obtener instrucciones sobre la aplicación)

vp.layout <- function(x, y) viewport(layout.pos.row=x, layout.pos.col=y)
arrange <- function(..., nrow=NULL, ncol=NULL, as.table=FALSE) {
 dots <- list(...)
 n <- length(dots)
 if(is.null(nrow) & is.null(ncol)) { nrow = floor(n/2) ; ncol = ceiling(n/nrow)}
 if(is.null(nrow)) { nrow = ceiling(n/ncol)}
 if(is.null(ncol)) { ncol = ceiling(n/nrow)}
        ## NOTE see n2mfrow in grDevices for possible alternative
grid.newpage()
pushViewport(viewport(layout=grid.layout(nrow,ncol) ) )
 ii.p <- 1
 for(ii.row in seq(1, nrow)){
 ii.table.row <- ii.row 
 if(as.table) {ii.table.row <- nrow - ii.table.row + 1}
  for(ii.col in seq(1, ncol)){
   ii.table <- ii.p
   if(ii.p > n) break
   print(dots[[ii.table]], vp=vp.layout(ii.table.row, ii.col))
   ii.p <- ii.p + 1
  }
 }
}
Jeromy Anglim
fuente
99
Básicamente es una versión muy desactualizada de grid.arrange(desearía no haberla publicado en las listas de correo en ese momento, no hay forma de actualizar estos recursos en línea), la versión empaquetada es una mejor opción si me preguntas
Baptiste
4

Utilizando tidyverse:

x <- rnorm(100)
eps <- rnorm(100,0,.2)
df <- data.frame(x, eps) %>% 
  mutate(p1 = 3*x+eps, p2 = 2*x+eps) %>% 
  tidyr::gather("plot", "value", 3:4) %>% 
  ggplot(aes(x = x , y = value)) + 
    geom_point() + 
    geom_smooth() + 
    facet_wrap(~plot, ncol =2)

df

ingrese la descripción de la imagen aquí

brillante
fuente
1

Las soluciones anteriores pueden no ser eficientes si desea trazar múltiples diagramas de ggplot usando un bucle (por ejemplo, como se pide aquí: Crear múltiples diagramas en ggplot con diferentes valores del eje Y usando un bucle ), que es un paso deseado para analizar lo desconocido ( o grandes) conjuntos de datos (por ejemplo, cuando desea trazar recuentos de todas las variables en un conjunto de datos).

El siguiente código muestra cómo hacerlo utilizando el mencionado 'multiplot ()', cuya fuente está aquí: http://www.cookbook-r.com/Graphs/Multiple_graphs_on_one_page_(ggplot2) :

plotAllCounts <- function (dt){   
  plots <- list();
  for(i in 1:ncol(dt)) {
    strX = names(dt)[i]
    print(sprintf("%i: strX = %s", i, strX))
    plots[[i]] <- ggplot(dt) + xlab(strX) +
      geom_point(aes_string(strX),stat="count")
  }

  columnsToPlot <- floor(sqrt(ncol(dt)))
  multiplot(plotlist = plots, cols = columnsToPlot)
}

Ahora ejecute la función: para obtener recuentos de todas las variables impresas usando ggplot en una página

dt = ggplot2::diamonds
plotAllCounts(dt)

Una cosa a tener en cuenta es que:
usar aes(get(strX)), que normalmente usarías en bucles cuando trabajas ggplot, en el código anterior en lugar de aes_string(strX)NO dibujará los gráficos deseados. En cambio, trazará la última trama muchas veces. No he descubierto por qué, es posible que tenga que hacer el aesy se aes_stringles llame ggplot.

De lo contrario, espero que encuentre útil la función.

IVIM
fuente
1
Tenga en cuenta que su código desarrolla plotsobjetos dentro de los for-loopcuales es altamente ineficiente y no se recomienda R. Consulte estas excelentes publicaciones para descubrir mejores formas de hacerlo: acumulación eficiente en R , aplicación de una función sobre filas de un marco de datos y flujos de trabajo orientados a filas en R con el tidyverse
Tung
Una forma más eficiente de recorrer las variables es usar un tidy evaluationenfoque que ha estado disponible desde ggplot2 v.3.0.0 stackoverflow.com/a/52045613/786542
Tung
0

En mi experiencia, gridExtra: grid.arrange funciona perfectamente, si está tratando de generar trazados en un bucle.

Fragmento de código corto:

gridExtra::grid.arrange(plot1, plot2, ncol = 2)
Mayank Agrawal
fuente
¿Cómo mejora su respuesta en la respuesta del bautista del 2 de diciembre de 2017 a las 4:20? Su respuesta parece ser un duplicado. Mira lo que hace una respuesta aceptable aquí Cómo responder
Peter
No pude dividir la trama según sea necesario dentro de un bucle, y de ahí la sugerencia. Inicialmente, escribí el fragmento completo de mi bucle for con su implementación, pero luego decidí no hacerlo por el momento. Actualizará el código completo en una semana más o menos.
Mayank Agrawal
En primer lugar, intenté hacerlo utilizando el paquete cowplot, pero no tuve éxito. En mi análisis rápido, nadie había mencionado una solución de trazado múltiple dentro de un bucle for y de ahí mi comentario. Recomiéndame cualquier comentario si me equivoco.
Mayank Agrawal
Si el código en su respuesta incluyera un bucle for que sería diferente.
Peter
Lo actualizaría en una semana probs aquí y todo el proyecto en Kaggle. Salud.
Mayank Agrawal
-3

El cowplotpaquete le brinda una buena manera de hacerlo, de una manera que se adapte a la publicación.

x <- rnorm(100)
eps <- rnorm(100,0,.2)
A = qplot(x,3*x+eps, geom = c("point", "smooth"))+theme_gray()
B = qplot(x,2*x+eps, geom = c("point", "smooth"))+theme_gray()
cowplot::plot_grid(A, B, labels = c("A", "B"), align = "v")

ingrese la descripción de la imagen aquí

tim
fuente
3
Consulte también la respuesta más detallada y la justificación de los autores del paquete arriba stackoverflow.com/a/31223588/199217
David LeBauer