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


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

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

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


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

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


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


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)


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.


-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()
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.