web-dev-qa-db-fra.com

Parcelles côte à côte avec ggplot2

Je voudrais placer deux parcelles 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 graphiques 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 nom data.fr?

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

Tout ggplots côte à côte (ou n parcelles sur une grille)

La fonction grid.arrange() dans le package gridExtra combinera plusieurs tracés; Voici comment vous mettez deux côte à côte.

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

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

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

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

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

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

Cela équivaut à créer deux tracés distincts en utilisant par(mfrow = c(1,2)). Cela permet non seulement de gagner du temps à organiser les données, mais également lorsque vous souhaitez deux graphiques différents.


Annexe: Utilisation des facettes

Les facettes sont utiles pour créer des parcelles similaires pour différents groupes. Ceci est indiqué ci-dessous dans de nombreuses réponses ci-dessous, mais je tiens à 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)

Mise à jour

la fonction plot_grid dans cowplot vaut la peine d'être vérifiée au lieu de grid.arrange. Voir le réponse par @ claus-wilke ci-dessous et cette vignette pour une approche équivalente; mais la fonction permet des contrôles plus précis sur l'emplacement et la taille du tracé, en fonction de cette vignette .

444
David LeBauer

Un inconvénient des solutions basées sur grid.arrange est qu’il est difficile d’étiqueter les parcelles avec des lettres (A, B, etc.), comme le demandent la plupart des revues.

J'ai écrit le paquet 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")

enter image description here

L'objet que plot_grid() renvoie 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)

Vous pouvez également utiliser la fonction cowplot save_plot(), qui est une mince enveloppe autour de ggsave() qui facilite l'obtention des dimensions correctes pour les graphes combinés, par exemple:

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

(L'argument ncol = 2 indique à save_plot() qu'il existe deux tracés côte à côte et que save_plot() agrandit l'image sauvegardée deux fois plus large.)

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

Un point de confusion fréquent est que le paquet cowplot change le thème par défaut de ggplot2. Le paquet se comporte de cette façon parce qu’il a été écrit à l’origine pour des utilisations internes en laboratoire et que nous n’utilisons jamais le thème par défaut. Si cela pose des problèmes, vous pouvez utiliser l'une des trois approches suivantes pour les résoudre:

1. Définissez le thème manuellement pour chaque parcelle. Je pense que c'est une bonne pratique de toujours spécifier un thème particulier pour chaque parcelle, 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 importe peu.

2. Retournez 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 de cowplot sans attacher le package. Vous ne pouvez pas non plus appeler library(cowplot) ou require(cowplot), mais appeler des fonctions de cowplot en faisant précéder 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")

enter image description here

Mises à jour:

  • À partir de cowplot 1.0, le thème par défaut de ggplot2 n’est plus modifié.
  • A partir de ggplot2 3.0.0, les parcelles peuvent être étiquetées directement, voir par exemple. ici.
130
Claus Wilke

Vous pouvez utiliser la fonction multiplot suivante de 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 ))
    }

}
46
David LeBauer

En utilisant le paquetage patchwork , vous pouvez simplement utiliser l’opérateur +:

# install.packages("devtools")
devtools::install_github("thomasp85/patchwork")

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

library(patchwork)
p1 + p2

patchwork

18
Deena

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 y a de meilleures astuces en plyr ou en refaçonnage - je ne suis pas encore vraiment au courant de tous ces puissants forfaits de Hadley.

17
Dirk Eddelbuettel

En utilisant le paquetage reshape, vous pouvez faire quelque chose comme ceci.

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

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


Stephen Turner posté la fonction arrange() sur Faire de la génétique blog (voir l'article 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
  }
 }
}
10
Jeromy Anglim

ggplot2 est basé sur des graphiques en grille, qui fournissent un système différent pour organiser les tracés sur une page. La commande par(mfrow...) n'a pas d'équivalent direct, car les objets de la 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 une sortie graphique. Cela permet une plus grande flexibilité que le modèle dessiné de maintenant des graphiques de base, mais la stratégie est nécessairement un peu différente.

J'ai écrit grid.arrange() pour fournir une interface simple aussi proche que possible de par(mfrow). Dans sa forme la plus simple, le code ressemblerait à ceci:

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)

enter image description here

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

Une plainte commune est que les parcelles ne sont pas nécessairement alignées, par exemple. quand ils ont des étiquettes d'axe de taille différente, mais cela est inhérent à leur conception: grid.arrange n'essaie pas de traiter les objets ggplot2 dans des cas spéciaux, et les traite de manière égale pour les autres objets (le tracé en treillis, par exemple). Il ne fait que placer des foulards dans une disposition rectangulaire.

Pour le cas particulier des objets ggplot2, j'ai écrit une autre fonction, ggarrange, avec une interface similaire, qui tente d'aligner les panneaux de tracé (y compris les tracés à facettes) et essaie de respecter les rapports de format 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
baptiste

Il y a aussi paquet multipanelfigure qui mérite d'être mentionné. Voir aussi ceci 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 paquetage reprex (v0.2.0.9000).

9
Tung

Utiliser 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

enter image description here

4
aelwan

Les solutions ci-dessus peuvent ne pas être efficaces si vous souhaitez tracer plusieurs tracés de ggplot à l'aide d'une boucle (par exemple, comme demandé ici: Création de plusieurs tracés dans ggplot avec différentes valeurs de l'axe Y à l'aide d'une boucle ), qui est une étape souhaitée dans l’analyse des ensembles de données inconnus (ou volumineux) (par exemple, lorsque vous souhaitez tracer le nombre de toutes les variables d’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, lancez la fonction - pour obtenir des comptes pour toutes les variables imprimées en utilisant ggplot sur une page

dt = ggplot2::diamonds
plotAllCounts(dt)

Une chose à noter est que:
en utilisant 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 dessine PAS les tracés souhaités. Au lieu de cela, il va tracer le dernier complot plusieurs fois. Je n'ai pas compris pourquoi - il pourrait être nécessaire de faire la aes et aes_string sont appelés dans ggplot.

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

1
IVIM

Le paquetage cowplot 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")

enter image description here

0
tim