Combiner plusieurs ggplots avec ggarrange (ggpubr)

Assemblez des graphiques en grille, étiquetez les panneaux, partagez une seule légende et exportez — le tout avec ggarrange()

Combinez plusieurs ggplots indépendants en une seule figure prête à publier dans R avec la fonction ggarrange() de ggpubr : disposez les graphiques en grille avec ncol/nrow, étiquetez les panneaux A/B/C, rassemblez une seule légende partagée, construisez des étendues inégales avec des appels imbriqués, ajoutez des graphiques de densité marginaux avec ggscatterhist(), annotez la figure entière, mêlez un tableau et du texte, et exportez-la dans un fichier.

Date de publication

12 juillet 2026

Modifié

12 juillet 2026

AstucePoints clés
  • ggarrange() [ggpubr] combine des ggplots indépendants en une seule figure — passez les graphiques ainsi que ncol / nrow pour une grille.
  • Étiquetez les panneaux avec labels = c("A", "B", "C") — les repères qu’un relecteur attend sur une figure multi-panneaux.
  • Une seule légende partagée pour toute la figure : common.legend = TRUE, legend = "bottom".
  • Des étendues inégales proviennent d’appels ggarrange() imbriqués plus widths / heights ; align = "v" aligne les panneaux.
  • Des graphiques de densité marginaux sur un nuage de points en un seul appel — ggscatterhist().
  • annotate_figure() ajoute un titre/une légende de figure ; ggexport() (ou ggsave()) l’écrit dans un fichier.
Obtenez le livre — R Graphics Essentials (PDF)

Introduction

Vous avez trois ou quatre graphiques terminés — une boîte à moustaches, un nuage de points, un tableau récapitulatif — et une revue les veut sous forme d’une seule figure, panneaux A/B/C, une légende unique. Les fonctions par(mfrow=) et layout() de R base ne fonctionnent pas sur les ggplots, il vous faut donc un outil qui parle ggplot.

Le package ggpubr du fondateur vous en offre un : ggarrange(). Il organise n’importe quel ensemble de ggplots en une grille, étiquette les panneaux, rassemble une légende partagée et — contrairement aux alternatives — pagine une longue liste de graphiques et exporte directement dans un fichier, le tout depuis un seul appel.

Deux distinctions rapides avant de commencer, pour que vous choisissiez le bon outil :

  • Le même graphique découpé par un groupe ? C’est du facettage, intégré à ggplot2 — voir facettes. N’organisez pas les facettes à la main.
  • Des graphiques indépendants en une seule figure ? C’est de l’organisation, et cette leçon couvre la manière ggpubr. La grammaire de ggplot2 a son propre outil basé sur des opérateurs, patchwork — le même travail, un idiome différent ; utilisez celui qui convient à votre flux de travail.

Chaque figure ci-dessous est rendue ici même à partir du code affiché, et vous pouvez réexécuter ou modifier n’importe lequel de ces exemples en direct à la fin.

Les graphiques que nous allons combiner

Nous construisons de petits graphiques à partir de trois jeux de données intégrés — ToothGrowth (longueur des dents selon la dose de vitamine C), mtcars (poids et consommation des voitures) et iris (mesures de fleurs) — à l’aide des fonctions de tracé de ggpubr (ggboxplot(), ggdotplot(), ggscatter()). Chacune prend un data frame plus des noms de colonnes sous forme de chaînes et renvoie un véritable objet ggplot que vous pouvez stocker dans une variable et combiner plus tard.

Comme chaque bloc de code de cette page est autonome (afin que vous puissiez le copier ou l’exécuter dans n’importe quel ordre), nous reconstruisons la poignée de graphiques dont nous avons besoin à l’intérieur de chaque bloc. Dans votre propre script, vous les construiriez une seule fois.

Organiser des graphiques sur une seule page

Stockez chaque graphique dans une variable, puis passez-les à ggarrange() avec la forme de la grille (ncol, nrow) et les labels des panneaux. Voici la figure que la plupart des gens recherchent — trois graphiques dans une grille 2×2, étiquetés A, B, C.

library(ggpubr)
ToothGrowth$dose <- factor(ToothGrowth$dose)

