ggplot2 vs matplotlib : guide côte à côte pour R et Python

Les mêmes graphiques dans les deux écosystèmes — et l’idée unique qui fait passer de l’un à l’autre

Programming

Une comparaison pratique, côte à côte, de ggplot2 (R) et matplotlib (Python) : le modèle mental grammaire des graphiques vs impératif, un tableau de correspondance concept-vers-syntaxe, les mêmes six graphiques (nuage de points, barres, courbes, boîte à moustaches, histogramme, facettes) dans les deux, plus le thème, les étiquettes et un honnête « lequel utiliser quand ».

Auteur·rice
Date de publication

14 juillet 2026

Modifié

14 juillet 2026

AstuceCe que vous trouverez ici
  • La seule différence de modèle mental entre ggplot2 et matplotlib — saisissez-la et le reste n’est que syntaxe.
  • Un tableau de correspondance : chaque idée de ggplot2 et son équivalent matplotlib.
  • Les mêmes six graphiques — nuage de points, barres, courbes, boîte à moustaches, histogramme, facettes — construits dans les deux.
  • Un honnête lequel utiliser quand, et la place de seaborn.

Si vous connaissez l’un de ces outils et devez désormais tracer dans l’autre, vous n’avez pas besoin d’une nouvelle philosophie des graphiques — vous avez besoin d’une carte. Ce guide est cette carte. Le code R ci-dessous s’exécute et produit la figure que vous voyez ; le Python est du matplotlib vérifié que vous pouvez copier-coller et exécuter, avec la figure résultante décrite en prose.

A wide ggplot2 scatter plot of miles-per-gallon against car weight, points coloured by cylinder count on a viridis scale, with a single dark linear trend line, on a clean theme_minimal grid.

La seule différence qui explique tout

ggplot2 est une bibliothèque déclarative fondée sur la grammaire des graphiques — l’idée, due à Leland Wilkinson et formalisée dans A Layered Grammar of Graphics de Hadley Wickham, selon laquelle tout graphique est un data frame plus une correspondance d’attributs esthétiques (quelle colonne pilote x, laquelle pilote la couleur) plus une ou plusieurs géométries (points, barres, courbes). Vous décrivez ce que vous voulez ; ggplot2 en déduit les axes, la légende et les échelles.

matplotlib est impératif. Vous disposez d’une Figure (le canevas) et d’un ou plusieurs Axes (une seule région de tracé), et vous lui adressez des commandes de dessin — « place ces points ici, définis ce titre, ajoute cette légende ». Rien n’est déduit : la coloration par groupe et la légende qui l’accompagne sont des étapes que vous écrivez.

C’est toute l’histoire. ggplot2 fait correspondre des données à une image ; matplotlib dessine une image. Une fois cela intégré, passer de l’un à l’autre devient mécanique :

Concept de la grammaire des graphiques ggplot2 (R) matplotlib (Python)
Les données ggplot(data = df) un DataFrame/des tableaux passés à chaque appel
Correspondance d’attributs esthétiques aes(x = wt, y = mpg) arguments positionnels : ax.scatter(df.wt, df.mpg)
Géométrie / couche geom_point(), geom_line(), geom_col() ax.scatter(), ax.plot(), ax.bar()
Colorer par groupe aes(color = cyl) — la légende est automatique boucler sur les groupes (ou passer un tableau de couleurs) ; construire la légende soi-même
Petits multiples (facettes) facet_wrap(~ cyl) une grille plt.subplots() que vous remplissez dans une boucle
Couche statistique geom_smooth(), stat_summary() la calculer soi-même, puis tracer le résultat
Échelle non linéaire scale_x_log10() ax.set_xscale("log")
Aspect général / thème theme_minimal() plt.style.use("seaborn-v0_8-whitegrid")
Titres et étiquettes d’axes labs(title =, x =, y =) ax.set_title(), ax.set_xlabel(), ax.set_ylabel()
Légende émise à partir de la correspondance ax.legend(), après avoir étiqueté chaque tracé

