Side-by-Side-Diagramme mit ggplot2

338

Ich möchte zwei Diagramme mit dem Paket ggplot2 nebeneinander platzieren , dh das Äquivalent von par(mfrow=c(1,2)).

Zum Beispiel möchte ich, dass die folgenden zwei Diagramme nebeneinander mit demselben Maßstab angezeigt werden.

x <- rnorm(100)
eps <- rnorm(100,0,.2)
qplot(x,3*x+eps)
qplot(x,2*x+eps)

Muss ich sie in denselben data.frame einfügen?

qplot(displ, hwy, data=mpg, facets = . ~ year) + geom_smooth()
Christopher DuBois
quelle
Ich denke, Sie könnten dies mit Gitter tun. Ist ggplot2 eine harte Anforderung?
JD Long
8
Nein. Aber ich hatte bereits die Zeit investiert, um die qplots so zu optimieren, dass es genau so war, wie ich es mochte. :-) Und ich versuche mit ggplot herumzuspielen.
Christopher DuBois
1
Eine schöne Übersicht finden Sie in der Vignette für das Eierpaket : Mehrere Diagramme auf einer Seite
Henrik

Antworten:

503

Alle ggplots nebeneinander (oder n Plots in einem Raster)

Die Funktion grid.arrange()im gridExtraPaket kombiniert mehrere Diagramme. So setzen Sie zwei nebeneinander.

require(gridExtra)
plot1 <- qplot(1)
plot2 <- qplot(1)
grid.arrange(plot1, plot2, ncol=2)

Dies ist nützlich, wenn die beiden Diagramme nicht auf denselben Daten basieren, z. B. wenn Sie verschiedene Variablen ohne Verwendung von reshape () zeichnen möchten.

Dadurch wird die Ausgabe als Nebeneffekt dargestellt. Um die Nebenwirkung in eine Datei zu drucken, spezifizieren einen Gerätetreiber (wie pdf, pngusw.), zB

pdf("foo.pdf")
grid.arrange(plot1, plot2)
dev.off()

oder arrangeGrob()in Kombination mit verwenden ggsave(),

ggsave("foo.pdf", arrangeGrob(plot1, plot2))

Dies entspricht der Erstellung von zwei unterschiedlichen Plots mit par(mfrow = c(1,2)). Dies spart nicht nur Zeit beim Anordnen von Daten, sondern ist auch erforderlich, wenn Sie zwei unterschiedliche Diagramme wünschen.


Anhang: Verwenden von Facetten

Facetten sind hilfreich, um ähnliche Diagramme für verschiedene Gruppen zu erstellen. Dies wird unten in vielen Antworten unten ausgeführt, aber ich möchte diesen Ansatz mit Beispielen hervorheben, die den obigen Darstellungen entsprechen.

mydata <- data.frame(myGroup = c('a', 'b'), myX = c(1,1))

qplot(data = mydata, 
    x = myX, 
    facets = ~myGroup)

ggplot(data = mydata) + 
    geom_bar(aes(myX)) + 
    facet_wrap(~myGroup)

Aktualisieren

Die plot_gridFunktion in der cowplotist es wert, als Alternative zu überprüft zu werden grid.arrange. Siehe die Antwort von @ claus-wilke unten und diese Vignette für einen äquivalenten Ansatz; Die Funktion ermöglicht jedoch feinere Steuerelemente für die Position und Größe des Plots, basierend auf dieser Vignette .

David LeBauer
quelle
2
Wenn ich Ihren Code mit ggplot-Objekten ausgeführt habe, ist sidebysideplot null. Wenn Sie die Ausgabe in einer Datei speichern möchten, verwenden Sie gridArrange. Siehe stackoverflow.com/questions/17059099/…
Jim
@ Jim, danke, dass du darauf hingewiesen hast. Ich habe meine Antwort überarbeitet. Lassen Sie mich wissen, wenn noch Fragen offen sind.
David LeBauer
1
Ist grid.aarange jetzt beraubt?
Atticus29
?grid.arrangelässt mich denken, dass diese Funktion jetzt arrangGrob heißt. Ich konnte tun, was ich wollte, indem ich es tat a <- arrangeGrob(p1, p2)und dann print(a).
Blakeoft
@blakeoft hast du dir die Beispiele angesehen? grid.arrangeist immer noch eine gültige, nicht veraltete Funktion. Haben Sie versucht, die Funktion zu verwenden? Was passiert, wenn nicht das, was Sie erwartet haben?
David LeBauer
159