# Build three independent ggpubr plots
bxp <- ggboxplot(ToothGrowth, x = "dose", y = "len", color = "dose", palette = "jco")
dp  <- ggdotplot(ToothGrowth, x = "dose", y = "len", color = "dose", palette = "jco",
                 binwidth = 1)
sp  <- ggscatter(mtcars, x = "wt", y = "mpg", color = "#3a86d4", add = "reg.line")

# Combine them into one figure
ggarrange(bxp, dp, sp,
          labels = c("A", "B", "C"),
          ncol = 2, nrow = 2)

Three ggpubr plots arranged in a two-by-two grid and labelled A, B, C: a boxplot of tooth length by dose, a dot plot of the same, and a scatter plot of car weight versus mileage with a regression line.

Les trois arguments que vous utiliserez à chaque fois : ncol / nrow définissent la grille ; labels étiquette les panneaux ("AUTO" génère automatiquement A, B, C…) ; les graphiques remplissent la grille ligne par ligne par défaut (fixez byrow = FALSE pour remplir par colonne).

Partager une seule légende sur toute la figure

Lorsque plusieurs panneaux portent la même légende, la répéter gaspille de l’espace. Fixez common.legend = TRUE et placez-la une seule fois avec legend ("top", "bottom", "left" ou "right").

library(ggpubr)
ToothGrowth$dose <- factor(ToothGrowth$dose)

bxp <- ggboxplot(ToothGrowth, x = "dose", y = "len", color = "dose", palette = "jco")
dp  <- ggdotplot(ToothGrowth, x = "dose", y = "len", color = "dose", palette = "jco",
                 binwidth = 1)

ggarrange(bxp, dp,
          labels = c("A", "B"),
          common.legend = TRUE, legend = "bottom")

A boxplot and a dot plot of tooth length by dose side by side, labelled A and B, sharing a single colour legend along the bottom.

Pour que cela fonctionne, les panneaux doivent partager la même légende (même variable, même échelle). ggarrange() prend la légende du premier graphique, supprime les autres et la dessine une seule fois dans la marge.

Étendues inégales et dispositions imbriquées

Toutes les figures ne sont pas des grilles bien nettes. Pour qu’un graphique occupe toute une ligne pendant que deux autres se partagent la ligne en dessous, imbriquez un ggarrange() dans un autre. L’appel interne devient une seule cellule de l’appel externe.

library(ggpubr)
ToothGrowth$dose <- factor(ToothGrowth$dose)

bxp <- ggboxplot(ToothGrowth, x = "dose", y = "len", color = "dose", palette = "jco")
dp  <- ggdotplot(ToothGrowth, x = "dose", y = "len", color = "dose", palette = "jco",
                 binwidth = 1)
sp  <- ggscatter(mtcars, x = "wt", y = "mpg", color = "#3a86d4", add = "reg.line")

ggarrange(
  sp,                                                  # top row: one wide plot
  ggarrange(bxp, dp, ncol = 2, labels = c("B", "C")),  # bottom row: two panels
  nrow = 2,
  labels = "A"                                         # label for the top plot
)

A scatter plot spanning the full top row labelled A, with a boxplot and a dot plot side by side in the bottom row labelled B and C.

Deux arguments affinent les tailles et l’alignement :

  • widths / heights — tailles relatives des colonnes / lignes, par ex. heights = c(2, 1) rend la ligne du haut deux fois plus haute que celle du bas.
  • align"v" aligne les panneaux verticalement, "h" horizontalement, "hv" les deux. Utilisez-le chaque fois que des panneaux empilés ont des étiquettes d’axe de largeurs différentes et que leurs zones de tracé se désalignent (une courbe de survie au-dessus de sa table des risques en est le cas classique).

Nuage de points avec graphiques de densité marginaux

Un nuage de points avec une courbe de densité sur chaque axe est un classique de la data-viz — et dans ggpubr, c’est un seul appel, ggscatterhist(), et non une grille à quatre cellules construite à la main. Pointez-le vers vos colonnes x/y et une color de groupement ; margin.plot choisit le type marginal.

library(ggpubr)

ggscatterhist(
  iris, x = "Sepal.Length", y = "Sepal.Width",
  color = "Species", palette = "jco",
  margin.plot = "density",
  margin.params = list(fill = "Species", color = "black", size = 0.2)
)

A scatter plot of iris sepal length versus sepal width coloured by species, with a density curve for each species along the top and right margins.