Gardez ce tableau ouvert les premières fois ; vous cesserez vite d’en avoir besoin.

Les mêmes six graphiques, dans les deux sens

Chaque graphique ci-dessous montre la version ggplot2 (exécutée — c’est la figure que vous voyez) et l’équivalent matplotlib. Les exemples R utilisent des jeux de données intégrés, de sorte que chaque bloc s’exécute de façon autonome ; les exemples Python construisent directement dans le code un petit tableau comparable, si bien qu’ils tournent avec rien de plus que numpy, pandas et matplotlib.

Nuage de points

Le cheval de bataille. Dans ggplot2, vous faites correspondre deux colonnes continues à x et y et ajoutez une couche geom_point() ; faire correspondre une troisième colonne à color dans aes() sépare les points en groupes et construit la légende en une seule étape. Dans matplotlib, il n’existe pas d’argument « colorer par groupe » — vous regroupez les données et appelez ax.scatter() une fois par groupe, en étiquetant chacun pour que ax.legend() ait quelque chose à afficher.

library(ggplot2)

ggplot(mtcars, aes(x = wt, y = mpg, color = factor(cyl))) +
  geom_point(size = 2.5, alpha = 0.9) +
  scale_color_viridis_d(end = 0.85) +
  labs(title = "Fuel economy vs. weight",
       x = "Weight (1000 lbs)", y = "Miles per gallon", color = "Cylinders") +
  theme_minimal(base_size = 12)

ggplot2 scatter plot of miles-per-gallon versus car weight from the mtcars data, points coloured by cylinder count (4, 6, 8) on a viridis scale, on a clean minimal grid.

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

rng = np.random.default_rng(123)
df = pd.DataFrame({
    "wt":  np.r_[rng.normal(2.5, 0.4, 16), rng.normal(3.8, 0.4, 16)],
    "mpg": np.r_[rng.normal(26, 3, 16),   rng.normal(16, 3, 16)],
    "cyl": ["4"] * 16 + ["8"] * 16,
})

fig, ax = plt.subplots(figsize=(6, 4))
for cyl, sub in df.groupby("cyl"):          # one scatter call per group
    ax.scatter(sub["wt"], sub["mpg"], label=cyl, alpha=0.8)
ax.set_title("Fuel economy vs. weight")
ax.set_xlabel("Weight (1000 lbs)")
ax.set_ylabel("Miles per gallon")
ax.legend(title="Cylinders")               # you ask for the legend explicitly
fig.tight_layout()
plt.show()

Sortie : un nuage de points avec deux nuées codées par couleur — voitures légères à haut mpg, voitures lourdes à bas mpg — et une légende « Cylinders ». La boucle groupby fait exactement ce que aes(color = cyl) fait pour vous dans ggplot2.

Diagramme en barres

Pour des barres de valeurs pré-calculées, le geom_col() de ggplot2 fait correspondre une catégorie à x et un nombre à y (utilisez geom_bar() quand vous voulez que ggplot2 compte les lignes pour vous). Le ax.bar() de matplotlib prend directement les catégories et les hauteurs.

library(ggplot2)

df_bar <- data.frame(category = c("A", "B", "C", "D"),
                     count = c(23, 17, 35, 29))

ggplot(df_bar, aes(x = category, y = count)) +
  geom_col(fill = "#3a86d4") +
  labs(title = "Counts by category", x = "Category", y = "Count") +
  theme_minimal(base_size = 12)

ggplot2 bar chart of counts for four categories A to D in brand azure on a minimal grid.

import pandas as pd
import matplotlib.pyplot as plt

df_bar = pd.DataFrame({"category": ["A", "B", "C", "D"],
                       "count": [23, 17, 35, 29]})

