top of page

merge, join, concat : comment fusionner des DataFrames pandas

Oct 26
6 min read

Vos ventes sont dans un fichier, vos clients dans un autre, et vous voulez une seule table pour analyser le chiffre d'affaires par ville. Avec pandas, trois outils permettent de combiner des DataFrames : pd.concat(), pd.merge() et DataFrame.join(). Ils ne font pas la même chose, et choisir le mauvais est la première cause de lignes dupliquées ou de valeurs manquantes.


La règle en une phrase : concat empile des tables qui ont la même structure, merge relie deux tables par une colonne commune, join fait la même chose que merge en s'appuyant sur l'index.


merge, join ou concat : le tableau récapitulatif


Outil

Ce qu'il fait

À utiliser quand

Équivalent Excel / Power Query

pd.concat()

Empile des lignes (ou colle des colonnes)

Vos tables ont les mêmes colonnes : un fichier par mois, par agence…

Ajouter des requêtes

pd.merge()

Jointure sur une ou plusieurs colonnes

Vous rapprochez deux tables via une clé : id client, code produit…

RECHERCHEV / Fusionner des requêtes

DataFrame.join()

Jointure sur l'index

La table de référence a déjà la clé en index

Fusionner des requêtes


Les données d'exemple


Tout au long de l'article, on travaille avec deux tables : les ventes de janvier et de février, et la liste des clients.


import pandas as pd

janvier = pd.DataFrame({"id_vente": [101, 102], "id_client": [1, 2], "montant": [250, 120]})
fevrier = pd.DataFrame({"id_vente": [103, 104, 105], "id_client": [2, 3, 5], "montant": [80, 310, 90]})

clients = pd.DataFrame({
    "id_client": [1, 2, 3, 4],
    "nom": ["Alice", "Bruno", "Chloé", "David"],
    "ville": ["Paris", "Lyon", "Nantes", "Lille"],
})

Deux pièges sont volontairement glissés dans ces données : le client 5 a acheté mais n'existe pas dans la table clients, et le client 4 (David) existe mais n'a rien acheté. Ce sont exactement les cas qui posent problème dans la vraie vie.


pd.concat() : empiler des tables


concat() met des DataFrames bout à bout. C'est l'outil à utiliser quand vous recevez les mêmes données découpées en plusieurs fichiers : un export par mois, par magasin ou par région.


ventes = pd.concat([janvier, fevrier], ignore_index=True)
print(ventes)

   id_vente  id_client  montant
0       101          1      250
1       102          2      120
2       103          2       80
3       104          3      310
4       105          5       90

ignore_index=True renumérote les lignes de 0 à n. Sans lui, vous gardez les index d'origine (0, 1, puis 0, 1, 2) : des doublons d'index qui posent problème dès que vous sélectionnez des lignes avec loc et iloc.


Garder la trace de l'origine avec keys


print(pd.concat([janvier, fevrier], keys=["janvier", "février"]))

           id_vente  id_client  montant
janvier 0       101          1      250
        1       102          2      120
février 0       103          2       80
        1       104          3      310
        2       105          5       90

Coller des colonnes avec axis=1


Avec axis=1, concat() place les tables côte à côte en alignant les lignes sur l'index, et non sur une colonne. Pour rapprocher deux tables par un identifiant, c'est merge() qu'il faut utiliser.


Bon à savoir : DataFrame.append() a été supprimé dans pandas 2.0. concat() est désormais la façon standard d'ajouter des lignes à un DataFrame. Voir aussi la fiche concat() du lexique Python.


pd.merge() : faire une jointure sur une colonne


merge() rapproche deux tables à partir d'une clé commune, comme une RECHERCHEV dans Excel ou une fusion de requêtes dans Power Query.


print(pd.merge(ventes, clients, on="id_client"))

   id_vente  id_client  montant    nom   ville
0       101          1      250  Alice   Paris
1       102          2      120  Bruno    Lyon
2       103          2       80  Bruno    Lyon
3       104          3      310  Chloé  Nantes

Par défaut, merge() fait une jointure interne (how="inner") : seules les lignes dont la clé existe des deux côtés sont conservées. La vente 105, rattachée au client 5 inconnu, a disparu, tout comme David, qui n'a rien acheté.


Choisir le type de jointure avec how


how

Lignes conservées

Dans notre exemple

"inner" (défaut)

Les clés présentes dans les deux tables

4 lignes : la vente 105 et David disparaissent

"left"

Toutes les lignes de la table de gauche

5 lignes : la vente 105 a un nom et une ville vides (NaN)

"right"

Toutes les lignes de la table de droite

5 lignes : David apparaît sans vente

"outer"

Toutes les lignes des deux tables

6 lignes : la vente 105 et David sont conservés


En pratique, how="left" est le plus utilisé : on part de la table des faits (les ventes) et on lui ajoute des informations, sans jamais perdre de lignes.


print(pd.merge(ventes, clients, on="id_client", how="left"))

   id_vente  id_client  montant    nom   ville
0       101          1      250  Alice   Paris
1       102          2      120  Bruno    Lyon
2       103          2       80  Bruno    Lyon
3       104          3      310  Chloé  Nantes
4       105          5       90    NaN     NaN

Les valeurs manquantes qui apparaissent se traitent ensuite avec dropna() ou fillna().


Quand les clés n'ont pas le même nom


clients2 = clients.rename(columns={"id_client": "code_client"})
pd.merge(ventes, clients2, left_on="id_client", right_on="code_client", how="left")

