Trame affiancate con ggplot2


339

Vorrei mettere due grafici fianco a fianco usando il pacchetto ggplot2 , cioè fare l'equivalente di par(mfrow=c(1,2)).

Ad esempio, vorrei che i seguenti due grafici fossero visualizzati fianco a fianco con la stessa scala.

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

Devo metterli nello stesso data.frame?

qplot(displ, hwy, data=mpg, facets = . ~ year) + geom_smooth()

Penso che potresti essere in grado di farlo con un reticolo. Ggplot2 è un requisito difficile?
JD Long

8
No. Ma avevo già messo il tempo a modificare le qplot, quindi era proprio come mi piaceva. :-) E sto provando a giocare con ggplot.
Christopher DuBois,


1
Per una bella panoramica, vedere la vignetta per il pacchetto di uova : Disposizione di più grafici su una pagina
Henrik

Risposte:


505

Qualsiasi ggplot affiancato (o n grafici su una griglia)

La funzione grid.arrange()nel gridExtrapacchetto combinerà più grafici; è così che ne metti due fianco a fianco.

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

Ciò è utile quando i due grafici non si basano sugli stessi dati, ad esempio se si desidera tracciare variabili diverse senza usare reshape ().

Questo traccia l'output come effetto collaterale. Per stampare l'effetto collaterale di un file, specificare un driver di periferica (come ad esempio pdf, pngecc), ad esempio,

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

oppure, utilizzare arrangeGrob()in combinazione con ggsave(),

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

Questo è l'equivalente di fare due distinti grafici usando par(mfrow = c(1,2)). Ciò non solo consente di risparmiare tempo nella disposizione dei dati, ma è necessario quando si desidera due grafici diversi.


Appendice: utilizzo delle sfaccettature

Le sfaccettature sono utili per creare trame simili per gruppi diversi. Questo è indicato di seguito in molte risposte di seguito, ma voglio evidenziare questo approccio con esempi equivalenti ai diagrammi di cui sopra.

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)

Aggiornare

vale plot_gridla cowplotpena verificare la funzione in alternativa a grid.arrange. Vedi la risposta di @ claus-wilke sotto e questa vignetta per un approccio equivalente; ma la funzione consente controlli più precisi su posizione e dimensioni della trama, in base a questa vignetta .


2
Quando ho eseguito il codice usando oggetti ggplot, sidebysideplot è null. Se si desidera salvare l'output in un file, utilizzare gridArrange. Vedi stackoverflow.com/questions/17059099/…
Jim

@Jim grazie per averlo sottolineato. Ho rivisto la mia risposta. Fammi sapere se rimangono delle domande.
David LeBauer,

1
Grid.aarange è deprecato adesso?
Atticus29,

?grid.arrangemi fa pensare che questa funzione sia ora chiamata arrangiamenti. Sono stato in grado di fare quello che volevo facendo a <- arrangeGrob(p1, p2)e poi print(a).
Blakeoft,

@blakeoft hai guardato gli esempi? grid.arrangeè ancora una funzione valida, non deprecata. Hai provato a usare la funzione? Cosa succede, se non quello che ti aspettavi.
David LeBauer,

159

Un aspetto negativo delle soluzioni basate su grid.arrange è che rendono difficile etichettare i grafici con lettere (A, B, ecc.), Come richiedono la maggior parte delle riviste.

Ho scritto il pacchetto cowplot per risolvere questo (e pochi altri) problemi, in particolare la funzione 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")

inserisci qui la descrizione dell'immagine

L'oggetto che plot_grid()ritorna è un altro oggetto ggplot2 e puoi salvarlo ggsave()come al solito:

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

In alternativa, è possibile utilizzare la funzione cowplot save_plot(), che è un involucro sottile ggsave()che facilita il raggiungimento delle dimensioni corrette per i grafici combinati, ad esempio:

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

(L' ncol = 2argomento dice save_plot()che ci sono due grafici affiancati e save_plot()rende l'immagine salvata due volte più larga.)

Per una descrizione più approfondita di come disporre i grafici in una griglia, vedere questa vignetta. C'è anche una vignetta che spiega come creare grafici con a legenda condivisa.