Ein Nachteil der darauf basierenden Lösungen grid.arrangebesteht darin, dass sie es schwierig machen, die Diagramme mit Buchstaben (A, B usw.) zu kennzeichnen, wie es die meisten Zeitschriften erfordern.

Ich habe das Cowplot- Paket geschrieben, um dieses (und einige andere) Probleme zu lösen, insbesondere die Funktion plot_grid():

library(cowplot)

iris1 <- ggplot(iris, aes(x = Species, y = Sepal.Length)) +
  geom_boxplot() + theme_bw()

iris2 <- ggplot(iris, aes(x = Sepal.Length, fill = Species)) +
  geom_density(alpha = 0.7) + theme_bw() +
  theme(legend.position = c(0.8, 0.8))

plot_grid(iris1, iris2, labels = "AUTO")

Geben Sie hier die Bildbeschreibung ein

Das zurückgegebene Objekt plot_grid()ist ein anderes ggplot2-Objekt, und Sie können es ggsave()wie gewohnt speichern :

p <- plot_grid(iris1, iris2, labels = "AUTO")
ggsave("plot.pdf", p)

Alternativ können Sie die Cowplot-Funktion verwenden save_plot(), bei der es sich um eine dünne Hülle handelt ggsave(), mit der Sie auf einfache Weise die richtigen Abmessungen für kombinierte Plots ermitteln können, z.

p <- plot_grid(iris1, iris2, labels = "AUTO")
save_plot("plot.pdf", p, ncol = 2)

(Das ncol = 2Argument besagt, save_plot()dass zwei Diagramme nebeneinander liegen und save_plot()das gespeicherte Bild doppelt so breit ist.)

Eine ausführlichere Beschreibung zum Anordnen von Plots in einem Raster finden Sie in dieser Vignette. Es gibt auch eine Vignette, in der erklärt wird, wie Plots mit einer gemeinsamen Legende erstellt werden.

Ein häufiger Punkt der Verwirrung ist, dass das Cowplot-Paket das Standardthema ggplot2 ändert. Das Paket verhält sich so, weil es ursprünglich für interne Laborzwecke geschrieben wurde und wir niemals das Standarddesign verwenden. Wenn dies zu Problemen führt, können Sie einen der folgenden drei Ansätze verwenden, um diese zu umgehen:

1. Stellen Sie das Thema für jedes Diagramm manuell ein. Ich denke, es ist eine gute Praxis, immer ein bestimmtes Thema für jede Handlung anzugeben, so wie ich es + theme_bw()im obigen Beispiel getan habe . Wenn Sie ein bestimmtes Thema angeben, spielt das Standardthema keine Rolle.

2. Setzen Sie das Standarddesign wieder auf das Standarddesign von ggplot2 zurück. Sie können dies mit einer Codezeile tun:

theme_set(theme_gray())

3. Rufen Sie die Cowplot-Funktionen auf, ohne das Paket anzuhängen. Sie können Cowplot-Funktionen auch nicht library(cowplot)oder require(cowplot)vorab aufrufen, indem Sie sie voranstellen cowplot::. Das obige Beispiel mit dem Standardthema ggplot2 würde beispielsweise wie folgt lauten:

## Commented out, we don't call this
# library(cowplot)

iris1 <- ggplot(iris, aes(x = Species, y = Sepal.Length)) +
  geom_boxplot()

iris2 <- ggplot(iris, aes(x = Sepal.Length, fill = Species)) +
  geom_density(alpha = 0.7) +
  theme(legend.position = c(0.8, 0.8))

cowplot::plot_grid(iris1, iris2, labels = "AUTO")

Geben Sie hier die Bildbeschreibung ein

Aktualisierung:

  • Ab Cowplot 1.0 wird das Standardthema ggplot2 nicht mehr geändert.
  • Ab ggplot2 3.0.0 können Plots direkt beschriftet werden, siehe zB hier.
Claus Wilke
quelle
Wird im Ausgabe-Cowplot das Hintergrundthema der beiden Plots entfernt? Gibt es eine Alternative?
VAR121
@ VAR121 Ja, es ist eine Codezeile. Erklärt am Ende des ersten Abschnitts der Einführungsvignette: cran.rstudio.com/web/packages/cowplot/vignettes/…
Claus Wilke
Ist es mit diesem Paket möglich, für alle Parzellen die gleiche y-Skala zu haben?
Herman Toothrot
Sie müssen die y-Skalen manuell so einstellen, dass sie übereinstimmen. Oder überlegen Sie sich Facettierung.
Claus Wilke
Sie könnten jedoch für jedes Diagramm einen ggtitle () festlegen, bevor Sie grid.arrange () verwenden.
Seanosapien
49