fig, ax = plt.subplots(figsize=(6, 4))
ax.bar(df_bar["category"], df_bar["count"], color="#3a86d4")
ax.set_title("Counts by category")
ax.set_xlabel("Category")
ax.set_ylabel("Count")
fig.tight_layout()
plt.show()

Sortie : quatre barres azur de hauteurs 23, 17, 35, 29. Même couleur, mêmes nombres, même résultat — la seule différence est que ggplot2 déduit l’axe des x discret à partir du facteur, tandis que matplotlib place les barres dans l’ordre où vous les passez.

Graphique en courbes

Une tendance selon une variable ordonnée. geom_line() relie les points dans l’ordre des x ; ajouter une couche geom_point() marque chaque observation. Dans matplotlib, ax.plot() trace la courbe et l’argument marker="o" ajoute les points dans le même appel.

library(ggplot2)

df_line <- data.frame(month = 1:12,
                       value = c(5, 9, 12, 10, 14, 18, 21, 19, 23, 25, 24, 28))

ggplot(df_line, aes(x = month, y = value)) +
  geom_line(color = "#3a86d4", linewidth = 1.1) +
  geom_point(color = "#3a86d4", size = 2) +
  scale_x_continuous(breaks = 1:12) +
  labs(title = "Monthly trend", x = "Month", y = "Value") +
  theme_minimal(base_size = 12)

ggplot2 line chart of a value rising over twelve months, azure line with point markers on a minimal grid.

import pandas as pd
import matplotlib.pyplot as plt

df_line = pd.DataFrame({
    "month": range(1, 13),
    "value": [5, 9, 12, 10, 14, 18, 21, 19, 23, 25, 24, 28],
})

fig, ax = plt.subplots(figsize=(6, 4))
ax.plot(df_line["month"], df_line["value"],
        color="#3a86d4", linewidth=2, marker="o")   # line + markers in one call
ax.set_xticks(range(1, 13))
ax.set_title("Monthly trend")
ax.set_xlabel("Month")
ax.set_ylabel("Value")
fig.tight_layout()
plt.show()

Sortie : une courbe azur ascendante avec un marqueur sur chacun des douze mois. geom_line() + geom_point() fait deux couches dans ggplot2 ; matplotlib fond les deux en un seul plot(..., marker="o").

Boîte à moustaches

Distribution par groupe — et un bon exemple du style maison : jamais une boîte nue. Dans ggplot2, nous superposons les points bruts avec geom_jitter() et marquons la moyenne de chaque groupe avec stat_summary(), de sorte que le lecteur voit les données, pas seulement le résumé. Le ax.boxplot() de matplotlib prend une liste de tableaux, un par groupe, vous scindez donc les données d’abord.

library(ggplot2)

ggplot(ToothGrowth, aes(x = factor(dose), y = len)) +
  geom_boxplot(fill = "#3a86d4", alpha = 0.3, outlier.shape = NA) +
  geom_jitter(width = 0.15, alpha = 0.5, color = "#3a86d4") +
  stat_summary(fun = mean, geom = "point",
               shape = 18, size = 3, color = "#e4572e") +
  labs(title = "Tooth length by dose", x = "Dose (mg/day)", y = "Length") +
  theme_minimal(base_size = 12)

ggplot2 boxplot of tooth length by vitamin-C dose (0.5, 1, 2 mg/day) from the ToothGrowth data, with jittered points and an orange mean marker per group, boxes in translucent azure.

import pandas as pd
import matplotlib.pyplot as plt

df = pd.DataFrame({
    "dose": ["0.5"] * 6 + ["1"] * 6 + ["2"] * 6,
    "len": [4.2, 11.5, 7.3, 5.8, 6.4, 10.0,
            16.5, 16.5, 15.2, 17.3, 22.5, 19.7,
            23.6, 18.5, 33.9, 25.5, 26.4, 32.5],
})