Un frequente punto di confusione è che il pacchetto cowplot modifica il tema predefinito di ggplot2. Il pacchetto si comporta in questo modo perché è stato originariamente scritto per usi di laboratorio interni e non utilizziamo mai il tema predefinito. Se ciò causa problemi, è possibile utilizzare uno dei seguenti tre approcci per aggirarli:

1. Imposta il tema manualmente per ogni trama. Penso che sia buona norma specificare sempre un tema particolare per ogni trama, proprio come ho fatto con+ theme_bw() nell'esempio sopra. Se si specifica un tema particolare, il tema predefinito non ha importanza.

2. Ripristina il tema predefinito al valore predefinito di ggplot2. Puoi farlo con una riga di codice:

theme_set(theme_gray())

3. Chiamare le funzioni cowplot senza collegare il pacchetto. Non è inoltre possibile chiamare library(cowplot)o require(cowplot)e invece chiamare le funzioni cowplot anteponendo cowplot::. Ad esempio, l'esempio sopra usando il tema predefinito ggplot2 diventerebbe:

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

inserisci qui la descrizione dell'immagine

aggiornamenti:

  • A partire da cowplot 1.0, il tema predefinito di ggplot2 non viene più modificato.
  • A partire da ggplot2 3.0.0, i grafici possono essere etichettati direttamente, vedere ad esempio qui.

nell'output cowplot si sta rimuovendo il tema di sfondo di entrambi i grafici? c'è qualche alternativa?
VAR121

@ VAR121 Sì, è una riga di codice. Spiegato alla fine della prima sezione della vignetta introduttiva: cran.rstudio.com/web/packages/cowplot/vignettes/…
Claus Wilke

È possibile avere la stessa scala y per tutti i grafici con questo pacchetto?
Herman Toothrot,

Dovrai impostare manualmente le scale y affinché corrispondano. O considerare le sfaccettature.
Claus Wilke,

Potresti impostare un ggtitle () per ogni trama prima di usare grid.arrange () però?
Seanosapien,

49

È possibile utilizzare la seguente multiplotfunzione dal ricettario R di Winston Chang

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

}

24

Usando il pacchetto patchwork , puoi semplicemente usare l' +operatore:

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


Per completezza, ora anche il patchwork è su CRAN. Spero che tu sia felice con la mia piccola modifica
Tjebo,

18

Sì, penso che sia necessario disporre i dati in modo appropriato. Un modo sarebbe questo:

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

Sono sicuro che ci sono trucchi migliori in plyr o reshape - non sono ancora davvero al passo con tutti questi potenti pacchetti di Hadley.


16

Usando il pacchetto reshape puoi fare qualcosa del genere.

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)

12

C'è anche un pacchetto multipanelfigure che vale la pena menzionare. Vedi anche questa risposta .

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

Creato il 06-07-2018 dal pacchetto reprex (v0.2.0.9000).


9

ggplot2 si basa sulla grafica della griglia, che fornisce un sistema diverso per organizzare i grafici su una pagina. Il par(mfrow...)comando non ha un equivalente diretto, poiché gli oggetti della griglia (chiamati grobs ) non sono necessariamente disegnati immediatamente, ma possono essere memorizzati e manipolati come normali oggetti R prima di essere convertiti in un output grafico. Ciò consente una maggiore flessibilità rispetto al sorteggio ora modello di grafica di base, ma la strategia è necessariamente leggermente diversa.

Ho scritto grid.arrange()per fornire un'interfaccia semplice il più vicino possibile par(mfrow). Nella sua forma più semplice, il codice sarebbe simile a:

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)

inserisci qui la descrizione dell'immagine

Altre opzioni sono dettagliate in questa vignetta .

Una lamentela comune è che i grafici non sono necessariamente allineati, ad esempio quando hanno etichette degli assi di dimensioni diverse, ma questo è di progettazione: grid.arrange non fa alcun tentativo di oggetti ggplot2 nel caso speciale e li tratta allo stesso modo ad altri grobs (grafici a traliccio, ad esempio ). Posiziona semplicemente grobs in una disposizione rettangolare.

Per il caso speciale degli oggetti ggplot2, ho scritto un'altra funzione, ggarrangecon un'interfaccia simile, che tenta di allineare i pannelli del diagramma (compresi i grafici sfaccettati) e cerca di rispettare le proporzioni quando definite dall'utente.

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