Remplacez margin.plot = "density" par "boxplot" ou "histogram" pour changer les panneaux marginaux ; retirez l’argument color/group pour un nuage de points à une seule série. ggscatterhist() affiche la figure assemblée directement, c’est donc la voie la plus rapide vers cette disposition.

Annoter la figure entière

Une fois les panneaux organisés, annotate_figure() ajoute du texte autour de la figure combinée — un titre général, une légende indiquant la source des données, des étiquettes latérales sur les axes, ou un numéro de figure.

library(ggpubr)
ToothGrowth$dose <- factor(ToothGrowth$dose)

sp  <- ggscatter(mtcars, x = "wt", y = "mpg", color = "#3a86d4", add = "reg.line")
bxp <- ggboxplot(ToothGrowth, x = "dose", y = "len", color = "dose", palette = "jco")

figure <- ggarrange(sp, bxp, ncol = 1, nrow = 2)

annotate_figure(
  figure,
  top    = text_grob("Two views of the data", face = "bold", size = 14),
  bottom = text_grob("Data source: mtcars and ToothGrowth", face = "italic", size = 10),
  fig.lab = "Figure 1", fig.lab.face = "bold"
)

A scatter plot stacked above a boxplot, wrapped with a bold title on top, an italic data-source caption on the bottom, and a Figure 1 label.

text_grob() stylise chaque élément de texte (color, face, size, rot pour pivoter une étiquette latérale). annotate_figure() fonctionne sur la sortie de n’importe quelle fonction d’organisation, vous pouvez donc étiqueter une figure quelle que soit la manière dont elle a été assemblée.

Mêler un tableau et du texte aux graphiques

Une figure de résultats a souvent besoin de plus que des graphiques — un petit tableau récapitulatif et une légende ont leur place sur la même page. ggpubr transforme chacun en un ggplot pour que ggarrange() puisse les empiler : ggtexttable() dessine un data frame sous forme de tableau, ggparagraph() dessine un bloc de texte, et desc_statby() calcule le récapitulatif par groupe.

library(ggpubr)

# 1. A density plot
density.p <- ggdensity(iris, x = "Sepal.Length", fill = "Species", palette = "jco")

# 2. A summary table (mean, sd, n by species) drawn as a plot
stable <- desc_statby(iris, measure.var = "Sepal.Length", grps = "Species")
stable <- stable[, c("Species", "length", "mean", "sd")]
stable.p <- ggtexttable(stable, rows = NULL, theme = ttheme("mBlue"))

# 3. A text paragraph
text.p <- ggparagraph(
  paste("The iris data set records sepal and petal length and width for 50 flowers",
        "from each of three iris species: setosa, versicolor and virginica."),
  face = "italic", size = 11
)

# Stack the three, giving the plot the most room
ggarrange(density.p, stable.p, text.p,
          ncol = 1, nrow = 3,
          heights = c(1, 0.5, 0.4))

A density plot of iris sepal length by species on top, a summary statistics table in the middle, and an italic descriptive paragraph at the bottom, stacked into one figure.

ttheme() stylise le tableau — "mBlue", "mOrange", "light" et d’autres définissent le jeu de couleurs. Voir la leçon dédiée aux tableaux de texte pour un contrôle complet du style des cellules et des en-têtes.

Exporter la figure dans un fichier

ggarrange() renvoie un ggplot, donc ggsave() fonctionne directement. ggpubr fournit aussi ggexport(), qui pagine en plus une liste de graphiques et écrit du PDF/PNG/EPS. Ces fonctions écrivent sur le disque, exécutez-les donc dans votre propre session :

library(ggpubr)
ToothGrowth$dose <- factor(ToothGrowth$dose)
bxp <- ggboxplot(ToothGrowth, x = "dose", y = "len", color = "dose", palette = "jco")

figure <- ggarrange(bxp, bxp, ncol = 2)

# ggpubr's exporter — PDF, PNG or EPS
ggexport(figure, filename = "figure.pdf", width = 8, height = 4)

# ...or ggplot2's ggsave, since ggarrange returns a ggplot
ggsave("figure.png", figure, width = 8, height = 4, dpi = 300)

De nombreux graphiques répartis sur plusieurs pages. Donnez à ggarrange() une longue liste plus ncol/nrow ; il calcule combien de pages il lui faut et renvoie une figure organisée par page, que ggexport() écrit sous forme de PDF multi-pages :