labels = ["0.5", "1", "2"]
groups = [df.loc[df["dose"] == d, "len"].values for d in labels]

fig, ax = plt.subplots(figsize=(6, 4))
ax.boxplot(groups)                 # one array per group
ax.set_xticklabels(labels)
ax.set_title("Tooth length by dose")
ax.set_xlabel("Dose (mg/day)")
ax.set_ylabel("Length")
fig.tight_layout()
plt.show()

Sortie : trois boîtes dont les médianes s’élèvent au fil des doses. matplotlib vous donne les boîtes ; si vous voulez les points dispersés et les marqueurs de moyenne qu’affiche la figure R, c’est là qu’une bibliothèque de plus haut niveau (seaborn, ci-dessous) justifie sa place.

Histogramme

Une seule variable continue, mise en classes. geom_histogram() n’a besoin que d’une correspondance x et d’un nombre de classes bins ; ax.hist() en est le jumeau direct.

library(ggplot2)

ggplot(faithful, aes(x = eruptions)) +
  geom_histogram(bins = 30, fill = "#3a86d4", color = "white") +
  labs(title = "Old Faithful eruption durations",
       x = "Eruption time (min)", y = "Count") +
  theme_minimal(base_size = 12)

ggplot2 histogram of Old Faithful eruption durations showing a clear bimodal shape, azure bars with white borders on a minimal grid.

import numpy as np
import matplotlib.pyplot as plt

rng = np.random.default_rng(42)
x = np.r_[rng.normal(2.0, 0.25, 90), rng.normal(4.3, 0.35, 180)]  # bimodal

fig, ax = plt.subplots(figsize=(6, 4))
ax.hist(x, bins=30, color="#3a86d4", edgecolor="white")
ax.set_title("Eruption durations")
ax.set_xlabel("Eruption time (min)")
ax.set_ylabel("Count")
fig.tight_layout()
plt.show()

Sortie : un histogramme bimodal à deux bosses nettes. L’argument bins signifie la même chose dans les deux — le nombre d’intervalles de largeur égale — si bien que son réglage est un travail identique dans l’une ou l’autre langue.

Facettes (petits multiples)

C’est ici que la grammaire des graphiques brille. facet_wrap(~ cyl) divise le graphique en un panneau par groupe avec des axes partagés et alignés — une seule ligne de code. matplotlib n’a pas de facettage ; vous créez une grille d’Axes avec plt.subplots() et remplissez chacun dans une boucle, en gérant vous-même les axes partagés.

library(ggplot2)

ggplot(mtcars, aes(x = wt, y = mpg)) +
  geom_point(color = "#3a86d4", size = 2, alpha = 0.9) +
  facet_wrap(~ cyl) +
  labs(title = "Fuel economy by cylinder count",
       x = "Weight (1000 lbs)", y = "MPG") +
  theme_minimal(base_size = 12)

ggplot2 faceted scatter plot: three panels (4, 6, 8 cylinders) of miles-per-gallon versus weight from mtcars, azure points, shared axes, minimal grid.

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

rng = np.random.default_rng(7)
df = pd.DataFrame({
    "wt":  rng.normal(3.0, 0.7, 90),
    "mpg": rng.normal(20, 5, 90),
    "cyl": np.repeat(["4", "6", "8"], 30),
})

cyls = ["4", "6", "8"]
fig, axes = plt.subplots(1, 3, figsize=(8, 3), sharey=True)   # the grid, by hand
for ax, cyl in zip(axes, cyls):
    sub = df[df["cyl"] == cyl]
    ax.scatter(sub["wt"], sub["mpg"], color="#3a86d4", alpha=0.8)
    ax.set_title(f"{cyl} cylinders")
    ax.set_xlabel("Weight")
axes[0].set_ylabel("MPG")
fig.tight_layout()
plt.show()