Entrambe le funzioni sono compatibili con ggsave(). Per una panoramica generale delle diverse opzioni e del contesto storico, questa vignetta offre ulteriori informazioni .


9

Aggiornamento: questa risposta è molto vecchia. gridExtra::grid.arrange()è ora l'approccio raccomandato. Lascio questo qui nel caso in cui possa essere utile.


Stephen Turner ha pubblicato la arrange()funzione sul blog Getting Genetics Done (vedi post per istruzioni sull'applicazione)

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

9
è fondamentalmente una versione molto obsoleta di grid.arrange(vorrei non averlo pubblicato sulle mailing list in quel momento - non c'è modo di aggiornare queste risorse online), la versione in pacchetto è una scelta migliore se me lo chiedi
baptiste

4

Utilizzando 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

inserisci qui la descrizione dell'immagine


1

Le soluzioni di cui sopra potrebbero non essere efficienti se si desidera tracciare più grafici ggplot utilizzando un ciclo (ad esempio, come richiesto qui: Creazione di più grafici in ggplot con valori dell'asse Y diversi utilizzando un ciclo ), che è un passaggio desiderato nell'analisi dell'ignoto ( o grandi) set di dati (ad esempio, quando si desidera tracciare i conteggi di tutte le variabili in un set di dati).

Il codice seguente mostra come farlo utilizzando il sopra menzionato 'multiplot ()', la cui fonte è qui: 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)
}

Ora esegui la funzione - per ottenere i conteggi per tutte le variabili stampate usando ggplot su una pagina

dt = ggplot2::diamonds
plotAllCounts(dt)

Una cosa da notare è che:
usando aes(get(strX)), che normalmente useresti in loop quando lavori ggplot, nel codice sopra invece aes_string(strX)NON disegnerai i grafici desiderati. Invece, traccerà l'ultima trama molte volte. Non ho capito il perché - potrebbe essere necessario eseguire aese aes_stringvengono chiamati ggplot.

Altrimenti, spero che troverai utile questa funzione.


1
Nota che il tuo codice fa crescere plotsoggetti all'interno dei for-loopquali è altamente inefficiente e non raccomandato in R. Si prega di vedere questi fantastici post per scoprire modi migliori per farlo: Accumulazione efficiente in R , Applicazione di una funzione su righe di un frame di dati e flussi di lavoro orientati alle righe in R con Tidyverse
Tung

Un modo più efficiente per scorrere le variabili è utilizzare l' tidy evaluationapproccio disponibile da ggplot2 v.3.0.0 stackoverflow.com/a/52045613/786542
Tung

0

Nella mia esperienza gridExtra: grid.arrange funziona perfettamente, se stai cercando di generare grafici in un ciclo.

Frammento di codice breve:

gridExtra::grid.arrange(plot1, plot2, ncol = 2)

In che modo la tua risposta migliora sulla risposta del battista del 2 dicembre 17 alle 4:20? La tua risposta sembra essere un duplicato. Scopri cosa rende accettabile una risposta qui Come rispondere
Peter,

Non sono stato in grado di dividere la trama secondo necessità all'interno di un ciclo, e quindi il suggerimento. Inizialmente, ho scritto l'intero frammento del mio for loop con la sua implementazione, ma per il momento ho deciso di non farlo. Aggiornerà il codice completo tra circa una settimana.
Mayank Agrawal,

L'ho provato per farlo usando il pacchetto cowplot in primo luogo ma non ci sono riuscito. Nella mia scansione rapida, nessuno aveva menzionato la soluzione di stampa multipla all'interno di un ciclo for e quindi il mio commento. Inviami qualsiasi commento se sbaglio.
Mayank Agrawal

Se il codice nella tua risposta includesse un ciclo for sarebbe diverso.
Peter,

Lo aggiornerei tra una settimana e qui e l'intero progetto su Kaggle. Saluti.
Mayank Agrawal

-3

Il cowplotpacchetto offre un modo piacevole per farlo, in un modo che si adatta alla pubblicazione.

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

inserisci qui la descrizione dell'immagine


3
Vedi anche la risposta dei pacchetti autori più dettagliate e razionale sopra stackoverflow.com/a/31223588/199217
David LeBauer
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.