Alinear a la izquierda dos bordes de gráfico (ggplot)

105

Estoy usando ggplot y tengo dos gráficos que quiero mostrar uno encima del otro. Solía grid.arrangedesde gridExtra apilarlos. El problema es que quiero que los bordes izquierdos de los gráficos se alineen así como los bordes derechos independientemente de las etiquetas de los ejes. (el problema surge porque las etiquetas de un gráfico son cortas mientras que el otro es largo).

La pregunta:
¿Cómo puedo hacer esto? No estoy casado con grid.arrange pero el ggplot2 es imprescindible.

Lo que probé:
intenté jugar con anchos y alturas, así como con ncol y nrow para hacer una cuadrícula de 2 x 2 y colocar las imágenes en esquinas opuestas y luego jugar con las anchuras, pero no pude obtener las imágenes en esquinas opuestas. .

require(ggplot2);require(gridExtra)
A <- ggplot(CO2, aes(x=Plant)) + geom_bar() +coord_flip() 
B <- ggplot(CO2, aes(x=Type)) + geom_bar() +coord_flip() 
grid.arrange(A, B, ncol=1)

ingrese la descripción de la imagen aquí

Tyler Rinker
fuente
2
Aquí hay dos opciones posibles: aquí y aquí .
joran
@Joran Estoy buscando alinear los ejes izquierdos. No creo que estos lo hagan. Aunque me gustaría estar equivocado.
Tyler Rinker

Respuestas:

132

Prueba esto,

 gA <- ggplotGrob(A)
 gB <- ggplotGrob(B)
 maxWidth = grid::unit.pmax(gA$widths[2:5], gB$widths[2:5])
 gA$widths[2:5] <- as.list(maxWidth)
 gB$widths[2:5] <- as.list(maxWidth)
 grid.arrange(gA, gB, ncol=1)

Editar

Aquí hay una solución más general (funciona con cualquier número de parcelas) usando una versión modificada de rbind.gtableincluido engridExtra

gA <- ggplotGrob(A)
gB <- ggplotGrob(B)
grid::grid.newpage()
grid::grid.draw(rbind(gA, gB))
baptiste
fuente
3
Hermoso y realmente bastante sencillo. Gracias por la solución.
Tyler Rinker
1
¡Solución perfecta! He estado buscando algo como esto para alinear múltiples tramas de series de tiempo separadas que no puedo hacer con las facetas debido a la gran personalización en cada trama.
wahalulu
¿Sería tan amable de proporcionarnos cuál sería la forma de igualar la altura si tuviéramos dos columnas? gA $ heights [2: 3] no parece funcionar. ¿Tengo que seleccionar otro elemento del grob que no sea 2: 3? ¡Gracias!
Etienne Low-Décarie
4
Gracias por tu solución Baptiste. Sin embargo, no consigo que esto funcione cuando una de las parcelas es un tableGrob. El gtable::cbindme da un error de decepcionante: nrow(x) == nrow(y) is not TRUE. ¿Alguna sugerencia?
Gabra
2
Esta solución funcionó para mí, puesto que estoy tratando de entenderla. ¿Qué significa el [2:5]soporte?
Hurlikus
38

Quería generalizar esto para cualquier número de parcelas. Aquí hay una solución paso a paso utilizando el enfoque de Baptiste:

plots <- list(A, B, C, D)
grobs <- list()
widths <- list()

recopilar los anchos de cada grupo de cada parcela

for (i in 1:length(plots)){
    grobs[[i]] <- ggplotGrob(plots[[i]])
    widths[[i]] <- grobs[[i]]$widths[2:5]
}

use do.call para obtener el ancho máximo

maxwidth <- do.call(grid::unit.pmax, widths)

asigne el ancho máximo a cada grob

for (i in 1:length(grobs)){
     grobs[[i]]$widths[2:5] <- as.list(maxwidth)
}

trama

do.call("grid.arrange", c(grobs, ncol = 1))
Slizb
fuente
2
Funciona incluso cuando las parcelas tienen leyendas de diferentes anchos, ¡muy agradable!
Keith Hughitt
30

Usando el paquete cowplot :

A <- ggplot(CO2, aes(x=Plant)) + geom_bar() +coord_flip() 
B <- ggplot(CO2, aes(x=Type)) + geom_bar() +coord_flip() 

library(cowplot)
plot_grid(A, B, ncol=1, align="v")

ingrese la descripción de la imagen aquí

zx8754
fuente
12

En http://rpubs.com/MarkusLoew/13295 hay una solución realmente fácil disponible (último elemento) aplicada a este problema:

