Fügen Sie eine gemeinsame Legende für kombinierte ggplots hinzu

138

Ich habe zwei ggplots, mit denen ich horizontal ausrichte grid.arrange. Ich habe viele Forenbeiträge durchgesehen, aber alles, was ich versuche, scheinen Befehle zu sein, die jetzt aktualisiert und etwas anderes benannt werden.

Meine Daten sehen so aus;

# Data plot 1                                   
        axis1     axis2   
group1 -0.212201  0.358867
group2 -0.279756 -0.126194
group3  0.186860 -0.203273
group4  0.417117 -0.002592
group1 -0.212201  0.358867
group2 -0.279756 -0.126194
group3  0.186860 -0.203273
group4  0.186860 -0.203273

# Data plot 2   
        axis1     axis2
group1  0.211826 -0.306214
group2 -0.072626  0.104988
group3 -0.072626  0.104988
group4 -0.072626  0.104988
group1  0.211826 -0.306214
group2 -0.072626  0.104988
group3 -0.072626  0.104988
group4 -0.072626  0.104988

#And I run this:
library(ggplot2)
library(gridExtra)


groups=c('group1','group2','group3','group4','group1','group2','group3','group4')

x1=data1[,1]
y1=data1[,2]

x2=data2[,1]
y2=data2[,2]

p1=ggplot(data1, aes(x=x1, y=y1,colour=groups)) + geom_point(position=position_jitter(w=0.04,h=0.02),size=1.8)

p2=ggplot(data2, aes(x=x2, y=y2,colour=groups)) + geom_point(position=position_jitter(w=0.04,h=0.02),size=1.8)

#Combine plots
p3=grid.arrange(
p1 + theme(legend.position="none"), p2+ theme(legend.position="none"), nrow=1, widths = unit(c(10.,10), "cm"), heights = unit(rep(8, 1), "cm")))

Wie würde ich die Legende aus einem dieser Diagramme extrahieren und sie unten / in der Mitte des kombinierten Diagramms hinzufügen?

jO.
quelle
2
Ich habe gelegentlich dieses Problem. Wenn Sie den Plot nicht facettieren möchten, ist die einfachste Lösung, die ich kenne, nur einen mit einer Legende zu speichern und ihn dann mit Photoshop / Ilustrator in die leeren Legendenplots einzufügen. Unelegant weiß ich - aber praktisch schnell und einfach.
Stephen Henderson
@ StephenHenderson Das ist eine Antwort. Facette oder Nachbearbeitung mit dem gfx-Editor.
Brandon Bertelsen

Antworten:

107

Update 2015-Feb

Siehe Stevens Antwort unten