Sortie : trois panneaux côte à côte partageant un axe des y. Comparez l’effort : facet_wrap(~ cyl) face à la grille subplots explicite, la boucle et la gestion manuelle du sharey et des étiquettes. Le facettage est le plus grand écart ergonomique au quotidien entre les deux bibliothèques.

Thème et étiquettes

Les deux bibliothèques séparent ce que vous tracez de son apparence, mais s’y prennent différemment. Dans ggplot2, un thème est une apparence complète que vous ajoutez comme une couche — theme_minimal(), theme_bw(), theme_classic() — et vous ajustez les éléments individuels via theme(...). Dans matplotlib, vous définissez un style global une fois avec plt.style.use(...) (les styles "seaborn-v0_8-*" sont livrés avec matplotlib), et vous surchargez élément par élément via rcParams ou des appels Axes directs.

Tâche ggplot2 matplotlib
Appliquer un thème épuré + theme_minimal() plt.style.use("seaborn-v0_8-whitegrid")
Titre et étiquettes d’axes labs(title =, x =, y =) ax.set_title(), ax.set_xlabel(), ax.set_ylabel()
Pivoter les étiquettes de graduation x theme(axis.text.x = element_text(angle = 45)) ax.tick_params(axis="x", rotation=45)
Passer un axe en échelle logarithmique scale_y_log10() ax.set_yscale("log")
Retirer la légende theme(legend.position = "none") omettre ax.legend()
Enregistrer dans un fichier ggsave("plot.png", width = 6, height = 4, dpi = 300) fig.savefig("plot.png", dpi=300, bbox_inches="tight")

Le déclic mental : les thèmes ggplot2 sont des couches additives sur un graphique ; les styles matplotlib sont des réglages globaux de la session. Aucun n’est meilleur — mais si vous définissez un style matplotlib et vous demandez pourquoi votre graphique suivant a changé lui aussi, voilà pourquoi.

La place de seaborn

Si c’est le style déclaratif et conscient des jeux de données de ggplot2 qui vous manque en Python, seaborn est le pont le plus proche. Il repose sur matplotlib et, comme ggplot2, prend un DataFrame entier et fait correspondre les colonnes à des rôles — hue= est l’attribut esthétique color de seaborn, et il construit la légende pour vous :

import seaborn as sns          # illustrative — the declarative style, closest to ggplot2

sns.set_theme(style="whitegrid")
sns.scatterplot(data=df, x="wt", y="mpg", hue="cyl")   # hue == aes(color = cyl)
sns.boxplot(data=tooth, x="dose", y="len")

La plus récente interface seaborn.objects va plus loin, adoptant une API explicite de grammaire des graphiques (so.Plot(df, x=…, y=…).add(so.Dot())) qui se lit presque comme ggplot2. Pour des graphiques statistiques soignés en Python avec le moins de code possible, tournez-vous d’abord vers seaborn et ne descendez au matplotlib brut que lorsque vous avez besoin d’un contrôle fin. Les hue/col de seaborn vous donnent aussi la coloration par groupe et le facettage (sns.FacetGrid, ou col= dans les fonctions de niveau figure) — les deux choses que le matplotlib nu vous oblige à coder à la main.

Lequel utiliser quand

Oubliez le tribalisme — choisissez selon le contexte :

  • Utilisez ggplot2 quand votre travail vit dans R, quand vous construisez beaucoup de graphiques statistiques groupés ou à facettes, et quand la cohérence entre de nombreux graphiques compte. La grammaire paie le plus quand les graphiques se complexifient : facettage, couches multiples et légendes sont quasiment gratuits.
  • Utilisez matplotlib quand votre pile est Python, quand vous avez besoin d’un contrôle au pixel près (annotations personnalisées, encarts, mises en page inhabituelles), ou quand une autre bibliothèque (le .plot() de pandas, scikit-learn, seaborn) vous remet un Axes matplotlib à finir. C’est la fondation sur laquelle tout le reste du traçage Python est bâti.
  • Utilisez seaborn pour les graphiques statistiques en Python quand vous voulez une ergonomie proche de ggplot2 sans quitter l’écosystème — il couvre l’essentiel du traçage quotidien avec une fraction du code de matplotlib.
  • Ne combattez pas l’outil. Reproduire les légendes automatiques et le facettage de ggplot2 dans du matplotlib nu représente beaucoup de code ; se tourner vers seaborn là est le bon choix, pas une défaite. De même, forcer une figure sur mesure, fortement annotée, à travers ggplot2 peut être plus difficile que quelques appels matplotlib directs.

