Joindre et combiner des data frames en R : merge, jointures dplyr, rbind & cbind
Mettre en correspondance des tables sur une clé partagée avec des jointures, ou les empiler avec rbind et cbind
Combinez deux data frames en R — mettez-les en correspondance sur une clé partagée avec les jointures dplyr (inner_join(), left_join(), right_join(), full_join()) et le merge() de base R, conservez ou supprimez des lignes avec les jointures filtrantes semi_join()/anti_join(), et empilez des tables avec rbind()/bind_rows() ou accolez des colonnes avec cbind()/bind_cols() — les deux approches, côte à côte.
Date de publication
17 juillet 2026
Modifié
17 juillet 2026
AstucePoints clés
Les jointures mutantes ajoutent des colonnes d’une table correspondante à partir d’une clé partagée : inner_join() / left_join() / right_join() / full_join() en dplyr équivalent à merge(all = FALSE) / merge(all.x = TRUE) / merge(all.y = TRUE) / merge(all = TRUE) en base R.
inner_join() ne conserve que les lignes présentes dans les deux tables ; left_join() conserve chaque ligne de la première table et remplit avec NA là où la seconde n’a pas de correspondance.
Les jointures filtrantes conservent ou suppriment des lignes sans ajouter de colonnes :semi_join() conserve les lignes de x qui ont une correspondance, anti_join() conserve les lignes qui n’en ont aucune — la façon propre de demander « quels clients n’ont aucune commande ? ».
Assembler empile, ça ne fait pas correspondre :rbind() / bind_rows() empilent des lignes (mêmes colonnes) ; cbind() / bind_cols() accolent des colonnes (même ordre de lignes).
Assembler est positionnel et fragile — un seul mauvais ordre de lignes désaligne tout en silence. Lorsqu’une clé partagée existe, faites plutôt une jointure dessus.
Introduction
Vos données vivent rarement dans une seule table. Les commandes se trouvent dans un data frame, les clients qui les ont passées dans un autre ; les mesures dans un fichier, les métadonnées des échantillons dans un second. Pour les analyser, il vous les faut côte à côte — le montant de la commande à côté du nom du client, mis en correspondance sur l’id qu’ils partagent.
Deux tâches, souvent confondues :
Jointure (join) — mettre en correspondance deux tables sur une colonne clé partagée et réunir leurs colonnes (merge(), ou la famille *_join() de dplyr). C’est ce que vous voulez presque à chaque fois.
Assemblage (bind) — empiler les tables par position : des lignes sous des lignes (rbind()), ou des colonnes à côté de colonnes (cbind()). Aucune clé, aucune correspondance — juste un collage.
Cette leçon couvre les deux, dplyr et base R côte à côte, sur de petites tables construites à la main pour que vous voyiez exactement quelles lignes survivent à chaque opération. Nous utilisons deux petites tables qui partagent une colonne id :
Regardez les clés avant de faire la jointure. customers contient A, B, C. orders contient A, B (deux fois) et D. Ainsi, Carol (C) n’a aucune commande et la commande D n’a aucun client — les non-correspondances sont délibérées, car ce qui advient des lignes sans correspondance constitue toute la différence entre les jointures.
Jointures mutantes — apporter les colonnes d’une table correspondante
Une jointure mutante ajoute les colonnes d’une table à une autre, en les mettant en correspondance sur une clé. dplyr les nomme selon les lignes qu’elles conservent ; by = "id" désigne la clé partagée.
Inner join — ne conserver que les correspondances
inner_join() ne conserve que les lignes dont la clé apparaît dans les deux tables. Le merge() de base R fait la même chose avec sa valeur par défaut all = FALSE :
# base R — same resultmerge(customers, orders, by ="id") # all = FALSE is the default
id name amount
1 A Alice 50
2 B Bob 30
3 B Bob 20
Trois lignes reviennent : A une fois et B deux fois (Bob a passé deux commandes). Carol a disparu (aucune commande) et la commande D a disparu (aucun client) — une jointure interne supprime toute non-correspondance des deux côtés.
Left join — conserver chaque ligne de la première table
left_join() conserve toutes les lignes de la table de gauche (la première) et récupère les colonnes correspondantes de celle de droite ; là où la droite n’a pas de correspondance, les nouvelles colonnes sont remplies de NA. En base R, c’est merge(..., all.x = TRUE) :
id name amount
1 A Alice 50
2 B Bob 30
3 B Bob 20
4 C Carol NA
# base R — same resultmerge(customers, orders, by ="id", all.x =TRUE)
id name amount
1 A Alice 50
2 B Bob 30
3 B Bob 20
4 C Carol NA
Cette fois, Carol reste, avec amount = NA — c’est une cliente sans commande, et la jointure l’enregistre au lieu de la supprimer. La commande D a toujours disparu (elle est à droite). left_join() est le cheval de bataille du quotidien : « garder ma table, ajouter ce qui correspond ».
Right et full joins — conserver l’autre côté, ou les deux
right_join() est le miroir de left_join() — elle conserve chaque ligne de la table de droite (merge(..., all.y = TRUE)). full_join() conserve chaque ligne des deux, en remplissant de NA le côté manquant (merge(..., all = TRUE)) :
library(dplyr)customers <-data.frame(id =c("A", "B", "C"), name =c("Alice", "Bob", "Carol"))orders <-data.frame(id =c("A", "B", "B", "D"), amount =c(50, 30, 20, 40))# right join — keep every ORDER, even the one with no customer (D)right_join(customers, orders, by ="id")
id name amount
1 A Alice 50
2 B Bob 30
3 B Bob 20
4 D <NA> 40
# full join — keep everything from both (Carol AND order D)full_join(customers, orders, by ="id")
id name amount
1 A Alice 50
2 B Bob 30
3 B Bob 20
4 C Carol NA
5 D <NA> 40
La jointure droite fait apparaître la commande D avec name = NA (une commande dont le client est manquant). La jointure complète conserve les deux extrémités isolées — Carol (aucune commande) et la commande D (aucun client). Voici toute la famille en un coup d’œil :
dplyr
base R merge()
Conserve
inner_join(x, y)
merge(x, y)
uniquement les lignes présentes dans les deux
left_join(x, y)
merge(x, y, all.x = TRUE)
tout x, correspondances de y (sinon NA)
right_join(x, y)
merge(x, y, all.y = TRUE)
tout y, correspondances de x (sinon NA)
full_join(x, y)
merge(x, y, all = TRUE)
toutes les lignes des deux (sinon NA)
Une différence à connaître : merge()trie le résultat selon la clé par défaut, tandis que les jointures dplyr conservent l’ordre de lignes d’origine de la table de gauche. Passez sort = FALSE à merge() si vous devez préserver l’ordre.
Jointures filtrantes — conserver ou supprimer des lignes selon une correspondance, sans nouvelles colonnes
Parfois, vous ne voulez pas du tout des colonnes de l’autre table — vous voulez seulement filtrer une table selon l’existence ou non d’une correspondance. C’est une jointure filtrante, une commodité propre à dplyr (la base R le fait avec %in%).
semi_join(x, y) conserve les lignes de x qui ont une correspondance dans y — et, contrairement à inner_join(), elle renvoie une ligne par ligne de x et n’ajoute aucune colonne.
anti_join(x, y) conserve les lignes de x qui n’ont aucune correspondance dans y.
library(dplyr)customers <-data.frame(id =c("A", "B", "C"), name =c("Alice", "Bob", "Carol"))orders <-data.frame(id =c("A", "B", "B", "D"), amount =c(50, 30, 20, 40))# which customers HAVE placed an order?semi_join(customers, orders, by ="id")
id name
1 A Alice
2 B Bob
# which customers have NO order? (the classic question)anti_join(customers, orders, by ="id")
id name
1 C Carol
semi_join() renvoie Alice et Bob — une fois chacun, même si Bob a deux commandes (une jointure filtrante ne duplique jamais et n’ajoute jamais de colonnes). anti_join() ne renvoie que Carol : la cliente sans commande. Les équivalents en base R sont customers[customers$id %in% orders$id, ] et customers[!customers$id %in% orders$id, ].
Empiler des lignes — rbind() et bind_rows()
La jointure met en correspondance sur une clé. L’assemblage ignore les clés et empile par position. Pour placer les lignes d’une table sous une autre — mêmes colonnes, plus de lignes — utilisez rbind() (base R) ou bind_rows() (dplyr) :
Les deux empilent les quatre lignes en une seule table. La différence apparaît quand les colonnes ne s’alignent pas : rbind() génère une erreur, tandis que bind_rows() comble les manques avec NA et poursuit —
library(dplyr)q1 <-data.frame(id =c("A", "B"), amount =c(50, 30))q3 <-data.frame(id =c("C"), amount =c(20), note ="late") # extra columnbind_rows(q1, q3) # 'note' is NA for the q1 rows
id amount note
1 A 50 <NA>
2 B 30 <NA>
3 C 20 late
Cette tolérance rend bind_rows() pratique pour empiler de nombreux fichiers dont les colonnes se correspondent presque — mais pas exactement.
Accoler des colonnes — cbind() et bind_cols()
Pour placer des colonnes côte à côte — mêmes lignes, plus de colonnes — utilisez cbind() (base R) ou bind_cols() (dplyr) :
library(dplyr)people <-data.frame(id =c("A", "B", "C"), name =c("Alice", "Bob", "Carol"))extra <-data.frame(age =c(30, 25, 40))# base Rcbind(people, extra)
id name age
1 A Alice 30
2 B Bob 25
3 C Carol 40
# dplyr — same resultbind_cols(people, extra)
id name age
1 A Alice 30
2 B Bob 25
3 C Carol 40
Les deux accolent age à people. Mais remarquez ce qui rend cela fragile : l’alignement des colonnes se fait par position — ligne 1 avec ligne 1, ligne 2 avec ligne 2 — en supposant que les deux tables sont déjà dans le même ordre. Si extra était trié différemment, vous attacheriez en silence le mauvais âge à chaque personne, sans aucune erreur. Dès que les deux tables partagent une clé, préférez un left_join() sur cette clé : il fait correspondre par identité, non par position, et ne peut donc pas se désaligner.
Laquelle, et quand
Un repère rapide pour choisir le bon outil :
Vous voulez…
Utilisez
Ajouter les colonnes d’une autre table, sur une clé partagée
left_join() / merge() (une jointure mutante)
Ne conserver que les lignes présentes dans les deux tables
inner_join()
Conserver chaque ligne de votre table principale, NA là où rien ne correspond
left_join()
Filtrer les lignes qui ont (ou non) une correspondance — sans nouvelles colonnes
semi_join() / anti_join()
Empiler plus de lignes (mêmes colonnes)
rbind() / bind_rows()
Ajouter plus de colonnes (même ordre de lignes)
cbind() / bind_cols()
La règle générale : faites correspondre sur une clé avec une jointure chaque fois que vous le pouvez ; ne recourez à rbind()/cbind() que lorsque les tables s’alignent réellement par position.
Essayez en direct
Les blocs ci-dessus ont été exécutés au moment de la génération. Modifiez celui-ci et appuyez sur Run pour faire des jointures et empiler des tables par vous-même — il s’exécute dans votre navigateur via webR (sans installation).
🟢 Avec un agent IA
Collez un head() de deux de vos propres tables et demandez à Prova« fais la jointure de celles-ci sur customer_id, en conservant chaque ligne de la première, puis montre-moi quelles lignes n’ont pas correspondu » ; elle écrit les appels left_join() + anti_join(). Comme le runtime est juste là, vous les exécutez pour confirmer que les lignes correspondantes et le nombre de lignes sont ceux que vous attendez sur vos données, et non une supposition d’apparence plausible. Demander à Prova →
Vous travaillez en Python ? Un guide pandas merge() / concat() arrive dans la série Python.
Problèmes fréquents
Une jointure a renvoyé bien plus de lignes que l’une ou l’autre table — une explosion plusieurs-à-plusieurs. Lorsque la clé est dupliquée des deux côtés, chaque paire correspondante est renvoyée, si bien que deux-fois-deux devient quatre lignes :
library(dplyr)x <-data.frame(id =c("A", "A"), val =c(1, 2))y <-data.frame(id =c("A", "A"), tag =c("p", "q"))inner_join(x, y, by ="id") # 4 rows — every combination, plus a dplyr warning
Warning in inner_join(x, y, by = "id"): Detected an unexpected many-to-many relationship between `x` and `y`.
ℹ Row 1 of `x` matches multiple rows in `y`.
ℹ Row 1 of `y` matches multiple rows in `x`.
ℹ If a many-to-many relationship is expected, set `relationship =
"many-to-many"` to silence this warning.
id val tag
1 A 1 p
2 A 1 q
3 A 2 p
4 A 2 q
C’est une véritable relation plusieurs-à-plusieurs, et dplyr vous en avertit parce que c’est généralement une erreur — une clé que vous pensiez unique ne l’est pas. Dédupliquez la clé d’au moins un côté (par ex. distinct(y, id, .keep_all = TRUE)), ou ajoutez une seconde colonne au by pour que la correspondance soit unique.
Un left_join() a perdu des correspondances dont vous étiez sûr — les clés ne concordent pas vraiment. Les jointures font correspondre des valeurs exactes et de même type, si bien que "B" ne correspond jamais à "b", " B", ni le nombre2 à la chaîne"2". Les lignes survivent (c’est une jointure gauche) mais leurs nouvelles colonnes reviennent à NA. Vérifiez avec str() que les deux colonnes clés sont du même type, et normalisez d’abord — trimws(), toupper() ou as.character() — avant de faire la jointure.
rbind() échoue avec « names do not match previous names ».rbind() exige que les deux tables aient les mêmes colonnes dans le même ordre. Réordonnez ou ajoutez la colonne manquante, ou passez à dplyr::bind_rows(), qui fait correspondre les colonnes par nom et comble tout manque avec NA.
Questions fréquentes
NoteComment fusionner deux data frames en R ?
Mettez-les en correspondance sur une colonne clé partagée. En base R : merge(x, y, by = "id") (une jointure interne par défaut). En dplyr : inner_join(x, y, by = "id"), ou left_join() pour conserver chaque ligne de x. Utilisez by = c("col1", "col2") quand la clé s’étend sur plusieurs colonnes.
NoteQuelle est la différence entre inner_join et left_join ?
inner_join() ne conserve que les lignes dont la clé apparaît dans les deux tables — les non-correspondances de chaque côté sont supprimées. left_join() conserve chaque ligne de la première table (celle de gauche) et remplit les nouvelles colonnes avec NA là où la seconde table n’a pas de correspondance. Utilisez left_join() lorsque vous ne devez pas perdre de lignes de votre table principale.
NoteComment empiler deux data frames en R (rbind) ?
Utilisez rbind(df1, df2) pour placer les lignes de df2 sous df1 — les deux doivent avoir les mêmes colonnes dans le même ordre. Le bind_rows(df1, df2) de dplyr fait la même chose mais fait correspondre les colonnes par nom et comble les colonnes manquantes avec NA, tolérant ainsi les tables qui ne s’alignent pas exactement.
NoteQuelle est la différence entre merge et join en R ?
Ils font le même travail — combiner deux tables sur une clé. merge() est en base R ; les verbes *_join() de dplyr sont les équivalents tidyverse aux noms plus clairs (left_join() = merge(all.x = TRUE), full_join() = merge(all = TRUE)). Deux différences pratiques : merge() trie le résultat selon la clé par défaut (sort = FALSE pour garder l’ordre), tandis que les jointures conservent l’ordre de lignes de la table de gauche ; et dplyr ajoute les jointures filtrantes semi_join()/anti_join(), auxquelles la base merge() n’a pas d’équivalent direct.
NoteComment trouver les lignes d’un data frame absentes d’un autre ?
Utilisez le anti_join(x, y, by = "id") de dplyr — il renvoie les lignes de x dont la clé n’a aucune correspondance dans y. En base R, l’équivalent est x[!x$id %in% y$id, ]. C’est la façon standard de répondre à des questions comme « quels clients n’ont aucune commande ? ».
Testez vos connaissances
ImportantExercice : joindre les employés aux salaires, puis trouver les manques
Vous avez une table employees et une table salaries qui partagent un id. (1) Utilisez une jointure qui conserve chaque employé et attache son salaire (NA là où il n’y en a pas), puis (2) listez les employés qui n’ont aucune ligne de salaire.
# (1) needs the join that keeps all of the FIRST table -> left_join().# (2) needs the filtering join that keeps rows with NO match -> anti_join().
library(dplyr)
employees <- data.frame(id = c(1, 2, 3), name = c("Ann", "Ben", "Cara"))
salaries <- data.frame(id = c(1, 2), salary = c(50000, 60000))
# 1) keep every employee, add salary
joined <- left_join(employees, salaries, by = "id")
joined
# 2) which employees have NO salary row?
anti_join(employees, salaries, by = "id")
library(dplyr)employees <-data.frame(id =c(1, 2, 3), name =c("Ann", "Ben", "Cara"))salaries <-data.frame(id =c(1, 2), salary =c(50000, 60000))# 1) keep every employee, add salaryjoined <-left_join(employees, salaries, by ="id")joined# 2) which employees have NO salary row?anti_join(employees, salaries, by ="id")
NoteVérification rapide : laquelle conserve chaque ligne de x ?
Vous voulez conserver chaque ligne de la table x, ajouter les colonnes de y là où la clé correspond, et NA là où elle ne correspond pas. Quel appel le fait ?
A. inner_join(x, y, by = "id") B. left_join(x, y, by = "id") C. semi_join(x, y, by = "id")
Afficher la réponse
B.left_join() conserve tout x et remplit les colonnes sans correspondance avec NA. A (inner_join) supprime les lignes de x qui n’ont pas de correspondance ; C (semi_join) ne conserve que les lignes correspondantes de xet n’ajoute aucune colonne de y.
Conclusion
Combiner des data frames se résume à deux idées. La jointure quand les tables partagent une clé : left_join() / merge() pour ajouter des colonnes (jointures mutantes), semi_join() / anti_join() pour filtrer les lignes selon une correspondance. L’assemblage seulement quand les tables s’alignent par position : rbind() / bind_rows() pour empiler des lignes, cbind() / bind_cols() pour ajouter des colonnes. Optez par défaut pour une jointure fondée sur une clé — elle fait correspondre par identité et ne peut donc pas se désaligner en silence comme le fait un assemblage positionnel. Les approches dplyr et base R donnent le même résultat ; choisissez celle qui convient à votre code.
Prouvez que vous savez le faire. Maîtrisez toute la série Manipulation de données en R et Python — suivez votre parcours, construisez des projets et obtenez un certificat.
Cette leçon est reproductible : chaque résultat de cette page a été produit par le code affiché — modifiez n’importe quel bloc et appuyez sur Run pour le reproduire dans votre navigateur. The runtime is the judge.
@online{2026,
author = {},
title = {Joindre et combiner des data frames en R : merge, jointures
dplyr, rbind \& cbind},
date = {2026-07-17},
url = {https://www.datanovia.com/learn/programming/data-wrangling/combine-data-frames-in-r},
langid = {fr}
}