dplyr vs pandas : guide côte à côte pour R et Python

Les mêmes manipulations de données dans les deux langages — et l’idée unique qui fait passer de l’un à l’autre

Programming

Une comparaison pratique, côte à côte, de dplyr (R) et pandas (Python) : le modèle mental verbes-et-pipe vs méthodes-et-chaînage, un tableau de correspondance dplyr-vers-pandas, et les opérations essentielles — filter, select, mutate, arrange, group_by + summarise, jointures, distinct/count et pivot (long <-> large) — dans les deux, plus 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 dplyr et pandas — saisissez-la et le reste n’est que syntaxe.
  • Un tableau de correspondance : chaque verbe dplyr et son équivalent pandas.
  • Les manipulations essentielles — filter, select, mutate, arrange, group + summarise, join, distinct, pivot — dans les deux.
  • Un honnête lequel utiliser quand pour quiconque navigue entre R et Python.

Si vous savez manipuler des données dans l’un de ces outils et devez désormais le faire dans l’autre, vous n’avez pas besoin de réapprendre la manipulation de données — vous avez besoin d’une carte reliant les verbes que vous connaissez déjà à leur jumeau dans l’autre langage. Ce guide est cette carte. Le code R ci-dessous s’exécute et affiche le résultat que vous voyez ; le Python est du pandas vérifié que vous pouvez copier-coller et exécuter, avec chaque résultat décrit en prose.

A wide ggplot2 bar chart of mean miles-per-gallon for 4-, 6-, and 8-cylinder cars from the mtcars data, three azure bars descending left to right with the value labelled on each, on a clean theme_minimal grid.

La seule différence qui explique tout

dplyr vous donne un petit ensemble de verbesfilter(), select(), mutate(), arrange(), group_by(), summarise(), left_join() — dont chacun prend un data frame et renvoie un data frame. Vous les composez avec le pipe (|>, l’opérateur natif de R, ou l’ancien %>% de magrittr), qui passe le résultat d’une étape comme premier argument de la suivante. Un script dplyr se lit de haut en bas comme une phrase : prends ces données, puis filtre-les, puis groupe-les, puis résume-les.

pandas est orienté objet. Vos données vivent dans un DataFrame, et chaque opération est une méthode appelée sur cet objetdf.query(), df.assign(), df.sort_values(), df.groupby(), df.merge(). Vous les composez par chaînage de méthodes : df.query(...).groupby(...).agg(...), où chaque méthode renvoie un nouveau DataFrame sur lequel agit la méthode suivante.

C’est toute l’histoire. dplyr compose des verbes autonomes avec un pipe ; pandas enchaîne des méthodes sur un objet. Les deux sont des pipelines « data frame en entrée, data frame en sortie » — donc une fois que vous connaissez la correspondance verbe-vers-méthode, la traduction est mécanique :

Tâche dplyr (R) pandas (Python)
Garder les lignes selon une condition filter(score > 80) df[df["score"] > 80] ou df.query("score > 80")
Garder / retirer des colonnes select(name, score) df[["name", "score"]] / df.drop(columns=...)
Créer ou modifier une colonne mutate(pass = score >= 80) df.assign(pass=df["score"] >= 80)
Trier les lignes arrange(desc(score)) df.sort_values("score", ascending=False)
Grouper + résumer group_by(team) |> summarise(m = mean(score)) df.groupby("team").agg(m=("score", "mean"))
Renommer des colonnes rename(exam = score) df.rename(columns={"score": "exam"})
Lignes distinctes distinct(team) df[["team"]].drop_duplicates()
Compter les lignes par groupe count(team) df["team"].value_counts()
Joindre deux tables left_join(a, b, by = "id") pd.merge(a, b, on="id", how="left")
Long → large pivot_wider(names_from, values_from) df.pivot(index, columns, values)
Large → long pivot_longer(cols, names_to, values_to) df.melt(id_vars, var_name, value_name)
Enchaîner les étapes le pipe |> / %>% le chaînage de méthodes .a().b().c()

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

Les verbes essentiels, dans les deux sens

Chaque opération ci-dessous montre la version dplyr (exécutée — c’est la sortie que vous voyez) et l’équivalent pandas (vérifié, avec son résultat décrit). Pour rendre les deux côtés directement comparables, tous deux utilisent le même petit tableau de six personnes réparties sur deux équipes :

library(dplyr)

employees <- data.frame(
  name  = c("Ada", "Ben", "Cara", "Dan", "Eve", "Finn"),
  team  = c("A",   "B",   "A",    "B",   "A",   "B"),
  score = c(90,    70,    60,     95,    82,    78),
  years = c(2,     5,     3,      8,     4,     6)
)
employees
  name team score years