La bonne nouvelle pour quiconque navigue entre les deux langages : les concepts — données, correspondance, géométrie, échelle, facette, thème — sont les mêmes. Apprenez-les une fois et vous êtes à l’aise dans les deux.

Foire aux questions

Aucun n’est universellement meilleur — ils optimisent des choses différentes. La grammaire des graphiques de ggplot2 rend les graphiques statistiques groupés, à facettes et en couches concis et cohérents, ce qui lui donne l’avantage pour les graphiques statistiques exploratoires et de publication. matplotlib offre un contrôle de plus bas niveau, au pixel près, et constitue la fondation de toute la pile de traçage Python, ce qui lui donne l’avantage quand vous avez besoin de mises en page sur mesure ou que vous finissez une figure remise par une autre bibliothèque. Choisissez selon l’écosystème (R vs Python) et selon la part de groupement/facettage qu’implique votre travail.

Le plus proche est seaborn, conscient des jeux de données et doté d’un argument hue= qui se comporte comme l’attribut esthétique color de ggplot2, construisant légendes et facettes pour vous. Sa plus récente interface seaborn.objects adopte une API explicite de grammaire des graphiques qui se lit beaucoup comme ggplot2. Il existe aussi une bibliothèque plotnine qui porte la syntaxe de ggplot2 directement en Python (ggplot(df) + geom_point()), si vous voulez la grammaire exacte en Python.

Contrairement au aes(color = group) de ggplot2, matplotlib n’a pas d’argument unique « colorer par groupe ». Vous regroupez les données (par exemple avec le groupby de pandas) et appelez la fonction de traçage une fois par groupe, en étiquetant chacune pour que ax.legend() puisse construire la légende — voyez l’exemple de nuage de points ci-dessus. Si cela vous semble trop de code répétitif, le hue= de seaborn le fait en une ligne.

C’est l’idée que tout graphique statistique peut se décrire comme un data frame, une correspondance d’attributs esthétiques des colonnes vers des rôles visuels (x, y, couleur, taille), une ou plusieurs géométries (points, barres, courbes), des transformations statistiques optionnelles, des échelles et des facettes. ggplot2 l’implémente directement, et c’est pourquoi vous décrivez un graphique plutôt que de le dessiner. matplotlib est impératif et ne suit pas ce modèle, bien que l’interface objects de seaborn et plotnine apportent la grammaire à Python.

Pour aller plus loin

🟢 Avec un agent IA

Demandez à Prova « réécris ce nuage de points ggplot2 en matplotlib, coloré par groupe avec une légende » — collez votre propre graphique et elle répond avec du code que vous pouvez exécuter. The runtime is the judge. Demander à Prova →

Cette page vous a-t-elle été utile ?

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

Citation

BibTeX
@online{kassambara2026,
  author = {Kassambara, Alboukadel},
  title = {ggplot2 vs matplotlib : guide côte à côte pour R et Python},
  date = {2026-07-14},
  url = {https://www.datanovia.com/blog/ggplot2-vs-matplotlib},
  langid = {fr}
}
Veuillez citer ce travail comme suit :
Kassambara, Alboukadel. 2026. “ggplot2 vs matplotlib : guide côte à côte pour R et Python.” July 14. https://www.datanovia.com/blog/ggplot2-vs-matplotlib.