require(ggplot2);require(gridExtra)
A <- ggplot(CO2, aes(x=Plant)) + geom_bar() +coord_flip() 
B <- ggplot(CO2, aes(x=Type)) + geom_bar() +coord_flip() 
grid.draw(rbind(ggplotGrob(A), ggplotGrob(B), size="first"))

también puede usar esto para ancho y alto:

require(ggplot2);require(gridExtra)
A <- ggplot(CO2, aes(x=Plant)) + geom_bar() +coord_flip() 
B <- ggplot(CO2, aes(x=Type)) + geom_bar() +coord_flip() 
C <- ggplot(CO2, aes(x=conc)) + geom_bar() +coord_flip()
D <- ggplot(CO2, aes(x=uptake)) + geom_bar() +coord_flip() 
grid.draw(cbind(
            rbind(ggplotGrob(A), ggplotGrob(B), size="first"),
            rbind(ggplotGrob(C), ggplotGrob(D), size="first"),
            size='first'))
Wilbert
fuente
2
el uso size="first"significa que la alineación no se verá muy bien si la segunda parcela es más grande que la primera
baptiste
10

El eggpaquete envuelve los objetos ggplot en una 3x3tabla gt estandarizada , lo que permite la alineación de los paneles de gráficos entre ggplots arbitrarios, incluidos los facetados.

library(egg) # devtools::install_github('baptiste/egg')
library(ggplot2)

p1 <- ggplot(mtcars, aes(mpg, wt, colour = factor(cyl))) +
  geom_point() 

p2 <- ggplot(mtcars, aes(mpg, wt, colour = factor(cyl))) +
  geom_point() + facet_wrap( ~ cyl, ncol=2, scales = "free") +
  guides(colour="none") +
  theme()

ggarrange(p1, p2)

ingrese la descripción de la imagen aquí

baptiste
fuente
para mí, esto podría organizar correctamente horizontalmente un mapa de calor simple ( geom_tile) con una leyenda en la parte inferior y un mapa de calor multifacético ( facet_gridcon geom_tile), pero no pudo alinear la altura de la tercera gráfica, que era un dendrograma ( geom_segment). sin embargo, cowplot o gridExtra::grid.arrangeno pudimos hacer ni siquiera lo primero, por lo que esto funciona mejor hasta ahora
deeenes
8

Aquí hay otra posible solución usando meltel paquete reshape2, y facet_wrap:

library(ggplot2)
library(reshape2)

dat = CO2[, c(1, 2)]
dat$id = seq(nrow(dat))
mdat = melt(dat, id.vars="id")

head(mdat)
#   id variable value
# 1  1    Plant   Qn1
# 2  2    Plant   Qn1
# 3  3    Plant   Qn1
# 4  4    Plant   Qn1
# 5  5    Plant   Qn1
# 6  6    Plant   Qn1

plot_1 = ggplot(mdat, aes(x=value)) + 
         geom_bar() + 
         coord_flip() +
         facet_wrap(~ variable, nrow=2, scales="free", drop=TRUE)

ggsave(plot=plot_1, filename="plot_1.png", height=4, width=6)

ingrese la descripción de la imagen aquí

bdemarest
fuente
Esta solución supone que tiene el mismo número de filas en cada columna. En mi MRWE eso es cierto pero no en la realidad.
Tyler Rinker
No estoy seguro de haber entendido: ¿Quiere decir que CO2 $ Plant y CO2 $ Type tienen la misma longitud, pero que sus datos reales no son así?
bdemarest
Son dos conjuntos de datos diferentes que comparten una variable, por lo que el número de filas no es el mismo.
Tyler Rinker
2

El paquete de patchwork maneja esto por defecto:

library(ggplot2)
library(patchwork)

A <- ggplot(CO2, aes(x = Plant)) + geom_bar() + coord_flip() 
B <- ggplot(CO2, aes(x = Type)) + geom_bar() + coord_flip() 

A / B

Creado el 2019-12-08 por el paquete reprex (v0.3.0)

MSR
fuente
0

En el mejor de los casos, esto es un truco:

library(wq)
layOut(list(A, 1, 2:16),  list(B, 2:3, 1:16))

Aunque se siente realmente mal.

Tyler Rinker
fuente
-1

Sé que esta es una publicación antigua y que ya ha sido respondida, pero puedo sugerir combinar el enfoque de @ baptiste con purrrpara que se vea más agradable:

library(purrr)
list(A, B) %>% 
  map(ggplotGrob) %>% 
  do.call(gridExtra::gtable_rbind, .) %>% 
  grid::grid.draw()
Felipe Gerard
fuente