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.