1  Ada    A    90     2
2  Ben    B    70     5
3 Cara    A    60     3
4  Dan    B    95     8
5  Eve    A    82     4
6 Finn    B    78     6

Le côté pandas construit le tableau identique avec pd.DataFrame, de sorte que vous pouvez faire correspondre chaque résultat ligne par ligne :

import pandas as pd

employees = pd.DataFrame({
    "name":  ["Ada", "Ben", "Cara", "Dan", "Eve", "Finn"],
    "team":  ["A",   "B",   "A",    "B",   "A",   "B"],
    "score": [90,    70,    60,     95,    82,    78],
    "years": [2,     5,     3,      8,     4,     6],
})

Filtrer les lignes

Ne garder que les lignes qui remplissent une condition. Le filter() de dplyr prend la condition directement. pandas a deux idiomes : un masque booléen (df[df["score"] > 80] — indexer le tableau avec une Series de True/False) ou le plus lisible df.query("score > 80").

library(dplyr)

employees <- data.frame(
  name  = c("Ada", "Ben", "Cara", "Dan", "Eve", "Finn"),
  team  = c("A", "B", "A", "B", "A", "B"),
  score = c(90, 70, 60, 95, 82, 78),
  years = c(2, 5, 3, 8, 4, 6)
)

employees |> filter(score > 80)
  name team score years
1  Ada    A    90     2
2  Dan    B    95     8
3  Eve    A    82     4
# boolean mask — the everyday idiom
employees[employees["score"] > 80]

# or, more readable for compound conditions
employees.query("score > 80")

Sortie : les deux renvoient les trois lignes dont le score dépasse 80 — Ada (90), Dan (95) et Eve (82) — identiques au résultat dplyr. Combinez les conditions avec &/| dans un masque (employees[(employees["score"] > 80) & (employees["team"] == "A")]) ou les mots simples and/or à l’intérieur de query().

Sélectionner et renommer des colonnes

select() garde (ou, avec -, retire) des colonnes par nom ; rename() change les noms de colonnes. Dans pandas, vous choisissez les colonnes en passant une liste de noms, df[["name", "score"]], et renommez avec un dictionnaire {old: new}.

library(dplyr)

employees <- data.frame(
  name  = c("Ada", "Ben", "Cara", "Dan", "Eve", "Finn"),
  team  = c("A", "B", "A", "B", "A", "B"),
  score = c(90, 70, 60, 95, 82, 78),
  years = c(2, 5, 3, 8, 4, 6)
)

employees |>
  select(name, score) |>
  rename(exam_score = score) |>
  head(3)
  name exam_score
1  Ada         90
2  Ben         70
3 Cara         60
(employees[["name", "score"]]
    .rename(columns={"score": "exam_score"})
    .head(3))

Sortie : un tableau à deux colonnes (name, exam_score) montrant les trois premières personnes — Ada 90, Ben 70, Cara 60. Notez le sens de rename : dplyr écrit new = old, pandas écrit {old: new} — facile à inverser.

Créer ou modifier une colonne

mutate() ajoute une nouvelle colonne (ou en écrase une existante) calculée à partir des autres. Le df.assign() de pandas est le jumeau adapté au chaînage ; la forme courante en place est df["new"] = ....

library(dplyr)

employees <- data.frame(
  name  = c("Ada", "Ben", "Cara", "Dan", "Eve", "Finn"),
  team  = c("A", "B", "A", "B", "A", "B"),
  score = c(90, 70, 60, 95, 82, 78),
  years = c(2, 5, 3, 8, 4, 6)
)

employees |>
  mutate(passed = score >= 80) |>
  head(3)
  name team score years passed
1  Ada    A    90     2   TRUE
2  Ben    B    70     5  FALSE
3 Cara    A    60     3  FALSE
# chain-friendly (returns a new frame)
employees.assign(passed=employees["score"] >= 80).head(3)

# or the everyday in-place form
employees["passed"] = employees["score"] >= 80

Sortie : le tableau gagne une colonne passed de True/FalseTrue pour Ada (90), False pour Ben (70) et Cara (60). assign() renvoie un nouveau tableau (utile dans un chaînage) ; df["passed"] = ... modifie en place.

Trier les lignes

arrange() trie par ordre croissant ; enveloppez une colonne dans desc() pour l’ordre décroissant. pandas utilise df.sort_values() avec ascending=False.

library(dplyr)