# 4 plots, 2 per page -> a 2-page PDF
multi.page <- ggarrange(bxp, bxp, bxp, bxp, ncol = 2, nrow = 1)
ggexport(multi.page, filename = "plots.pdf")

D’autres packages que vous croiserez. ggarrange() est une surcouche de plot_grid() de cowplot ; le grid.arrange() de gridExtra et le plot_grid() de cowplot font aussi la grille de base. ggarrange ajoute la légende partagée, les étendues imbriquées, l’export multi-pages et annotate_figure() dans une seule interface cohérente, c’est pourquoi nous l’enseignons comme choix par défaut ici.

Essayez en direct

Les graphiques ci-dessus ont été rendus au moment de la construction. Envie d’expérimenter ? Modifiez le code et appuyez sur Run — il s’exécute dans votre navigateur via webR (aucun serveur, aucune installation). Essayez de changer ncol/nrow, de déplacer la legend, ou de désactiver common.legend.

🟢 Avec un agent IA

Demandez à Prova « j’ai organisé quatre ggplots avec ggarrange() mais les panneaux ne s’alignent pas — comment utiliser align et heights pour corriger cela ? » — elle répond avec du code que vous pouvez exécuter sur vos propres graphiques et vérifie les arguments align/widths/heights par rapport à votre disposition, de sorte que la correction soit reproductible. The runtime is the judge. Demander à Prova →

Problèmes fréquents

  • La légende partagée ne se regroupe pas. common.legend = TRUE ne fusionne que les légendes qui sont vraiment les mêmes (même attribut esthétique, même variable, même échelle). Si les panneaux associent des variables différentes à la couleur, ils ont légitimement besoin de légendes distinctes — donnez-en une à chaque graphique, ou harmonisez d’abord les échelles.
  • Des panneaux empilés sont mal alignés. Des étiquettes d’axe y de largeurs différentes décalent les zones de tracé. Ajoutez align = "v" (ou "hv") à ggarrange() pour qu’il aligne les panneaux, et pas seulement les cadres extérieurs.
  • ggexport() / ggsave() génère une erreur lors du rendu d’un document. Elles écrivent un fichier sur le disque — exécutez-les de manière interactive, pas au sein d’un build sans chemin accessible en écriture. Dans un rapport, enregistrez une fois et relisez l’image.
  • Les étiquettes de panneau chevauchent le graphique. Déplacez-les avec label.x / label.y (0–1 dans chaque panneau) ou restylisez-les avec font.label = list(size = 14, face = "bold").

Questions fréquentes

Stockez chaque graphique dans une variable, puis passez-les à ggarrange() [ggpubr] avec la forme de la grille : ggarrange(p1, p2, p3, ncol = 2, nrow = 2). Ajoutez labels = c("A", "B", "C") pour étiqueter les panneaux. C’est la propre fonction du fondateur et elle gère aussi les légendes partagées, les étendues imbriquées et l’export.

Utilisez le facettage (facet_wrap() / facet_grid()) quand un seul jeu de données est découpé en panneaux par une variable de groupement — même type de graphique, échelles partagées. Utilisez ggarrange() pour combiner des graphiques indépendants (un nuage de points à côté d’une boîte à moustaches à côté d’un tableau). Voir facettes pour la voie du facettage.

Passez common.legend = TRUE et fixez sa position avec legend, par ex. ggarrange(p1, p2, common.legend = TRUE, legend = "bottom"). Les panneaux doivent partager la même légende (même variable et même échelle) ; ggarrange() la dessine alors une seule fois dans la marge.

Imbriquez des appels ggarrange() : placez le graphique large dans l’appel externe et un ggarrange() interne (contenant la ligne de graphiques plus petits) comme cellule suivante — ggarrange(wide, ggarrange(p2, p3, ncol = 2), nrow = 2). Utilisez widths / heights pour définir les tailles relatives.

Enveloppez la figure organisée dans annotate_figure() : annotate_figure(figure, top = text_grob("My title", face = "bold"), bottom = text_grob("Data source", face = "italic")). text_grob() stylise chaque élément de texte et peut pivoter les étiquettes latérales avec rot = 90.

