2013-04-27 11 views
39

Mam kilka różnych kategorii, które chcę wydrukować. Są to różne kategorie: , z których każdy ma własny zestaw etykiet, ale ma sens grupowania się w dokumencie. Poniżej przedstawiono kilka prostych ułożone przykłady wykresie słupkowym:Jak mogę utworzyć spójne szerokości wykresów w ggplot (z legendami)?

df <- data.frame(x=c("a", "b", "c"), 
       y=c("happy", "sad", "ambivalent about life")) 
ggplot(df, aes(x=factor(0), fill=x)) + geom_bar() 
ggplot(df, aes(x=factor(0), fill=y)) + geom_bar() 

Problem polega na tym, że z różnych etykiet, legendy mają różne szerokości, co oznacza, że ​​działki mają różne szerokości, co prowadzi do rzeczy, patrząc nieco głupkowaty jeśli robię elementy tabeli lub \subfigure. Jak mogę to naprawić?

Czy istnieje sposób jawnego ustawienia szerokości (bezwzględnej lub względnej) wykresu lub legendy?

Chart 1 based on x (wider) Chart 2 based on y (narrower)

+2

inna alternatywa: prawdopodobnie można umieścić legendy na góra/dół/do środka działki? – Arun

+4

'+ theme (legend.position =" top ")' (lub "bottom") (lub) '+ theme (legend.position = c (1,0), legend.justification = c (1,0)) – Arun

Odpowiedz

30

Edit: Bardzo łatwo z egg pakietu dostępne na github

# install.package(devtools) 
# devtools::install_github("baptiste/egg") 

library(egg) 

p1 <- ggplot(data.frame(x=c("a","b","c"), 
         y=c("happy","sad","ambivalent about life")), 
      aes(x=factor(0),fill=x)) + 
     geom_bar() 
p2 <- ggplot(data.frame(x=c("a","b","c"), 
         y=c("happy","sad","ambivalent about life")), 
      aes(x=factor(0),fill=y)) + 
     geom_bar() 

ggarrange(p1,p2, ncol = 1) 

Original Udated do ggplot2 2.2.1

Oto rozwiązanie, które korzysta z funkcji pakiet gtable i koncentruje się na szerokości skrzynek legendy. (Bardziej ogólnie można znaleźć rozwiązanie here.)

library(ggplot2) 
library(gtable)  
library(grid) 
library(gridExtra) 

# Your plots 
p1 <- ggplot(data.frame(x=c("a","b","c"),y=c("happy","sad","ambivalent about life")),aes(x=factor(0),fill=x)) + geom_bar() 
p2 <- ggplot(data.frame(x=c("a","b","c"),y=c("happy","sad","ambivalent about life")),aes(x=factor(0),fill=y)) + geom_bar() 

# Get the gtables 
gA <- ggplotGrob(p1) 
gB <- ggplotGrob(p2) 

# Set the widths 
gA$widths <- gB$widths 

# Arrange the two charts. 
# The legend boxes are centered 
grid.newpage() 
grid.arrange(gA, gB, nrow = 2) 

Jeśli dodatkowo pola legendy muszą być wyrównane do lewej, a pożyczanie jakiś kod z here napisany przez @Julius

p1 <- ggplot(data.frame(x=c("a","b","c"),y=c("happy","sad","ambivalent about life")),aes(x=factor(0),fill=x)) + geom_bar() 
p2 <- ggplot(data.frame(x=c("a","b","c"),y=c("happy","sad","ambivalent about life")),aes(x=factor(0),fill=y)) + geom_bar() 

# Get the widths 
gA <- ggplotGrob(p1) 
gB <- ggplotGrob(p2) 

# The parts that differs in width 
leg1 <- convertX(sum(with(gA$grobs[[15]], grobs[[1]]$widths)), "mm") 
leg2 <- convertX(sum(with(gB$grobs[[15]], grobs[[1]]$widths)), "mm") 

# Set the widths 
gA$widths <- gB$widths 

# Add an empty column of "abs(diff(widths)) mm" width on the right of 
# legend box for gA (the smaller legend box) 
gA$grobs[[15]] <- gtable_add_cols(gA$grobs[[15]], unit(abs(diff(c(leg1, leg2))), "mm")) 

# Arrange the two charts 
grid.newpage() 
grid.arrange(gA, gB, nrow = 2) 

enter image description here

rozwiązania alternatywne Istnieje rbind i cbind funkcji w pakiecie gtable łączenia GRO bs w jeden grob. W przypadku wykresów szerokość powinna być ustawiona za pomocą size = "max", ale wersja CRAN gtable zgłasza błąd.

Jedna opcja: Powinno być oczywiste, że legenda na drugim wykresie jest szersza. Dlatego użyj opcji size = "last".

# Get the grobs 
gA <- ggplotGrob(p1) 
gB <- ggplotGrob(p2) 