employees <- data.frame(
  name  = c("Ada", "Ben", "Cara", "Dan", "Eve", "Finn"),
  team  = c("A", "B", "A", "B", "A", "B"),
  score = c(90, 70, 60, 95, 82, 78),
  years = c(2, 5, 3, 8, 4, 6)
)

employees |> arrange(desc(score))
  name team score years
1  Dan    B    95     8
2  Ada    A    90     2
3  Eve    A    82     4
4 Finn    B    78     6
5  Ben    B    70     5
6 Cara    A    60     3
employees.sort_values("score", ascending=False)

Sortie : les six lignes ordonnées du score le plus élevé au plus bas — Dan (95), Ada (90), Eve (82), Finn (78), Ben (70), Cara (60). Triez selon plusieurs colonnes en passant une liste aux deux arguments, par exemple sort_values(["team", "score"], ascending=[True, False]).

Grouper et résumer

Le cœur de la plupart des analyses : scinder les données selon une colonne de regroupement, puis calculer un résumé par groupe. Le group_by() + summarise() de dplyr est le groupby() + agg() de pandas. La forme d’agrégation nommée de aggnew_name=("column", "function") — se lit presque exactement comme summarise().

library(dplyr)

employees <- data.frame(
  name  = c("Ada", "Ben", "Cara", "Dan", "Eve", "Finn"),
  team  = c("A", "B", "A", "B", "A", "B"),
  score = c(90, 70, 60, 95, 82, 78),
  years = c(2, 5, 3, 8, 4, 6)
)

employees |>
  group_by(team) |>
  summarise(avg_score = mean(score), n = n())
# A tibble: 2 × 3
  team  avg_score     n
  <chr>     <dbl> <int>
1 A          77.3     3
2 B          81       3
(employees
    .groupby("team", as_index=False)
    .agg(avg_score=("score", "mean"), n=("score", "size")))

Sortie : une ligne par équipe — l’équipe A a une moyenne de 77.3 sur 3 personnes, l’équipe B une moyenne de 81.0 sur 3. Deux détails pandas à connaître : as_index=False garde team comme une colonne normale (sinon elle devient l’index des lignes), et size compte les lignes comme le fait n(). Sans agrégation nommée, vous écririez employees.groupby("team")["score"].mean(), qui renvoie seulement les moyennes.

Lignes distinctes et comptage

distinct() supprime les lignes en double ; count() dénombre les lignes par groupe. Leurs jumeaux pandas sont drop_duplicates() et value_counts().

library(dplyr)

employees <- data.frame(
  name  = c("Ada", "Ben", "Cara", "Dan", "Eve", "Finn"),
  team  = c("A", "B", "A", "B", "A", "B"),
  score = c(90, 70, 60, 95, 82, 78),
  years = c(2, 5, 3, 8, 4, 6)
)

employees |> distinct(team)   # unique teams
  team
1    A
2    B
employees |> count(team)      # rows per team
  team n
1    A 3
2    B 3
employees[["team"]].drop_duplicates()   # unique teams -> A, B
employees["team"].value_counts()        # rows per team -> A: 3, B: 3

Sortie : drop_duplicates() renvoie les deux équipes distinctes (A, B) ; value_counts() en signale 3 lignes dans chacune. Un piège : value_counts() trie par fréquence (la plus grande d’abord) et renvoie une Series indexée par l’équipe, tandis que le count() de dplyr renvoie un data frame soigné à deux colonnes — ajoutez .reset_index(name="n") dans pandas si vous voulez la même forme.

Joindre deux tables

Combiner des tables sur une clé commune se fait avec left_join() / inner_join() dans dplyr et un seul pd.merge() avec un argument how= dans pandas. Une jointure gauche garde chaque ligne de la table de gauche (en remplissant les clés non appariées par NA en R, NaN en pandas) ; une jointure interne ne garde que les clés présentes dans les deux.

library(dplyr)

scores <- data.frame(id = c(1, 2, 3), math = c(90, 80, 70))
grades <- data.frame(id = c(2, 3, 4), grade = c("B", "C", "D"))

left_join(scores, grades, by = "id")    # keep all of `scores`
  id math grade
1  1   90  <NA>
2  2   80     B
3  3   70     C
inner_join(scores, grades, by = "id")   # keep only matching ids
  id math grade
1  2   80     B
2  3   70     C
pd.merge(scores, grades, on="id", how="left")    # keep all of `scores`
pd.merge(scores, grades, on="id", how="inner")   # keep only matching ids

Sortie : la jointure gauche renvoie les trois lignes de scores ; l’id 1 n’a pas de note, donc son grade vaut NaN (pandas) / NA (R). La jointure interne ne renvoie que les id 2 et 3, les deux présents dans les deux tables. how= accepte aussi "right" et "outer", correspondant aux right_join() et full_join() de dplyr.

