Allinea a sinistra due bordi del grafico (ggplot)


105

Sto usando ggplot e ho due grafici che voglio visualizzare uno sopra l'altro. Ho usato grid.arrangeda gridExtra per impilarli. Il problema è che voglio che i bordi sinistro dei grafici si allineino così come i bordi destro indipendentemente dalle etichette degli assi. (il problema sorge perché le etichette di un grafico sono corte mentre l'altra è lunga).

La domanda:
come posso farlo? Non sono sposato con grid.arrange ma ggplot2 è un must.

Cosa ho provato:
ho provato a giocare con larghezze e altezze, nonché ncol e nrow per creare una griglia 2 x 2 e posizionare le immagini negli angoli opposti e poi giocare con le larghezze ma non sono riuscito a ottenere le immagini negli angoli opposti .

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)

inserisci qui la descrizione dell'immagine


2
Ecco due possibili opzioni: qui e qui .
joran

@Joran, sto cercando di allineare gli assi di sinistra. Non credo che questi lo faranno. Mi piacerebbe comunque sbagliarmi.
Tyler Rinker

Risposte:


132

Prova questo,

 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)

modificare

Ecco una soluzione più generale (funziona con qualsiasi numero di grafici) utilizzando una versione modificata di rbind.gtableincluso ingridExtra

gA <- ggplotGrob(A)
gB <- ggplotGrob(B)
grid::grid.newpage()
grid::grid.draw(rbind(gA, gB))

3
Bello e davvero molto semplice. Grazie per la soluzione.
Tyler Rinker

1
Soluzione perfetta! Stavo cercando qualcosa di simile per allineare più trame di serie temporali separate che non posso fare con la sfaccettatura a causa della maggiore personalizzazione in ogni trama.
Wahalulu

Saresti così gentile da fornire quale sarebbe il modo per abbinare l'altezza se avessimo due colonne? gA $ heights [2: 3] non sembra funzionare. Devo selezionare un altro elemento del grob diverso da 2: 3? Grazie!
Etienne Low-Décarie

4
Grazie per la tua soluzione Baptiste. Tuttavia, non riesco a farlo funzionare quando uno dei grafici è un file tableGrob. Il gtable::cbindmi dà un errore di deludente: nrow(x) == nrow(y) is not TRUE. Eventuali suggerimenti?
Gabra

2
Questa soluzione ha funzionato per me, sto cercando di capirla. A cosa serve lo [2:5]stand?
Hurlikus

38

Volevo generalizzare questo per qualsiasi numero di trame. Ecco una soluzione passo passo utilizzando l'approccio di Baptiste:

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

raccogliere le larghezze per ogni grob di ogni trama

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

usa do.call per ottenere la larghezza massima

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

assegnare la larghezza massima a ogni grob

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

tracciare

do.call("grid.arrange", c(grobs, ncol = 1))

2
Funziona anche quando le trame hanno leggende di larghezza variabile - molto bello!
Keith Hughitt

30

Utilizzo del pacchetto 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")

inserisci qui la descrizione dell'immagine


12

Su http://rpubs.com/MarkusLoew/13295 è disponibile una soluzione davvero semplice (ultimo elemento) Applicata a questo 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"))

puoi anche usarlo sia per la larghezza che per l'altezza:

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'))

2
usare size="first"significa che l'allineamento non sarà molto buono se il secondo lotto è più grande del primo
baptiste

10

Il eggpacchetto avvolge gli oggetti ggplot in una 3x3tabella gtable standardizzata , consentendo l'allineamento dei pannelli di trama tra ggplot arbitrari, inclusi quelli sfaccettati.

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)

inserisci qui la descrizione dell'immagine


per me questo potrebbe disporre correttamente in orizzontale una semplice mappa di calore ( geom_tile) con legenda in basso e una mappa di calore sfaccettata ( facet_gridcon geom_tile), ma non è riuscita ad allineare l'altezza del terzo grafico, che era un dendrogramma ( geom_segment). tuttavia, cowplot o gridExtra::grid.arrangenon sono stati in grado di fare nemmeno il primo, quindi finora funziona al meglio
deeenes

8

Ecco un'altra possibile soluzione utilizzando meltdal pacchetto 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)

inserisci qui la descrizione dell'immagine


Questa soluzione presuppone che tu abbia lo stesso numero di righe in ogni colonna. Nel mio MRWE questo è vero ma non nella realtà.
Tyler Rinker

Non sono sicuro di aver capito: vuoi dire che CO2 $ Plant e CO2 $ Type hanno la stessa lunghezza, ma che i tuoi dati effettivi non sono così?
bdemarest

Sono due diversi set di dati che condividono una variabile, quindi il numero di righe non è lo stesso.
Tyler Rinker

2

Il pacchetto patchwork gestisce questo per impostazione predefinita:

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

Creato il 08/12/2019 dal pacchetto reprex (v0.3.0)


0

Nella migliore delle ipotesi questo è un trucco:

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

Però sembra davvero sbagliato.


-1

So che questo è un vecchio post e che ha già ricevuto risposta, ma posso suggerire di combinare l'approccio di @ baptiste con purrrper renderlo più bello:

library(purrr)
list(A, B) %>% 
  map(ggplotGrob) %>% 
  do.call(gridExtra::gtable_rbind, .) %>% 
  grid::grid.draw()
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.