Alinhar à esquerda duas arestas do gráfico (ggplot)

105

Estou usando o ggplot e tenho dois gráficos que quero exibir um sobre o outro. Usei grid.arrangedo gridExtra para empilhá-los. O problema é que eu quero que as bordas esquerdas dos gráficos se alinhem assim como as bordas direitas, independentemente dos rótulos dos eixos. (o problema surge porque os rótulos de um gráfico são curtos enquanto o outro é longo).

A pergunta:
como posso fazer isso? Não sou casado com grid.arrange, mas o ggplot2 é obrigatório.

O que eu tentei:
tentei brincar com larguras e alturas, bem como ncol e nrow para fazer uma grade 2 x 2 e colocar os visuais em cantos opostos e, em seguida, brincar com as larguras, mas não consegui obter os visuais em cantos opostos .

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)

insira a descrição da imagem aqui

Tyler Rinker
fonte
2
Aqui estão duas opções possíveis: aqui e aqui .
joran
@Joran Estou procurando os eixos esquerdos para serem alinhados. Eu não acho que isso vai resolver. Eu gostaria de estar errado.
Tyler Rinker

Respostas:

132

Tente isto,

 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

Aqui está uma solução mais geral (funciona com qualquer número de plotagens) usando uma versão modificada do rbind.gtableincluído emgridExtra

gA <- ggplotGrob(A)
gB <- ggplotGrob(B)
grid::grid.newpage()
grid::grid.draw(rbind(gA, gB))
batista
fonte
3
Linda e realmente muito simples. Obrigado pela solução.
Tyler Rinker
1
Solução perfeita! Tenho procurado algo assim para alinhar vários gráficos de série temporal separados que não posso fazer com facetação por causa da grande personalização em cada lote.
wahalulu de
Você poderia ter a gentileza de fornecer qual seria a maneira de combinar a altura se tivéssemos duas colunas? gA $ heights [2: 3] não parece funcionar. Tenho que selecionar outro elemento do grob além de 2: 3? Obrigado!
Etienne Low-Décarie
4
Obrigado pela sua solução Baptiste. No entanto, não consigo fazer isso funcionar quando uma das parcelas é a tableGrob. O gtable::cbinddá-me um erro decepcionante: nrow(x) == nrow(y) is not TRUE. Alguma sugestão?
Gabra
2
Essa solução funcionou para mim, mas estou tentando entender. O que [2:5]significa isso?
Hurlikus
38

Eu queria generalizar isso para qualquer número de parcelas. Aqui está uma solução passo a passo usando a abordagem de Baptiste:

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

colete as larguras para cada grob 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 obter a largura máxima

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

atribua a largura máxima para cada grob

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

enredo

do.call("grid.arrange", c(grobs, ncol = 1))
Slizb
fonte
2
Funciona mesmo quando as parcelas têm legendas de larguras variadas - muito bom!
Keith Hughitt
30

Usando o pacote 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")

insira a descrição da imagem aqui

zx8754
fonte
12

Em http://rpubs.com/MarkusLoew/13295 está disponível uma solução realmente fácil (último item) 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"))

você também pode usar isso para largura e altura:

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
fonte
2
usar size="first"significa que o alinhamento não ficará muito bom se o segundo gráfico for maior que o primeiro
baptiste
10

O eggpacote envolve objetos ggplot em uma 3x3gtable padronizada , permitindo o alinhamento de painéis de plotagem entre ggplots arbitrários, incluindo os 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)

insira a descrição da imagem aqui

batista
fonte
para mim, isso poderia organizar horizontalmente um mapa de calor simples ( geom_tile) com legenda na parte inferior e um mapa de calor multifacetado ( facet_gridcom geom_tile), mas falhou em alinhar a altura do terceiro gráfico, que era um dendrograma ( geom_segment). no entanto, o cowplot ou gridExtra::grid.arrangenão foram capazes de fazer nem mesmo o primeiro, então isso funciona melhor até agora
deeenes
8

Aqui está outra solução possível usando meltdo pacote reshape2 e 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)

insira a descrição da imagem aqui

bdemarest
fonte
Esta solução pressupõe que você tenha o mesmo número de linhas em cada coluna. No meu MRWE isso é verdade, mas não na realidade.
Tyler Rinker
Não tenho certeza se entendi: você quer dizer que CO2 $ Planta e CO2 $ Tipo têm o mesmo comprimento, mas seus dados reais não são assim?
bdemarest
São dois conjuntos de dados diferentes que compartilham uma variável, portanto o número de linhas não é o mesmo.
Tyler Rinker
2

O pacote patchwork lida com isso por padrão:

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

Criado em 08/12/2019 pelo pacote reprex (v0.3.0)

MSR
fonte
0

Na melhor das hipóteses, isso é um hack:

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

Mas parece muito errado.

Tyler Rinker
fonte
-1

Sei que esta é uma postagem antiga e que já foi respondida, mas posso sugerir combinar a abordagem de @baptiste purrrpara torná-la mais bonita:

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