Remodeler : long ↔︎ large

Passer d’une disposition longue à large est l’opération que les gens doivent le plus souvent rechercher. Le tidyr de dplyr fournit pivot_wider() et pivot_longer() ; pandas a pivot() et melt().

library(dplyr)
library(tidyr)

long <- data.frame(
  city    = c("Paris", "Paris", "Lyon", "Lyon"),
  quarter = c("Q1", "Q2", "Q1", "Q2"),
  sales   = c(10, 15, 8, 12)
)

# long -> wide: one column per quarter
wide <- long |>
  pivot_wider(names_from = quarter, values_from = sales)
wide
# A tibble: 2 × 3
  city     Q1    Q2
  <chr> <dbl> <dbl>
1 Paris    10    15
2 Lyon      8    12
# wide -> long: back to one row per city-quarter
wide |>
  pivot_longer(cols = c(Q1, Q2), names_to = "quarter", values_to = "sales")
# A tibble: 4 × 3
  city  quarter sales
  <chr> <chr>   <dbl>
1 Paris Q1         10
2 Paris Q2         15
3 Lyon  Q1          8
4 Lyon  Q2         12
long = pd.DataFrame({
    "city":    ["Paris", "Paris", "Lyon", "Lyon"],
    "quarter": ["Q1", "Q2", "Q1", "Q2"],
    "sales":   [10, 15, 8, 12],
})

# long -> wide  (pivot_wider)
wide = long.pivot(index="city", columns="quarter", values="sales").reset_index()

# wide -> long  (pivot_longer)
wide.melt(id_vars="city", var_name="quarter", value_name="sales")

Sortie : pivot/pivot_wider transforme les quatre lignes longues en une ligne par ville avec des colonnes Q1 et Q2 (Paris 10/15, Lyon 8/12) ; melt/pivot_longer inverse le tout pour revenir à une ligne par ville-trimestre. Deux remarques pandas : pivot() place l’index (city) dans l’index des lignes, donc .reset_index() le ramène à une colonne normale ; et pour les pivots agrégeants — des paires index/colonne en double qui doivent être sommées ou moyennées — utilisez pivot_table(..., aggfunc="mean"), le jumeau de pivot_wider(values_fn = mean).

Le pipe vs le chaînage de méthodes

Tout ce qui précède se compose. Une vraie analyse enchaîne les verbes — et c’est là que les deux modèles mentaux se côtoient le plus clairement. Disons que nous voulons le score moyen par équipe, mais seulement pour les personnes ayant obtenu au moins 75, trié de la meilleure équipe vers le bas :

library(dplyr)

employees <- data.frame(
  name  = c("Ada", "Ben", "Cara", "Dan", "Eve", "Finn"),
  team  = c("A", "B", "A", "B", "A", "B"),
  score = c(90, 70, 60, 95, 82, 78),
  years = c(2, 5, 3, 8, 4, 6)
)

employees |>
  filter(score >= 75) |>
  group_by(team) |>
  summarise(avg = mean(score)) |>
  arrange(desc(avg))
# A tibble: 2 × 2
  team    avg
  <chr> <dbl>
1 B      86.5
2 A      86  
(employees
    .query("score >= 75")
    .groupby("team", as_index=False)["score"].mean()
    .rename(columns={"score": "avg"})
    .sort_values("avg", ascending=False))

Sortie : les deux placent l’équipe B en premier (moyenne 86.5) puis l’équipe A (86.0), après avoir écarté les trois scores inférieurs à 75. Lisez les deux pipelines ligne par ligne — filter ↔︎ query, group_by |> summarise(mean) ↔︎ groupby(...).mean(), arrange(desc()) ↔︎ sort_values(ascending=False). Les parenthèses englobantes en Python sont ce qui vous permet de découper un chaînage de méthodes sur plusieurs lignes, comme le pipe vous permet de découper un chaînage dplyr.

Les deux styles convergent encore davantage si vous utilisez la méthode .pipe() de pandas ou les bibliothèques tierces siuba / tidypolars, qui portent les verbes de dplyr en Python presque à l’identique. Mais vous en avez rarement besoin — le simple chaînage de méthodes se lit déjà comme un pipeline propre, de haut en bas, une fois habitué aux parenthèses.

Lequel utiliser quand