Sie können die folgende multiplotFunktion aus Winston Changs R-Kochbuch verwenden

multiplot(plot1, plot2, cols=2)

multiplot <- function(..., plotlist=NULL, cols) {
    require(grid)

    # Make a list from the ... arguments and plotlist
    plots <- c(list(...), plotlist)

    numPlots = length(plots)

    # Make the panel
    plotCols = cols                          # Number of columns of plots
    plotRows = ceiling(numPlots/plotCols) # Number of rows needed, calculated from # of cols

    # Set up the page
    grid.newpage()
    pushViewport(viewport(layout = grid.layout(plotRows, plotCols)))
    vplayout <- function(x, y)
        viewport(layout.pos.row = x, layout.pos.col = y)

    # Make each plot, in the correct location
    for (i in 1:numPlots) {
        curRow = ceiling(i/plotCols)
        curCol = (i-1) %% plotCols + 1
        print(plots[[i]], vp = vplayout(curRow, curCol ))
    }

}
David LeBauer
quelle
24

Mit dem Patchwork- Paket können Sie einfach den +Operator verwenden:

library(ggplot2)
library(patchwork)

p1 <- ggplot(mtcars) + geom_point(aes(mpg, disp))
p2 <- ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear))


p1 + p2

Patchwork

Deena
quelle
Der Vollständigkeit halber ist Patchwork jetzt auch auf CRAN verfügbar. Ich hoffe, Sie sind zufrieden mit meiner kleinen Bearbeitung
Tjebo
18

Ja, Sie müssen Ihre Daten entsprechend anordnen. Ein Weg wäre folgender:

X <- data.frame(x=rep(x,2),
                y=c(3*x+eps, 2*x+eps),
                case=rep(c("first","second"), each=100))

qplot(x, y, data=X, facets = . ~ case) + geom_smooth()

Ich bin mir sicher, dass es bessere Tricks bei Plyr oder Reshape gibt - ich bin immer noch nicht wirklich auf dem neuesten Stand bei all diesen leistungsstarken Paketen von Hadley.

Dirk Eddelbuettel
quelle
16

Mit dem Umformungspaket können Sie so etwas tun.

library(ggplot2)
wide <- data.frame(x = rnorm(100), eps = rnorm(100, 0, .2))
wide$first <- with(wide, 3 * x + eps)
wide$second <- with(wide, 2 * x + eps)
long <- melt(wide, id.vars = c("x", "eps"))
ggplot(long, aes(x = x, y = value)) + geom_smooth() + geom_point() + facet_grid(.~ variable)
Thierry
quelle
12

Es gibt auch ein Multipanelfigure-Paket , das es wert ist, erwähnt zu werden. Siehe auch diese Antwort .

library(ggplot2)
theme_set(theme_bw())

q1 <- ggplot(mtcars) + geom_point(aes(mpg, disp))
q2 <- ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear))
q3 <- ggplot(mtcars) + geom_smooth(aes(disp, qsec))
q4 <- ggplot(mtcars) + geom_bar(aes(carb))

library(magrittr)
library(multipanelfigure)
figure1 <- multi_panel_figure(columns = 2, rows = 2, panel_label_type = "none")
# show the layout
figure1

figure1 %<>%
  fill_panel(q1, column = 1, row = 1) %<>%
  fill_panel(q2, column = 2, row = 1) %<>%
  fill_panel(q3, column = 1, row = 2) %<>%
  fill_panel(q4, column = 2, row = 2)
figure1

# complex layout
figure2 <- multi_panel_figure(columns = 3, rows = 3, panel_label_type = "upper-roman")
figure2

figure2 %<>%
  fill_panel(q1, column = 1:2, row = 1) %<>%
  fill_panel(q2, column = 3, row = 1) %<>%
  fill_panel(q3, column = 1, row = 2) %<>%
  fill_panel(q4, column = 2:3, row = 2:3)
figure2

Erstellt am 06.07.2018 durch das reprex-Paket (v0.2.0.9000).

Tung
quelle
9

ggplot2 basiert auf Rastergrafiken, die ein anderes System zum Anordnen von Plots auf einer Seite bieten. Der par(mfrow...)Befehl hat keine direkte Entsprechung, da Rasterobjekte ( Grobs genannt ) nicht unbedingt sofort gezeichnet werden, sondern als reguläre R-Objekte gespeichert und bearbeitet werden können, bevor sie in eine grafische Ausgabe konvertiert werden. Dies ermöglicht eine größere Flexibilität als die Auslosung Modells von Basisgrafiken, aber die Strategie ist notwendigerweise etwas anders.