Les deux colonnes de clé sont conservées dans le résultat. Supprimez la seconde ensuite avec drop(columns="code_client").


Contrôler la jointure avec indicator=True


print(pd.merge(ventes, clients, on="id_client", how="outer", indicator=True))

   id_vente  id_client  montant    nom   ville      _merge
0     101.0          1    250.0  Alice   Paris        both
1     102.0          2    120.0  Bruno    Lyon        both
2     103.0          2     80.0  Bruno    Lyon        both
3     104.0          3    310.0  Chloé  Nantes        both
4       NaN          4      NaN  David   Lille  right_only
5     105.0          5     90.0    NaN     NaN   left_only

La colonne _merge indique d'où vient chaque ligne. Filtrer sur left_only est le moyen le plus rapide de retrouver les ventes rattachées à un client inconnu. Remarquez aussi que id_vente et montant sont passés en décimaux : une colonne d'entiers qui reçoit des NaN est convertie en float. Plus de détails dans la fiche merge() du lexique Python.


DataFrame.join() : la jointure sur l'index


join() est un raccourci de merge() qui utilise l'index de la table de droite comme clé. Contrairement à merge(), il fait par défaut une jointure à gauche.


clients_idx = clients.set_index("id_client")
print(ventes.join(clients_idx, on="id_client"))

   id_vente  id_client  montant    nom   ville
0       101          1      250  Alice   Paris
1       102          2      120  Bruno    Lyon
2       103          2       80  Bruno    Lyon
3       104          3      310  Chloé  Nantes
4       105          5       90    NaN     NaN

Si vos deux tables ont déjà la clé en index, df1.join(df2) suffit. Dans les autres cas, merge() est plus lisible et plus explicite.


Les 4 erreurs les plus fréquentes


1. Des lignes en trop après le merge


Si la clé apparaît plusieurs fois des deux côtés, merge() produit toutes les combinaisons possibles et le nombre de lignes explose. Ajoutez validate pour que pandas vérifie la relation attendue :


pd.merge(ventes, clients, on="id_client", how="left", validate="many_to_one")

Si un client apparaît deux fois dans la table clients, pandas lève une MergeError au lieu de dupliquer silencieusement vos ventes.


2. Des clés de types différents


Une clé lue comme texte d'un côté ("1") et comme nombre de l'autre (1) bloque la jointure : pandas lève une ValueError (« You are trying to merge on int64 and object columns »). Convertissez d'abord la colonne, comme expliqué dans notre article pour changer le type d'une colonne pandas.


clients["id_client"] = clients["id_client"].astype(int)

3. Des colonnes suffixées _x et _y


Quand les deux tables ont une colonne de même nom en dehors de la clé, pandas les renomme montant_x et montant_y. Donnez-leur des noms explicites avec suffixes :


objectifs = pd.DataFrame({"id_client": [1, 2, 3], "montant": [300, 100, 300]})
print(pd.merge(ventes, objectifs, on="id_client", suffixes=("_réel", "_objectif")))

   id_vente  id_client  montant_réel  montant_objectif
0       101          1           250               300
1       102          2           120               100
2       103          2            80               100
3       104          3           310               300

4. Des NaN inattendus après un left


Une jointure à gauche laisse des NaN sur les lignes sans correspondance. Avant de les remplir, utilisez indicator=True pour comprendre pourquoi la clé ne trouve pas son équivalent : faute de frappe, espace en trop, majuscules, zéro initial perdu dans un code postal…


Fusionner plus de deux DataFrames


merge() ne prend que deux tables à la fois. Pour en combiner plusieurs, enchaînez les appels ou utilisez functools.reduce :


from functools import reduce

segments = pd.DataFrame({"id_client": [1, 2, 3, 4], "segment": ["Pro", "Particulier", "Pro", "Pro"]})
resultat = reduce(lambda g, d: pd.merge(g, d, on="id_client", how="left"), [ventes, clients, segments])

Quel outil choisir ?


  • Mêmes colonnes, plusieurs fichiers → pd.concat()

  • Deux tables reliées par un identifiant → pd.merge(..., how="left")

  • Clé déjà en index → DataFrame.join()

  • Besoin de vérifier les lignes sans correspondance → indicator=True

  • Peur des doublons → validate="many_to_one"


Fusionner des tables, traiter les valeurs manquantes, regrouper avec groupby : c'est le cœur de la préparation de données en Python. C'est aussi ce que nous pratiquons sur des cas concrets dans notre formation Python – Les bases + approfondissement data.



Questions fréquentes


merge() joint deux tables sur une ou plusieurs colonnes, avec une jointure interne par défaut. join() joint sur l'index de la table de droite, avec une jointure à gauche par défaut. Avec les mêmes clés et le même type de jointure, les deux donnent le même résultat.

Avec pd.merge(table, reference, on="cle", how="left"). Contrairement à RECHERCHEV, merge() ramène plusieurs colonnes d'un coup et accepte des clés composées de plusieurs colonnes, par exemple on=["region", "produit"].

Avec pd.concat([df, nouvelles_lignes], ignore_index=True). La méthode append() a été supprimée dans pandas 2.0.

Parce que la clé n'est pas unique dans au moins une des deux tables. Vérifiez-le avec df["cle"].duplicated().sum(), et ajoutez validate="many_to_one" pour que pandas vous alerte.


 
 
bottom of page