Oubliez le tribalisme — choisissez selon le contexte :

  • Utilisez dplyr quand votre travail vit dans R, quand vous appréciez un vocabulaire de verbes réduit et cohérent qui se lit comme de la prose, et quand vous vous appuyez sur le reste du tidyverse (tidyr pour le remodelage, stringr, lubridate, ggplot2 en aval). Les verbes sont délibérément peu nombreux et composables ; les résumés groupés et les jointures sont particulièrement concis.
  • Utilisez pandas quand votre pile est Python, quand vous devez transmettre des données à scikit-learn, numpy ou un framework d’apprentissage profond, ou quand vous voulez l’écosystème profond et mûr qui l’entoure (séries temporelles, E/S pour tous les formats, merge/groupby à grande échelle). Il fait bien plus que les verbes de dplyr — c’est le data.frame de R, dplyr et tidyr réunis en un seul objet.
  • Ne combattez pas l’outil. L’index de pandas (les étiquettes de lignes qui survivent aux opérations et se réalignent discrètement entre les tableaux) n’a pas d’équivalent dplyr et déroute constamment les utilisateurs de R — pensez tôt à reset_index() et as_index=False. De même, l’évaluation non standard de dplyr (noms de colonnes nus, sans guillemets) semble magique aux utilisateurs de Python ; adoptez-la plutôt que de tout mettre entre guillemets.

La bonne nouvelle pour quiconque navigue entre les deux langages : les opérations — filter, select, mutate, arrange, group-and-summarise, join, reshape — sont la même poignée d’idées. Apprenez-les une fois et vous êtes à l’aise dans les deux.

Foire aux questions

Ils résolvent le même problème — manipuler des données tabulaires — avec une conception différente. dplyr est un petit ensemble de verbes autonomes composés avec un pipe ; pandas est un objet DataFrame doté de méthodes composées par chaînage. La plupart des verbes dplyr correspondent un à un à une méthode pandas (filterquery/masque, mutateassign, group_by + summarisegroupby + agg, left_joinmerge), donc la traduction entre eux est mécanique. Le plus grand écart conceptuel est l’index de lignes de pandas, qui n’a pas d’équivalent dplyr.

pandas couvre toutes les fonctionnalités de dplyr, simplement avec une syntaxe de chaînage de méthodes au lieu de verbes et d’un pipe. Si vous voulez la syntaxe de verbes exacte de dplyr en Python, des bibliothèques comme siuba et tidypolars la portent fidèlement (filter, mutate, summarize sur un DataFrame), et polars offre une API rapide fondée sur les expressions que beaucoup d’anciens utilisateurs de dplyr préfèrent. Pour la plupart des travaux, cependant, le simple chaînage de méthodes pandas est la norme et vous suffit.

Utilisez groupby() suivi de agg() avec l’agrégation nommée, qui se lit comme summarise() : df.groupby("team", as_index=False).agg(avg=("score", "mean"), n=("score", "size")). Ajoutez as_index=False pour que la colonne de regroupement reste une colonne normale au lieu de devenir l’index des lignes. Pour un seul résumé, vous pouvez raccourcir en df.groupby("team")["score"].mean().

pivot_wider() (long → large) correspond à df.pivot(index=, columns=, values=) — ou df.pivot_table(..., aggfunc=) quand le remodelage doit aussi agréger. pivot_longer() (large → long) correspond à df.melt(id_vars=, var_name=, value_name=). Après pivot(), appelez .reset_index() pour retransformer l’index en une colonne ordinaire, puisque pandas déplace la colonne d’index du pivot dans l’index des lignes.

Apprenez celui qui correspond à votre pile : dplyr si vous travaillez en R, pandas si vous travaillez en Python — les concepts se transfèrent dans les deux sens, donc le second n’est surtout qu’un changement de syntaxe. Si vous avez le choix, pandas a le marché de l’emploi le plus vaste et l’écosystème d’apprentissage automatique le plus profond, tandis que dplyr a la courbe d’apprentissage la plus douce et la plus lisible. Beaucoup de data scientists finissent par utiliser les deux et s’appuient sur une carte de correspondance comme le tableau ci-dessus.

Pour aller plus loin

🟢 Avec un agent IA

Demandez à Prova « réécris ce pipeline dplyr en pandas, en gardant le group-by summarise et la jointure » — collez votre propre code et elle répond avec une version 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 = {dplyr vs pandas : guide côte à côte pour R et Python},
  date = {2026-07-14},
  url = {https://www.datanovia.com/blog/dplyr-vs-pandas},
  langid = {fr}
}
Veuillez citer ce travail comme suit :
Kassambara, Alboukadel. 2026. “dplyr vs pandas : guide côte à côte pour R et Python.” July 14. https://www.datanovia.com/blog/dplyr-vs-pandas.