ggarrange() renvoie un ggplot, donc ggsave("figure.png", figure, width = 8, height = 4) fonctionne. ggpubr fournit aussi ggexport(figure, filename = "figure.pdf"), qui pagine en plus une longue liste de graphiques en un PDF multi-pages.

Testez vos connaissances

Complétez le code pour que la boîte à moustaches et le graphique de points soient disposés côte à côte, étiquetés A et B, et partagent une seule légende le long du bas.

# 1. Tag the panels:        labels = c("A", "B")
# 2. Merge the legends:     common.legend = TRUE
library(ggpubr) ToothGrowth$dose <- factor(ToothGrowth$dose) bxp <- ggboxplot(ToothGrowth, x = "dose", y = "len", color = "dose", palette = "jco") dp <- ggdotplot(ToothGrowth, x = "dose", y = "len", color = "dose", palette = "jco", binwidth = 1) ggarrange(bxp, dp, labels = c("A", "B"), common.legend = TRUE, legend = "bottom", ncol = 2, nrow = 1)
library(ggpubr)
ToothGrowth$dose <- factor(ToothGrowth$dose)

bxp <- ggboxplot(ToothGrowth, x = "dose", y = "len", color = "dose", palette = "jco")
dp  <- ggdotplot(ToothGrowth, x = "dose", y = "len", color = "dose", palette = "jco",
                 binwidth = 1)

ggarrange(bxp, dp,
          labels = c("A", "B"),
          common.legend = TRUE, legend = "bottom",
          ncol = 2, nrow = 1)

Vérification rapide. Vous empilez un graphique au-dessus de son tableau récapitulatif avec ggarrange(p, table, ncol = 1), mais le tableau est aussi haut que le graphique. Quel argument corrige les tailles relatives ?

heights — passez heights = c(2, 1) (ou similaire) à ggarrange() pour que le graphique obtienne deux fois l’espace du tableau. widths fait de même pour les colonnes.

Conclusion

Vous avez combiné des ggplots indépendants en une seule figure prête à publier avec ggarrange() — organisés en grille avec ncol/nrow, étiqueté les panneaux, rassemblé une seule légende partagée, construit des étendues inégales avec des appels imbriqués, ajouté des graphiques de densité marginaux avec ggscatterhist(), annoté la figure entière avec annotate_figure(), mêlé un tableau et du texte, et exportée avec ggexport() / ggsave(). Le schéma est toujours le même : construisez chaque graphique comme un objet, puis passez les objets à ggarrange().

Pour la manière d’assembler des graphiques avec des opérateurs selon la grammaire de ggplot2, voir combiner les graphiques avec patchwork ; pour des panneaux d’un seul jeu de données, utilisez le facettage.

Leçons connexes

Vous préférez un livre ? R Graphics Essentials est disponible en PDF téléchargeable — toutes les leçons de cette série, hors ligne et à vous.

Obtenir le livre (PDF) →Les leçons restent gratuites en ligne.
Cette page vous a-t-elle été utile ?

Prouvez que vous savez le faire. Maîtrisez toute la série ggpubr — suivez votre parcours, construisez des projets et obtenez un certificat.

Commencer gratuitement →

Passez à Pro — Prova illimité sur vos propres données et un certificat vérifiable qui atteste la compétence.

dès 15 $/mois facturé annuellement

Passer à Pro →

✓ Vous êtes Pro — continuez. The runtime is the judge.

Recevez les nouvelles leçons R & Python par e-mail

Pratique, reproductible, sans spam. Désinscription à tout moment.

Double opt-in. Nous ne partageons jamais votre e-mail.

Partager cette pageXLinkedInRedditHN
Note

Cette leçon est reproductible : les figures sont exécutées au moment de la construction (si elles s’affichent, le code fonctionne), et le bac à sable et le quiz se réexécutent en direct dans votre navigateur. The runtime is the judge.

Réutilisation

Citation

BibTeX
@online{2026,
  author = {},
  title = {Combiner plusieurs ggplots avec ggarrange (ggpubr)},
  date = {2026-07-12},
  url = {https://www.datanovia.com/learn/data-visualization/ggpubr/ggarrange},
  langid = {fr}
}
Veuillez citer ce travail comme suit :
“Combiner plusieurs ggplots avec ggarrange (ggpubr).” 2026. July 12. https://www.datanovia.com/learn/data-visualization/ggpubr/ggarrange.