# Combine the plots 
g = rbind(gA, gB, size = "last") 

# Draw it 
grid.newpage() 
grid.draw(g) 

wyrównany do lewej strony legendy:

# Get the grobs 
gA <- ggplotGrob(p1) 
gB <- ggplotGrob(p2) 

# The parts that differs in width 
leg1 <- convertX(sum(with(gA$grobs[[15]], grobs[[1]]$widths)), "mm") 
leg2 <- convertX(sum(with(gB$grobs[[15]], grobs[[1]]$widths)), "mm") 

# Add an empty column of "abs(diff(widths)) mm" width on the right of 
# legend box for gA (the smaller legend box) 
gA$grobs[[15]] <- gtable_add_cols(gA$grobs[[15]], unit(abs(diff(c(leg1, leg2))), "mm")) 

# Combine the plots 
g = rbind(gA, gB, size = "last") 

# Draw it 
grid.newpage() 
grid.draw(g) 

Drugą opcją jest użycie rbind z gridExtra pakietu Baptiste za

# Get the grobs 
gA <- ggplotGrob(p1) 
gB <- ggplotGrob(p2) 

# Combine the plots 
g = gridExtra::rbind.gtable(gA, gB, size = "max") 

# Draw it 
grid.newpage() 
grid.draw(g) 

wyrównany do lewej strony legendy:

# Get the grobs 
gA <- ggplotGrob(p1) 
gB <- ggplotGrob(p2) 

# The parts that differs in width 
leg1 <- convertX(sum(with(gA$grobs[[15]], grobs[[1]]$widths)), "mm") 
leg2 <- convertX(sum(with(gB$grobs[[15]], grobs[[1]]$widths)), "mm") 

# Add an empty column of "abs(diff(widths)) mm" width on the right of 
# legend box for gA (the smaller legend box) 
gA$grobs[[15]] <- gtable_add_cols(gA$grobs[[15]], unit(abs(diff(c(leg1, leg2))), "mm")) 

# Combine the plots 
g = gridExtra::rbind.gtable(gA, gB, size = "max") 

# Draw it 
grid.newpage() 
grid.draw(g) 
+1

To świetnie! Czy te funkcje i zmienne są udokumentowane w dowolnym miejscu lub po prostu przejrzeć źródło 'ggplot'? –

+0

@ jamie.f.olson W CRAN znajduje się instrukcja [gtable manual] (http://cran.r-project.org/web/packages/gtable/index.html), ale poza tym jest to kwestia podjęcia zwróć uwagę na przykłady innych osób na SO, a czasem na liście dyskusyjnej [ggplot] (https://groups.google.com/forum/?hl=en&fromgroups#!forum/ggplot2). –

+1

Czy wypróbowałeś 'rbind() razem działki? To powinno po prostu zadziałać, ale nie pamiętam, czy skończyliśmy ten kod. – hadley

8

Jak @hadley sugeruje, rbind.gtable powinien być w stanie obsłużyć tego

grid.draw(rbind(ggplotGrob(p1), ggplotGrob(p2), size="last")) 

jednak, szerokości układ powinien być idealnie size="max", który doesn't cope well z niektórych rodzajów jednostek siatki.

+0

Is: 'Błąd w mmm

+0

należy uruchomić przykładowy kod, aby go zobaczyć – baptiste

1

Przez przypadek zauważyłem, że rozwiązanie Aruna, które zasugerował w swoich komentarzach, nie zostało odebrane. Uważam, że jego proste i skuteczne podejście naprawdę warte jest zilustrowania.

Arun zaproponował, aby przenieść legendę do górnej lub dolnej:

ggplot(df, aes(x=factor(0), fill=x)) + geom_bar() + theme(legend.position = "bottom") 
ggplot(df, aes(x=factor(0), fill=y)) + geom_bar() + theme(legend.position = "bottom") 

enter image description here enter image description here

Teraz działki mają taką samą szerokość na żądanie. Ponadto powierzchnia działek jest jednakowo wielkości w obu przypadkach.

Jeśli istnieje więcej czynników lub nawet dłuższych etykiet, może być konieczne obejrzenie legendy, np. Wyświetlenie legendy w dwóch lub więcej wierszach. theme() i guide_legend() mają kilka parametrów do kontrolowania położenia i wyglądu legend w ggplot2.

5

Pakiet cowplot posiada również funkcję align_plots do tego celu (wyjście nie pokazano),

both2 <- align_plots(p1, p2, align="hv", axis="tblr") 
p1x <- ggdraw(both2[[1]]) 
p2x <- ggdraw(both2[[2]]) 
save_plot("cow1.png", p1x) 
save_plot("cow2.png", p2x) 

a także plot_grid który zapisuje działek do tego samego pliku.

library(cowplot) 
both <- plot_grid(p1, p2, ncol=1, labels = c("A", "B"), align = "v") 
save_plot("cow.png", both) 

enter image description here

Powiązane problemy