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?


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


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


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.


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)


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


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


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.


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

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

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.