Tracés côte à côte avec ggplot2


339

Je voudrais placer deux tracés côte à côte en utilisant le paquet ggplot2 , c'est-à-dire faire l'équivalent de par(mfrow=c(1,2)).

Par exemple, j'aimerais que les deux tracés suivants s'affichent côte à côte avec la même échelle.

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

Dois-je les mettre dans le même data.frame?

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

Je pense que vous pourriez être en mesure de le faire avec un réseau. Ggplot2 est-il une exigence difficile?
JD Long

8
Non. Mais j'avais déjà mis du temps à peaufiner les qplots, donc c'était exactement ce que j'aimais. :-) Et j'essaie de jouer avec ggplot.
Christopher DuBois


1
Pour un bon aperçu, voir la vignette du paquet d'oeufs : Disposer plusieurs parcelles sur une page
Henrik

Réponses:


505

Tous les ggplots côte à côte (ou n tracés sur une grille)

La fonction grid.arrange()dans le gridExtrapackage combinera plusieurs tracés; c'est ainsi que vous en mettez deux côte à côte.

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

Cela est utile lorsque les deux tracés ne sont pas basés sur les mêmes données, par exemple si vous souhaitez tracer des variables différentes sans utiliser reshape ().

Cela tracera la sortie comme un effet secondaire. Pour imprimer l'effet secondaire à un fichier, spécifiez un pilote de périphérique ( par exemple pdf, png, etc.), par exemple

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

ou, utiliser arrangeGrob()en combinaison avec ggsave(),

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

C'est l'équivalent de faire deux parcelles distinctes en utilisant par(mfrow = c(1,2)). Cela permet non seulement de gagner du temps lors de l'organisation des données, mais également lorsque vous souhaitez créer deux tracés différents.


Annexe: Utilisation des facettes

Les facettes sont utiles pour créer des tracés similaires pour différents groupes. Ceci est souligné ci-dessous dans de nombreuses réponses ci-dessous, mais je veux souligner cette approche avec des exemples équivalents aux graphiques ci-dessus.

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)

Mettre à jour

la plot_gridfonction dans le cowplotvaut la peine d'être vérifiée comme alternative à grid.arrange. Voir la réponse de @ claus-wilke ci-dessous et cette vignette pour une approche équivalente; mais la fonction permet des contrôles plus fins sur l'emplacement et la taille du tracé, en fonction de cette vignette .


2
Lorsque j'ai exécuté votre code à l'aide d'objets ggplot, sidebysideplot est nul. Si vous souhaitez enregistrer la sortie dans un fichier, utilisez gridArrange. Voir stackoverflow.com/questions/17059099/…
Jim

@Jim, merci de l'avoir signalé. J'ai révisé ma réponse. Faites-moi savoir s'il reste des questions.
David LeBauer

1
Grid.aarange est-il désormais privé?
Atticus29

?grid.arrangeme fait penser que cette fonction s'appelle maintenant arrangeGrob. J'ai pu faire ce que je voulais en faisant a <- arrangeGrob(p1, p2)et ensuite print(a).
blakeoft

@blakeoft avez-vous regardé les exemples? grid.arrangeest toujours une fonction valide et non obsolète. Avez-vous essayé d'utiliser la fonction? Que se passe-t-il, sinon ce que vous attendiez.
David LeBauer

159

Un inconvénient des solutions basées sur le grid.arrangefait qu'elles rendent difficile l'étiquetage des parcelles avec des lettres (A, B, etc.), comme la plupart des revues l'exigent.

J'ai écrit le package cowplot pour résoudre ce problème (et quelques autres), en particulier la fonction 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")

entrez la description de l'image ici

L'objet qui plot_grid()retourne est un autre objet ggplot2, et vous pouvez l'enregistrer avec ggsave()comme d'habitude:

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

Alternativement, vous pouvez utiliser la fonction cowplot save_plot(), qui est une fine enveloppe autour ggsave()qui permet d'obtenir facilement les bonnes dimensions pour les parcelles combinées, par exemple:

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