Ich habe geschrieben grid.arrange(), um eine einfache Schnittstelle so nah wie möglich zu bieten par(mfrow). In seiner einfachsten Form würde der Code folgendermaßen aussehen:

library(ggplot2)
x <- rnorm(100)
eps <- rnorm(100,0,.2)
p1 <- qplot(x,3*x+eps)
p2 <- qplot(x,2*x+eps)

library(gridExtra)
grid.arrange(p1, p2, ncol = 2)

Geben Sie hier die Bildbeschreibung ein

Weitere Optionen finden Sie in dieser Vignette .

Eine häufige Beschwerde ist, dass Diagramme nicht unbedingt ausgerichtet sind, z. B. wenn sie Achsenbeschriftungen unterschiedlicher Größe haben. Dies ist jedoch beabsichtigt: grid.arrange beabsichtigt wird kein Versuch unternommen, ggplot2-Objekte in Sonderfällen zu erstellen, und sie werden auch für andere Grobs (z. B. Gitterdiagramme) gleich behandelt ). Es werden lediglich Grobs in einem rechteckigen Layout platziert.

Für den Sonderfall von ggplot2-Objekten habe ich eine andere Funktion ggarrangemit einer ähnlichen Oberfläche geschrieben, die versucht, Plotfelder (einschließlich facettierter Plots) auszurichten und die Seitenverhältnisse zu berücksichtigen, wenn sie vom Benutzer definiert werden.

library(egg)
ggarrange(p1, p2, ncol = 2)

Beide Funktionen sind kompatibel mit ggsave(). Für einen allgemeinen Überblick über die verschiedenen Optionen und einige historische Zusammenhänge bietet diese Vignette zusätzliche Informationen .

Baptiste
quelle
9

Update: Diese Antwort ist sehr alt. gridExtra::grid.arrange()ist jetzt der empfohlene Ansatz. Ich lasse dies hier, falls es nützlich sein könnte.


Stephen Turner hat die arrange()Funktion im Blog Getting Genetics Done veröffentlicht (Anweisungen zur Anwendung finden Sie im Beitrag).

vp.layout <- function(x, y) viewport(layout.pos.row=x, layout.pos.col=y)
arrange <- function(..., nrow=NULL, ncol=NULL, as.table=FALSE) {
 dots <- list(...)
 n <- length(dots)
 if(is.null(nrow) & is.null(ncol)) { nrow = floor(n/2) ; ncol = ceiling(n/nrow)}
 if(is.null(nrow)) { nrow = ceiling(n/ncol)}
 if(is.null(ncol)) { ncol = ceiling(n/nrow)}
        ## NOTE see n2mfrow in grDevices for possible alternative
grid.newpage()
pushViewport(viewport(layout=grid.layout(nrow,ncol) ) )
 ii.p <- 1
 for(ii.row in seq(1, nrow)){
 ii.table.row <- ii.row 
 if(as.table) {ii.table.row <- nrow - ii.table.row + 1}
  for(ii.col in seq(1, ncol)){
   ii.table <- ii.p
   if(ii.p > n) break
   print(dots[[ii.table]], vp=vp.layout(ii.table.row, ii.col))
   ii.p <- ii.p + 1
  }
 }
}
Jeromy Anglim
quelle
9
Es ist im Grunde eine sehr veraltete Version von grid.arrange(ich wünschte, ich hätte sie zu diesem Zeitpunkt nicht auf Mailinglisten veröffentlicht - es gibt keine Möglichkeit, diese Online-Ressourcen zu aktualisieren). Die verpackte Version ist eine bessere Wahl, wenn Sie mich fragen
baptiste
4

Verwenden von tidyverse:

x <- rnorm(100)
eps <- rnorm(100,0,.2)
df <- data.frame(x, eps) %>% 
  mutate(p1 = 3*x+eps, p2 = 2*x+eps) %>% 
  tidyr::gather("plot", "value", 3:4) %>% 
  ggplot(aes(x = x , y = value)) + 
    geom_point() + 
    geom_smooth() + 
    facet_wrap(~plot, ncol =2)

df

Geben Sie hier die Bildbeschreibung ein

glänzend
quelle
1

Die obigen Lösungen sind möglicherweise nicht effizient, wenn Sie mehrere ggplot-Diagramme mit einer Schleife zeichnen möchten (z. B. wie hier gefragt: Erstellen mehrerer Diagramme in ggplot mit unterschiedlichen Y-Achsenwerten mithilfe einer Schleife ). Dies ist ein gewünschter Schritt bei der Analyse des Unbekannten ( oder große) Datensätze (z. B. wenn Sie die Anzahl aller Variablen in einem Datensatz zeichnen möchten).