df1 <- read.table(text="group   x     y   
group1 -0.212201  0.358867
group2 -0.279756 -0.126194
group3  0.186860 -0.203273
group4  0.417117 -0.002592
group1 -0.212201  0.358867
group2 -0.279756 -0.126194
group3  0.186860 -0.203273
group4  0.186860 -0.203273",header=TRUE)

df2 <- read.table(text="group   x     y   
group1  0.211826 -0.306214
group2 -0.072626  0.104988
group3 -0.072626  0.104988
group4 -0.072626  0.104988
group1  0.211826 -0.306214
group2 -0.072626  0.104988
group3 -0.072626  0.104988
group4 -0.072626  0.104988",header=TRUE)


library(ggplot2)
library(gridExtra)

p1 <- ggplot(df1, aes(x=x, y=y,colour=group)) + geom_point(position=position_jitter(w=0.04,h=0.02),size=1.8) + theme(legend.position="bottom")

p2 <- ggplot(df2, aes(x=x, y=y,colour=group)) + geom_point(position=position_jitter(w=0.04,h=0.02),size=1.8)

#extract legend
#https://github.com/hadley/ggplot2/wiki/Share-a-legend-between-two-ggplot2-graphs
g_legend<-function(a.gplot){
  tmp <- ggplot_gtable(ggplot_build(a.gplot))
  leg <- which(sapply(tmp$grobs, function(x) x$name) == "guide-box")
  legend <- tmp$grobs[[leg]]
  return(legend)}

mylegend<-g_legend(p1)

p3 <- grid.arrange(arrangeGrob(p1 + theme(legend.position="none"),
                         p2 + theme(legend.position="none"),
                         nrow=1),
             mylegend, nrow=2,heights=c(10, 1))

Hier ist die resultierende Handlung: 2 Grundstücke mit gemeinsamer Legende

Roland
quelle
2
Beide Antworten verweisen auf dieselbe Wiki-Seite, die aktualisiert werden kann, wenn neue Versionen von ggplot2 den Code beschädigen.
Taufe
Mehr als sechs Jahre später löste diese Antwort mein Problem. Vielen Dank!
SPK.z
Dies mag für einige / die meisten Leute unkompliziert sein, aber ich habe es nicht sofort verstanden, also dachte ich, ich würde es kommentieren. Wenn Sie die allgemeine Legende über dem Plot (und nicht unten) haben möchten, müssen Sie nur die Argumente wechseln. Im obigen Beispiel geht mylegend voran arrangeGrob(). Sie müssen auch die Höhen umkehren (dhheights=c(1,10)
ljh2001
113

Sie können auch ggarrange aus dem ggpubr- Paket verwenden und "common.legend = TRUE" setzen:

library(ggpubr)

dsamp <- diamonds[sample(nrow(diamonds), 1000), ]
p1 <- qplot(carat, price, data = dsamp, colour = clarity)
p2 <- qplot(cut, price, data = dsamp, colour = clarity)
p3 <- qplot(color, price, data = dsamp, colour = clarity)
p4 <- qplot(depth, price, data = dsamp, colour = clarity) 

ggarrange(p1, p2, p3, p4, ncol=2, nrow=2, common.legend = TRUE, legend="bottom")

Geben Sie hier die Bildbeschreibung ein

Huiyan Wan
quelle
1
Ist es möglich, dass dies in einer glänzenden Anwendung (oder einem Flexdashboard) mit renderPlot () nicht funktioniert? Es funktioniert einwandfrei in einem normalen R-Skript mit normalen Plots. Aber wenn ich genau dasselbe mit Plots mache, die mit renderPlot () in meinem Flexdashboard erstellt wurden, wird nichts angezeigt.
Tingolfin
1
Vielen Dank dafür - ich denke, dies war bei weitem die einfachste Lösung für das, was ich suchte
Komal Rathi
Das ist fantastisch! Danke dir!
Yanes
@Tingolfin Ich musste manchmal print(ggarrangeobject)eines meiner ggarrangeObjekte umwickeln, wenn es von einer anderen Funktion geplottet werden musste, die möglicherweise der Lösung für Ihr Objekt ähnelt renderPlot().
Brandon
common.legend = TRUEist alles was ich brauche!
Aryo
62

Rolands Antwort muss aktualisiert werden. Siehe: https://github.com/hadley/ggplot2/wiki/Share-a-legend-between-two-ggplot2-graphs

Diese Methode wurde für ggplot2 v1.0.0 aktualisiert.

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


grid_arrange_shared_legend <- function(...) {
    plots <- list(...)
    g <- ggplotGrob(plots[[1]] + theme(legend.position="bottom"))$grobs
    legend <- g[[which(sapply(g, function(x) x$name) == "guide-box")]]
    lheight <- sum(legend$height)
    grid.arrange(
        do.call(arrangeGrob, lapply(plots, function(x)
            x + theme(legend.position="none"))),
        legend,
        ncol = 1,
        heights = unit.c(unit(1, "npc") - lheight, lheight))
}

dsamp <- diamonds[sample(nrow(diamonds), 1000), ]
p1 <- qplot(carat, price, data=dsamp, colour=clarity)
p2 <- qplot(cut, price, data=dsamp, colour=clarity)
p3 <- qplot(color, price, data=dsamp, colour=clarity)
p4 <- qplot(depth, price, data=dsamp, colour=clarity)
grid_arrange_shared_legend(p1, p2, p3, p4)

Beachten Sie das Fehlen von ggplot_gtableund ggplot_build. ggplotGrobwird stattdessen verwendet. Dieses Beispiel ist etwas komplizierter als die obige Lösung, aber es hat es trotzdem für mich gelöst.

Steven Lockton
quelle
10
Hallo, ich habe 6 Diagramme, und ich möchte die 6 Diagramme als 2 Zeilen × 3 Spalten anordnen und die Legende oben zeichnen. Wie kann ich also die Funktion ändern grid_arrange_shared_legend? Danke dir!
just_rookie
4
@just_rookie hast du eine lösung gefunden, wie man die funktion so ändert, dass man statt nur verschiedene ncol- und nrow-anordnungen verwenden kann ncol = 1?
Giuseppe
Hallo, ich habe diese Lösung ausprobiert, sie funktioniert gut, aber beim Drucken habe ich 2 PDF-Seiten statt nur 1, die erste ist leer, während die spätere meine Handlung enthält. Warum habe ich so ein Verhalten? danke,
HanniBaL90
Für alle, die das gleiche Problem wie ich haben, gibt es hier eine Problemumgehung
HanniBaL90
1
Tolles Zeug hier. Irgendeine Idee, wie man einen einzelnen Titel für alle Handlungen hinzufügen kann?
Pertinax
27

Eine neue, attraktive Lösung ist die Verwendung patchwork. Die Syntax ist sehr einfach:

library(ggplot2)
library(patchwork)

p1 <- ggplot(df1, aes(x = x, y = y, colour = group)) + 
  geom_point(position = position_jitter(w = 0.04, h = 0.02), size = 1.8)
p2 <- ggplot(df2, aes(x = x, y = y, colour = group)) + 
  geom_point(position = position_jitter(w = 0.04, h = 0.02), size = 1.8)

combined <- p1 + p2 & theme(legend.position = "bottom")
combined + plot_layout(guides = "collect")

Erstellt am 2019-12-13 durch das reprex-Paket (v0.2.1)

MSR
quelle
2
Wenn Sie die Reihenfolge der Befehle geringfügig ändern, können Sie dies in einer Zeile tun: combined <- p1 + p2 + plot_layout(guides = "collect") & theme(legend.position = "bottom")
mlcyo
17

Ich schlage vor, Cowplot zu verwenden. Aus ihrer R-Vignette :

# load cowplot
library(cowplot)

# down-sampled diamonds data set
dsamp <- diamonds[sample(nrow(diamonds), 1000), ]

# Make three plots.
# We set left and right margins to 0 to remove unnecessary spacing in the
# final plot arrangement.
p1 <- qplot(carat, price, data=dsamp, colour=clarity) +
   theme(plot.margin = unit(c(6,0,6,0), "pt"))
p2 <- qplot(depth, price, data=dsamp, colour=clarity) +
   theme(plot.margin = unit(c(6,0,6,0), "pt")) + ylab("")
p3 <- qplot(color, price, data=dsamp, colour=clarity) +
   theme(plot.margin = unit(c(6,0,6,0), "pt")) + ylab("")

# arrange the three plots in a single row
prow <- plot_grid( p1 + theme(legend.position="none"),
           p2 + theme(legend.position="none"),
           p3 + theme(legend.position="none"),
           align = 'vh',
           labels = c("A", "B", "C"),
           hjust = -1,
           nrow = 1
           )

# extract the legend from one of the plots
# (clearly the whole thing only makes sense if all plots
# have the same legend, so we can arbitrarily pick one.)
legend_b <- get_legend(p1 + theme(legend.position="bottom"))

# add the legend underneath the row we made earlier. Give it 10% of the height
# of one plot (via rel_heights).
p <- plot_grid( prow, legend_b, ncol = 1, rel_heights = c(1, .2))
p

kombinierte Handlungen mit Legende unten

Gregor Sturm
quelle
Dies war nur ein Weg, der es möglich machte, eine manuelle Legende mit annotate_figure(ggarrange())legend_b () in meine Handlung aufzunehmen. Vielen Dank, Gott segne dich!
Jean Karlos
12

@Giuseppe, vielleicht möchten Sie dies für eine flexible Spezifikation der Plotanordnung berücksichtigen (von hier aus modifiziert ):

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

grid_arrange_shared_legend <- function(..., nrow = 1, ncol = length(list(...)), position = c("bottom", "right")) {

  plots <- list(...)
  position <- match.arg(position)
  g <- ggplotGrob(plots[[1]] + theme(legend.position = position))$grobs
  legend <- g[[which(sapply(g, function(x) x$name) == "guide-box")]]
  lheight <- sum(legend$height)
  lwidth <- sum(legend$width)
  gl <- lapply(plots, function(x) x + theme(legend.position = "none"))
  gl <- c(gl, nrow = nrow, ncol = ncol)

  combined <- switch(position,
                     "bottom" = arrangeGrob(do.call(arrangeGrob, gl),
                                            legend,
                                            ncol = 1,
                                            heights = unit.c(unit(1, "npc") - lheight, lheight)),
                     "right" = arrangeGrob(do.call(arrangeGrob, gl),
                                           legend,
                                           ncol = 2,
                                           widths = unit.c(unit(1, "npc") - lwidth, lwidth)))
  grid.newpage()
  grid.draw(combined)

}

Zusätzliche Argumente nrowund ncolsteuern das Layout der angeordneten Diagramme:

dsamp <- diamonds[sample(nrow(diamonds), 1000), ]
p1 <- qplot(carat, price, data = dsamp, colour = clarity)
p2 <- qplot(cut, price, data = dsamp, colour = clarity)
p3 <- qplot(color, price, data = dsamp, colour = clarity)
p4 <- qplot(depth, price, data = dsamp, colour = clarity)
grid_arrange_shared_legend(p1, p2, p3, p4, nrow = 1, ncol = 4)
grid_arrange_shared_legend(p1, p2, p3, p4, nrow = 2, ncol = 2)

Geben Sie hier die Bildbeschreibung ein Geben Sie hier die Bildbeschreibung ein

Epsilon
quelle
Wie bei der anderen Lösung habe ich es versucht, es funktioniert gut, aber beim Drucken habe ich 2 PDF-Seiten statt nur 1, die erste ist leer, während die spätere meine Handlung enthält. Warum habe ich so ein Verhalten? danke,
HanniBaL90
Für alle, die das gleiche Problem wie ich haben, gibt es hier eine Problemumgehung
HanniBaL90
Kann mir jemand die Lösung erklären? Wie kann ich die Legende oben statt unten platzieren? Danke
HanniBaL90
8

Wenn Sie in beiden Plots dieselben Variablen zeichnen, besteht der einfachste Weg darin, die Datenrahmen zu einem zu kombinieren und dann facet_wrap zu verwenden.

Für Ihr Beispiel:

big_df <- rbind(df1,df2)

big_df <- data.frame(big_df,Df = rep(c("df1","df2"),
times=c(nrow(df1),nrow(df2))))

ggplot(big_df,aes(x=x, y=y,colour=group)) 
+ geom_point(position=position_jitter(w=0.04,h=0.02),size=1.8) 
+ facet_wrap(~Df)

Grundstück 1

Ein weiteres Beispiel mit dem Diamanten-Datensatz. Dies zeigt, dass Sie es sogar zum Laufen bringen können, wenn Sie nur eine Variable in Ihren Plots haben.

diamonds_reshaped <- data.frame(price = diamonds$price,
independent.variable = c(diamonds$carat,diamonds$cut,diamonds$color,diamonds$depth),
Clarity = rep(diamonds$clarity,times=4),
Variable.name = rep(c("Carat","Cut","Color","Depth"),each=nrow(diamonds)))

ggplot(diamonds_reshaped,aes(independent.variable,price,colour=Clarity)) + 
geom_point(size=2) + facet_wrap(~Variable.name,scales="free_x") + 
xlab("")

Grundstück 2

Das einzig schwierige am zweiten Beispiel ist, dass die Faktorvariablen zu numerisch gezwungen werden, wenn Sie alles in einem Datenrahmen kombinieren. Idealerweise tun Sie dies hauptsächlich, wenn alle Ihre interessierenden Variablen vom gleichen Typ sind.

hmgeiger
quelle
1

@ Guiseppe:

Ich habe überhaupt keine Ahnung von Grobs usw., aber ich habe eine Lösung für zwei Handlungen zusammen gehackt, die sich auf eine beliebige Zahl ausweiten lassen sollte, aber keine sexy Funktion hat:

plots <- list(p1, p2)
g <- ggplotGrob(plots[[1]] + theme(legend.position="bottom"))$grobs
legend <- g[[which(sapply(g, function(x) x$name) == "guide-box")]]
lheight <- sum(legend$height)
tmp <- arrangeGrob(p1 + theme(legend.position = "none"), p2 + theme(legend.position = "none"), layout_matrix = matrix(c(1, 2), nrow = 1))
grid.arrange(tmp, legend, ncol = 1, heights = unit.c(unit(1, "npc") - lheight, lheight))
Jack
quelle
1

Wenn die Legende für beide Diagramme gleich ist, gibt es eine einfache Lösung grid.arrange(vorausgesetzt, Sie möchten, dass Ihre Legende entweder vertikal oder horizontal an beiden Diagrammen ausgerichtet wird). Behalten Sie einfach die Legende für die Handlung ganz unten oder ganz rechts bei, während Sie die Legende für die andere weglassen. Durch Hinzufügen einer Legende zu nur einem Plot wird jedoch die Größe eines Plots relativ zum anderen geändert. Um dies zu vermeiden, verwenden Sie den heightsBefehl, um sie manuell anzupassen und die gleiche Größe beizubehalten. Sie können sogar grid.arrangeallgemeine Achsentitel erstellen. Beachten Sie, dass dies library(grid)zusätzlich erforderlich ist library(gridExtra). Für vertikale Diagramme:

y_title <- expression(paste(italic("E. coli"), " (CFU/100mL)"))

grid.arrange(arrangeGrob(p1, theme(legend.position="none"), ncol=1), arrangeGrob(p2, theme(legend.position="bottom"), ncol=1), heights=c(1,1.2), left=textGrob(y_title, rot=90, gp=gpar(fontsize=20)))

Hier ist das Ergebnis für ein ähnliches Diagramm für ein Projekt, an dem ich gearbeitet habe: Geben Sie hier die Bildbeschreibung ein

Wesley Lozano
quelle