(L' ncol = 2argument indique save_plot()qu'il y a deux tracés côte à côte et save_plot()rend l'image enregistrée deux fois plus large.)

Pour une description plus approfondie de la façon d'organiser les tracés dans une grille, consultez cette vignette. Il existe également une vignette expliquant comment créer des tracés avec une légende partagée.

Un point de confusion fréquent est que le package cowplot change le thème ggplot2 par défaut. Le package se comporte de cette façon car il a été initialement écrit pour des utilisations en laboratoire interne, et nous n'utilisons jamais le thème par défaut. Si cela pose problème, vous pouvez utiliser l'une des trois approches suivantes pour les contourner:

1. Définissez le thème manuellement pour chaque intrigue. Je pense que c'est une bonne pratique de toujours spécifier un thème particulier pour chaque intrigue, comme je l'ai fait avec + theme_bw()dans l'exemple ci-dessus. Si vous spécifiez un thème particulier, le thème par défaut n'a pas d'importance.

2. Rétablissez le thème par défaut à la valeur par défaut ggplot2. Vous pouvez le faire avec une seule ligne de code:

theme_set(theme_gray())

3. Appelez les fonctions cowplot sans attacher le paquet. Vous ne pouvez pas non plus appeler library(cowplot)ou require(cowplot)et appeler à la place des fonctions cowplot en ajoutant le préfixe cowplot::. Par exemple, l'exemple ci-dessus utilisant le thème par défaut ggplot2 deviendrait:

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

entrez la description de l'image ici

Mises à jour:

  • Depuis cowplot 1.0, le thème ggplot2 par défaut n'est plus modifié.
  • Depuis ggplot2 3.0.0, les tracés peuvent être étiquetés directement, voir par exemple ici.

dans le cowplot de sortie supprime le thème d'arrière-plan, des deux tracés? existe-t-il une alternative?
VAR121

@ VAR121 Oui, c'est une ligne de code. Expliqué à la fin de la première section de la vignette d'introduction: cran.rstudio.com/web/packages/cowplot/vignettes/…
Claus Wilke

Est-il possible d'avoir la même échelle y pour toutes les parcelles avec ce package?
Herman Toothrot

Vous devrez définir les échelles y manuellement pour qu'elles correspondent. Ou pensez aux facettes.
Claus Wilke

Vous pouvez cependant définir un ggtitle () pour chaque tracé avant d'utiliser grid.arrange ()?
Seanosapien

49

Vous pouvez utiliser la multiplotfonction suivante du livre de recettes R de 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

En utilisant le package de patchwork , vous pouvez simplement utiliser l' +opérateur:

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


Juste pour être complet, le patchwork est maintenant aussi sur CRAN. J'espère que vous êtes satisfait de mon petit montage
Tjebo

18

Oui, je pense que vous devez organiser vos données de manière appropriée. Une façon serait la suivante:

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

Je suis sûr qu'il existe de meilleures astuces dans plyr ou remodeler - je ne suis toujours pas vraiment au courant de tous ces puissants packages de Hadley.


16

En utilisant le package de remodelage, vous pouvez faire quelque chose comme ça.

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

Il existe également un package multipanelfigure qui mérite d'être mentionné. Voir aussi cette réponse .

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

Créé le 2018-07-06 par le package reprex (v0.2.0.9000).


9

ggplot2 est basé sur des graphiques en grille, qui fournissent un système différent pour organiser les tracés sur une page. La par(mfrow...)commande n'a pas d'équivalent direct, car les objets de grille (appelés grobs ) ne sont pas nécessairement dessinés immédiatement, mais peuvent être stockés et manipulés en tant qu'objets R normaux avant d'être convertis en sortie graphique. Cela permet une plus grande flexibilité que le dessin de ce modèle de graphiques de base, mais la stratégie est nécessairement un peu différente.

J'ai écrit grid.arrange()pour fournir une interface simple au plus près par(mfrow). Dans sa forme la plus simple, le code ressemblerait à:

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)

entrez la description de l'image ici

Plus d'options sont détaillées dans cette vignette .

Une plainte courante est que les tracés ne sont pas nécessairement alignés, par exemple lorsqu'ils ont des étiquettes d'axe de taille différente, mais cela est de par leur conception: grid.arrangen'essaie pas de cas spécial ggplot2 objets, et les traite également à d'autres grobs (tracés en treillis, par exemple ). Il place simplement les grobs dans une disposition rectangulaire.

Pour le cas spécial des objets ggplot2, j'ai écrit une autre fonction, ggarrangeavec une interface similaire, qui tente d'aligner les panneaux de tracé (y compris les tracés à facettes) et essaie de respecter les rapports d'aspect lorsqu'ils sont définis par l'utilisateur.

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