Der folgende Code zeigt, wie dies mit dem oben genannten 'multiplot ()' gemacht wird, dessen Quelle hier angegeben ist: http://www.cookbook-r.com/Graphs/Multiple_graphs_on_one_page_(ggplot2) :

plotAllCounts <- function (dt){   
  plots <- list();
  for(i in 1:ncol(dt)) {
    strX = names(dt)[i]
    print(sprintf("%i: strX = %s", i, strX))
    plots[[i]] <- ggplot(dt) + xlab(strX) +
      geom_point(aes_string(strX),stat="count")
  }

  columnsToPlot <- floor(sqrt(ncol(dt)))
  multiplot(plotlist = plots, cols = columnsToPlot)
}

Führen Sie nun die Funktion aus, um die Anzahl der mit ggplot gedruckten Variablen auf einer Seite abzurufen

dt = ggplot2::diamonds
plotAllCounts(dt)

Eine Sache , die Sie beachten sollten, ist folgende: Wenn Sie im obigen Code anstelle von
verwenden aes(get(strX)), was Sie normalerweise in Schleifen verwenden würden ggplot, aes_string(strX)werden die gewünschten Diagramme NICHT gezeichnet. Stattdessen wird der letzte Plot viele Male geplottet. Ich habe nicht herausgefunden warum - es muss vielleicht das tun aesund aes_stringwird gerufen ggplot.

Ansonsten hoffe ich, dass Sie die Funktion nützlich finden.

IVIM
quelle
1
Beachten Sie, dass in Ihrem Code plotsObjekte wachsen , for-loopdie sehr ineffizient sind und in denen nicht empfohlen wird R. Bitte beachten Sie diese großen Beiträge , um herauszufinden , bessere Möglichkeiten , es zu tun: Effiziente Akkumulation in R , Anwenden einer Funktion über Zeilen eines Datenrahmens & Row orientierte Workflows in R mit der tidyverse
Tung
Eine effizientere Methode zum Durchlaufen von Variablen ist die Verwendung eines tidy evaluationAnsatzes, der seit ggplot2 v.3.0.0 stackoverflow.com/a/52045613/786542
Tung
0

Nach meiner Erfahrung funktioniert gridExtra: grid.arrange perfekt, wenn Sie versuchen, Diagramme in einer Schleife zu generieren.

Funktionscode-Snippet:

gridExtra::grid.arrange(plot1, plot2, ncol = 2)
Mayank Agrawal
quelle
Wie verbessert sich Ihre Antwort gegenüber der Antwort des Täufers vom 2. Dezember 17 um 4:20 Uhr? Ihre Antwort scheint ein Duplikat zu sein. Lesen Sie , was hier eine akzeptable Antwort macht wie man Antwort
Peter
Ich war nicht in der Lage, die Handlung nach Bedarf innerhalb einer Schleife aufzuteilen, und daher der Vorschlag. Anfangs habe ich das vollständige Snippet meiner for-Schleife mit ihrer Implementierung geschrieben, mich dann aber vorerst dagegen entschieden. Aktualisiert den vollständigen Code in ungefähr einer Woche.
Mayank Agrawal
Überprüfen Sie die akzeptierte Antwort von David LeBauer
Peter
Ich habe versucht, es mit dem Cowplot-Paket zu tun, war aber erfolglos. Bei meinem schnellen Scan hatte niemand mehrere Plotlösungen innerhalb einer for-Schleife erwähnt und daher mein Kommentar. Verweisen Sie mir einen Kommentar, wenn ich falsch liege.
Mayank Agrawal
Wenn der Code in Ihrer Antwort eine for-Schleife enthalten würde, wäre dies anders.
Peter
-3

Das cowplotPaket bietet Ihnen eine gute Möglichkeit, dies auf eine Weise zu tun, die zur Veröffentlichung passt.

x <- rnorm(100)
eps <- rnorm(100,0,.2)
A = qplot(x,3*x+eps, geom = c("point", "smooth"))+theme_gray()
B = qplot(x,2*x+eps, geom = c("point", "smooth"))+theme_gray()
cowplot::plot_grid(A, B, labels = c("A", "B"), align = "v")

Geben Sie hier die Bildbeschreibung ein

tim
quelle
3
Siehe auch die detailliertere Antwort und Begründung der Paketautoren
David LeBauer