Wyrównaj dwie krawędzie wykresu do lewej (ggplot)

105

Używam ggplot i mam dwa wykresy, które chcę wyświetlić jeden na drugim. Użyłem grid.arrangez gridExtra, aby je ułożyć. Problem polega na tym, że chcę, aby lewe krawędzie wykresów były wyrównane, a także prawe krawędzie, niezależnie od etykiet osi. (problem pojawia się, ponieważ etykiety jednego wykresu są krótkie, a drugiego długie).

Pytanie:
jak mogę to zrobić? Nie jestem żonaty z grid.arrange, ale ggplot2 jest koniecznością.

Co próbowałem:
próbowałem grać z szerokościami i wysokościami, a także ncol i nrow, aby utworzyć siatkę 2 x 2 i umieścić wizualizacje w przeciwnych rogach, a następnie grać z szerokościami, ale nie mogłem uzyskać wizualizacji w przeciwnych rogach .

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)

wprowadź opis obrazu tutaj

Tyler Rinker
źródło
2
Oto dwie możliwe opcje: tutaj i tutaj .
joran
@Joran Szukam wyrównania lewej osi. Nie sądzę, żeby to wystarczyło. Chciałbym się jednak mylić.
Tyler Rinker

Odpowiedzi:

132

Spróbuj tego,

 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)

Edytować

Oto bardziej ogólne rozwiązanie (działa z dowolną liczbą działek) przy użyciu zmodyfikowanej wersji rbind.gtablezawartej wgridExtra

gA <- ggplotGrob(A)
gB <- ggplotGrob(B)
grid::grid.newpage()
grid::grid.draw(rbind(gA, gB))
baptiste
źródło
3
Piękne i bardzo proste. Dziękuję za rozwiązanie.
Tyler Rinker
1
Idealne rozwiązanie! Szukałem czegoś takiego, aby wyrównać wiele oddzielnych wykresów szeregów czasowych, których nie mogę zrobić z facetingiem ze względu na duże dostosowanie w każdej fabule.
wahalulu
Czy byłbyś tak uprzejmy, aby podać, jaki byłby sposób dopasowania wysokości, gdybyśmy mieli dwie kolumny? gA $ heights [2: 3] nie wydaje się działać. Czy muszę wybrać inny element grobu niż 2: 3? Dziękuję Ci!
Etienne Low-Décarie
4
Dziękuję za rozwiązanie Baptiste. Jednak nie udaje mi się to, gdy jeden z działek to plik tableGrob. gtable::cbindDaje mi rozczarowujący błąd: nrow(x) == nrow(y) is not TRUE. Jakieś sugestie?
Gabra,
2
To rozwiązanie zadziałało dla mnie, więc próbuję to zrozumieć. Co to [2:5]oznacza?
Hurlikus
38

Chciałem uogólnić to dla dowolnej liczby wątków. Oto rozwiązanie krok po kroku wykorzystujące podejście Baptiste:

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

zbierz szerokości dla każdego grobu na każdym wykresie

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

użyj do.call, aby uzyskać maksymalną szerokość

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

przypisz maksymalną szerokość do każdego groba

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

wątek

do.call("grid.arrange", c(grobs, ncol = 1))
slizb
źródło
2
Działa nawet wtedy, gdy działki mają legendy o różnej szerokości - bardzo fajnie!
Keith Hughitt
30

Korzystanie z pakietu 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")

wprowadź opis obrazu tutaj

zx8754
źródło
12

Na http://rpubs.com/MarkusLoew/13295 jest naprawdę łatwe dostępne rozwiązanie (ostatnia pozycja) Zastosowane do tego problemu:

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

możesz również użyć tego zarówno dla szerokości, jak i wysokości:

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'))
Wilberta
źródło
2
użycie size="first"oznacza, że ​​wyrównanie nie będzie wyglądało zbyt dobrze, jeśli druga działka jest większa niż pierwsza
baptiste
10

Te eggokłady pakietów ggplot przedmiotów do standaryzowanego 3x3gtable, umożliwiając ustawienie paneli działce między dowolnymi ggplots oraz szlifowanych nich.

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)

wprowadź opis obrazu tutaj

baptiste
źródło
dla mnie to mogłoby poprawnie ułożyć poziomo prostą mapę cieplną ( geom_tile) z legendą na dole i wieloaspektową mapą cieplną ( facet_gridz geom_tile), ale nie udało się wyrównać wysokości trzeciego wykresu, który był dendrogramem ( geom_segment). jednak cowplot lub gridExtra::grid.arrangenie byli w stanie zrobić nawet tego pierwszego, więc to działa najlepiej jak dotąd
deeenes
8

Oto inne możliwe rozwiązanie przy użyciu meltpakietu reshape2 i 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)

wprowadź opis obrazu tutaj

bdemarest
źródło
To rozwiązanie zakłada, że ​​masz równą liczbę wierszy w każdej kolumnie. W moim MRWE to prawda, ale nie w rzeczywistości.
Tyler Rinker
Nie jestem pewien, czy rozumiem: czy masz na myśli, że CO2 $ Plant i CO2 $ Type mają taką samą długość, ale twoje rzeczywiste dane nie są takie?
bdemarest
Są to dwa różne zestawy danych, które mają wspólną zmienną, więc liczba wierszy nie jest taka sama.
Tyler Rinker
2

Patchwork Pakiet ten obsługuje domyślnie:

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

Utworzony 08.12.2019 przez pakiet reprex (v0.3.0)

MSR
źródło
0

W najlepszym przypadku jest to hack:

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

Jednak wydaje się to naprawdę złe.

Tyler Rinker
źródło
-1

Wiem, że to stary post i że już na niego udzielono odpowiedzi, ale mogę zasugerować połączenie podejścia @ baptiste z, purrraby wyglądał ładniej:

library(purrr)
list(A, B) %>% 
  map(ggplotGrob) %>% 
  do.call(gridExtra::gtable_rbind, .) %>% 
  grid::grid.draw()
Felipe Gerard
źródło