Les deux fonctions sont compatibles avec ggsave(). Pour un aperçu général des différentes options et du contexte historique, cette vignette offre des informations supplémentaires .


9

Mise à jour: cette réponse est très ancienne. gridExtra::grid.arrange()est maintenant l'approche recommandée. Je laisse cela ici au cas où cela pourrait être utile.


Stephen Turner a publié la arrange()fonction sur le blog Getting Genetics Done (voir le post pour les instructions d'application)

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
il s'agit essentiellement d'une version très obsolète de grid.arrange(j'aurais aimé ne pas l'avoir publiée sur les listes de diffusion à l'époque - il n'y a aucun moyen de mettre à jour ces ressources en ligne), la version packagée est un meilleur choix si vous me le demandez
baptiste

4

En utilisant 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

entrez la description de l'image ici


1

Les solutions ci-dessus peuvent ne pas être efficaces si vous souhaitez tracer plusieurs tracés ggplot à l'aide d'une boucle (par exemple, comme demandé ici: Créer plusieurs tracés dans ggplot avec différentes valeurs de l'axe Y à l'aide d'une boucle ), ce qui est une étape souhaitée dans l'analyse de l'inconnu ( ou grands) ensembles de données (par exemple, lorsque vous voulez tracer le nombre de toutes les variables dans un ensemble de données).

Le code ci-dessous montre comment faire cela en utilisant le 'multiplot ()' mentionné ci-dessus, dont la source est ici: 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)
}

Maintenant, exécutez la fonction - pour obtenir les comptes pour toutes les variables imprimées à l'aide de ggplot sur une seule page

dt = ggplot2::diamonds
plotAllCounts(dt)

Une chose à noter est que: l'
utilisation aes(get(strX)), que vous utiliseriez normalement dans les boucles lorsque vous travaillez avec ggplot, dans le code ci-dessus au lieu de aes_string(strX)ne dessinera PAS les tracés souhaités. Au lieu de cela, il tracera le dernier tracé plusieurs fois. Je n'ai pas compris pourquoi - il se peut qu'il doive le faire aeset qu'il aes_stringsoit appelé ggplot.

Sinon, j'espère que vous trouverez la fonction utile.


1
Notez que votre code développe des plotsobjets dans for-looplesquels il est très inefficace et non recommandé dans R. Veuillez consulter ces excellents articles pour découvrir de meilleures façons de le faire: accumulation efficace dans R , application d'une fonction sur les lignes d'un bloc
Tung

Un moyen plus efficace de parcourir les variables consiste à utiliser l' tidy evaluationapproche qui est disponible depuis ggplot2 v.3.0.0 stackoverflow.com/a/52045613/786542
Tung

0

D'après mon expérience, gridExtra: grid.arrange fonctionne parfaitement si vous essayez de générer des tracés en boucle.

Extrait de code court:

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

Comment votre réponse améliore-t-elle la réponse du baptiste du 2 décembre 17 à 4:20? Votre réponse semble être un doublon. Découvrez ce qui fait une réponse acceptable ici Comment répondre
Peter

Je n'ai pas pu diviser l'intrigue selon les besoins dans une boucle, et donc la suggestion. Au départ, j'ai écrit l'extrait complet de ma boucle for avec son implémentation, mais j'ai décidé de ne pas le faire pour le moment. Met à jour le code complet dans une semaine environ.
Mayank Agrawal

Je l'ai essayé de le faire en utilisant le package cowplot en premier lieu, mais sans succès. Sur mon scan rapide, personne n'avait mentionné plusieurs solutions de traçage dans une boucle for et donc mon commentaire. Me renvoyer tout commentaire si je me trompe.
Mayank Agrawal

Si le code dans votre réponse incluait une boucle for, ce serait différent.
Peter

Je le mettrais à jour dans une semaine ici et avec tout le projet sur Kaggle. À votre santé.
Mayank Agrawal

-3

Le cowplotpackage vous offre un moyen agréable de le faire, d'une manière qui convient à la publication.

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

entrez la description de l'image ici


3
Voir également la réponse et la justification plus détaillées des auteurs du package ci-dessus stackoverflow.com/a/31223588/199217
David LeBauer
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.