Fusionner des dataframes à l’aide de la méthode merge
- 2021-05-11
- Publié par : Christophe DELEUZE
- Catégorie : Pandas
Si vous souhaitez vous initier à la librairie pandas vous êtes au bon endroit !
De plus, si vous galérez pour fusionner des dataframes, vous êtes aussi au bon endroit et cet article est fait pour vous ! Au programme, nous allons explorer toutes les possibilités pour fusionner deux dataframes ensemble. Afin d’éviter l’indigestion, je ferais un parallèle avec les jointures du langage SQL. Cela nous permettra de mieux comprendre comment fusionner des dataframes en s’appuyant sur les schémas du diagramme de Venn !
Transposition des jointures SQL vers les dataframes
Les jointures en SQL permettent d’associer plusieurs tables d’une base de donnée dans une même requête. Cela permet d’exploiter toute la puissance des bases de données relationnelles pour obtenir des résultats qui combinent les données de plusieurs tables de manière efficace. Il existe plusieurs types de jointures dont les différences concernent principalement les éléments conservés ou non après la jointure. Généralement, la synthèse des différentes jointures possibles de faire entre deux tables est toujours étayée d’un diagramme de Venn qui en résume les caractéristiques.
C’est ce diagramme que nous allons reprendre au profit des dataframes pour en illustrer les différentes possibilités de fusions.
Sur les 10 jointures alternatives qui existent, j’en détaillerai essentiellement 7. Par convention, nous utiliserons deux dataframes nommés A (gauche) et B (droite) :
import pandas as pd
A = pd.DataFrame(
{
"col_key": ["K0", "K1", "K2", "K3"],
"col_key_2": ["K0", "K1", "K0", "K1"],
"col_A": ["A0", "A1", "A2", "A3"],
"col_B": ["B0", "B1", "B2", "B3"]
}
)
B = pd.DataFrame(
{
"col_key": ["K0", "K1", "K2", "K3"],
"col_key_2": ["K0", "K1", "K0", "K1"],
"col_C": ["C0", "C1", "C2", "C3"],
"col_D": ["D0", "D1", "D2", "D3"]
}
)
La méthode .merge()
Avant d’aborder en détail chaque type de fusion, attardons-nous quelques instants sur la méthode .merge(). Celle-ci peut-être utilisée de deux manières différentes. Vous pouvez directement appeler la méthode .merge() de la librairie pandas ou employer la méthode .merge() associée à un dataframe. Les deux solutions sont équivalentes. Dans les deux cas, il vous faudra deux dataframes et au moins une colonne dans chaque dataframe qui servira d’élément de comparaison pour lier les données.
Comparaison à l'aide d'une colonne identique dans chaque dataframe
Si au moins une colonne à un nom identique dans les deux dataframes, la méthode merge() l’utilisera pour comparer les dataframes. En SQL, cela correspond à faire une jointure naturelle. Dans notre cas, nous remarquerons que c’est la colonne col_key qui a été utilisée pour faire la comparaison :
>>> pd.merge(A, B)
col_key col_key_2 col_A col_B col_C col_D
0 K0 K0 A0 B0 C0 D0
1 K1 K1 A1 B1 C1 D1
2 K2 K0 A2 B2 C2 D2
3 K3 K1 A3 B3 C3 D3
Maintenant, si nous souhaitons définir nous-mêmes la colonne, nous pouvons aussi le faire. Pour cela, il faudra utiliser le paramètre on en lui précisant le nom de la colonne commune qui permettra de faire le lien entre les dataframes :
# Merge les dataframes A et B sur la colonne 'col_key'
pd.merge(A, B, on="col_key")
# Merge équivalent à l'exemple précédent
A.merge(B, on="col_key")
# Idem que précédemment
B.merge(A, on="col_key")
Comparaison à l'aide de colonnes différentes
Toutefois, si le nom des colonnes est différent, on ne sera plus adapté pour répondre à votre problème. Dans ce cas, on utilisera alors les paramètres left_on et right_on pour préciser le nom de la colonne de A et de B à comparer :
# La colonne "col_key" de A est comparée à la colonne "col_key_2" de B
pandas.merge(A, B, left_on="col_key", right_on="col_key_2")
Comparaison multicolonnes
En complément, il est aussi possible d’avoir comme critère de comparaison plusieurs colonnes à gauche et à droite. Pour définir l’ensemble des colonnes, il suffira d’adapter le paramètre on en lui fournissant la liste des colonnes à comparer entrent-elles :
# La colonne "col_key" de A et B sont comparées l'une par rapport à l'autre, idem pour "col_key_2"
pandas.merge(A, B, on=["col_key", "col_key_2"])
Avant de rentrer dans le détail de chaque type de fusion, intéressons-nous à : comment leur en spécifier le type. Pour cela, rien de plus simple, il suffit d’utiliser le paramètre how qui prend comme valeur le nom de la fusion à effectuer :
pandas.merge(A, B, how="inner", on="col_key")
Notez que le nom des fusions est quasiment le même que leur équivalent SQL.
Maintenant que nous connaissons tous les secrets de merge() et que vous savez fusionner des dataframes, intéressons-nous aux différentes fusions dont vous pourriez avoir besoin.
La jointure Inner
La fusion "inner" est celle appliquée par défaut par la méthode merge(). Sa particularité est qu’elle retourne tous les éléments dont la condition est vraie entre les dataframes.
>>>pd.merge(A, B, how='inner', on='col_key')
col_key col_key_2_x col_A col_B col_key_2_y col_C col_D
0 K0 K0 A0 B0 K0 C0 D0
1 K1 K1 A1 B1 K1 C1 D1
2 K2 K0 A2 B2 K0 C2 D2
3 K3 K1 A3 B3 K1 C3 D3
La jointure Left
Concernant la jointure Left (gauche), elle retourne tous les éléments du dataframe de gauche (A) combinés au dataframe de droite (B) même si la condition n’est pas vérifiée dans le dataframe de droite (B).
>>> pd.merge(A, B, how='left', on='col_key')
col_key col_key_2_x col_A col_B col_key_2_y col_C col_D
0 K0 K0 A0 B0 K0 C0 D0
1 K1 K1 A1 B1 K1 C1 D1
2 K2 K0 A2 B2 K0 C2 D2
3 K3 K1 A3 B3 K1 C3 D3
>>> pd.merge(A, B, how='left', left_on="col_key", right_on="col_key_2")
col_key_x col_key_2_x col_A col_B col_key_y col_key_2_y col_C col_D
0 K0 K0 A0 B0 K0 K0 C0 D0
1 K0 K0 A0 B0 K2 K0 C2 D2
2 K1 K1 A1 B1 K1 K1 C1 D1
3 K1 K1 A1 B1 K3 K1 C3 D3
4 K2 K0 A2 B2 NaN NaN NaN NaN
5 K3 K1 A3 B3 NaN NaN NaN NaN
Vous noterez qu’à chaque fois que la condition n’est pas respectée, la valeur des champs du dataframe de droite (B) est à NaN.
La jointure Left Outer
Parfois, on ne souhaite conserver de Left que les éléments donc la condition n’est pas vérifié. Cela s’appelle une fusion Left Outer (externe gauche).
>>> pd.merge(A, B, how='outer', on='col_key', indicator=True).query('_merge=="left_only"').drop(columns='_merge')
Empty DataFrame
Columns: [col_key, col_key_2_x, col_A, col_B, col_key_2_y, col_C, col_D]
Index: []
>>> pd.merge(A, B, how='right', left_on="col_key_2", right_on="col_key")
col_key_x col_key_2_x col_A col_B col_key_y col_key_2_y col_C col_D
0 K0 K0 A0 B0 K0 K0 C0 D0
1 K2 K0 A2 B2 K0 K0 C0 D0
2 K1 K1 A1 B1 K1 K1 C1 D1
3 K3 K1 A3 B3 K1 K1 C1 D1
4 NaN NaN NaN NaN K2 K0 C2 D2
5 NaN NaN NaN NaN K3 K1 C3 D3
La jointure Right
Quant à la jointure Right (droite), elle retourne tous les enregistrements du dataframe de droite, même si la condition n’est pas vérifiée dans l’autre dataframe.
>>> pd.merge(A, B, how='right', on='col_key')
col_key col_key_2_x col_A col_B col_key_2_y col_C col_D
0 K0 K0 A0 B0 K0 C0 D0
1 K1 K1 A1 B1 K1 C1 D1
2 K2 K0 A2 B2 K0 C2 D2
3 K3 K1 A3 B3 K1 C3 D3
La jointure Right Outer
Encore une fois, à la différence de la jointure précédente, Right Outer (externe droite) retourne uniquement tous les enregistrements du dataframe de droite dont la condition n’est pas vérifiée dans l’autre.
>>> pd.merge(A, B, how='outer', on='col_key', indicator=True).query('_merge=="right_only"').drop(columns='_merge')
Empty DataFrame
Columns: [col_key, col_key_2_x, col_A, col_B, col_key_2_y, col_C, col_D]
Index: []
>>> pd.merge(A, B, how='outer', left_on="col_key_2", right_on="col_key", indicator=True).query('_merge=="right_only"').drop(columns='_merge')
col_key_x col_key_2_x col_A col_B col_key_y col_key_2_y col_C col_D
4 NaN NaN NaN NaN K2 K0 C2 D2
5 NaN NaN NaN NaN K3 K1 C3 D3
La jointure Full
Cette fois, nous allons voir la jointure Full (pleine). Elle retourne tous les éléments des deux dataframes même si la condition n’est pas vérifiée dans l’un des dataframes.
>>> pd.merge(A, B, how='outer', on='col_key')
col_key col_key_2_x col_A col_B col_key_2_y col_C col_D
0 K0 K0 A0 B0 K0 C0 D0
1 K1 K1 A1 B1 K1 C1 D1
2 K2 K0 A2 B2 K0 C2 D2
3 K3 K1 A3 B3 K1 C3 D3
>>> pd.merge(A, B, how='outer', left_on="col_key_2", right_on="col_key")
col_key_x col_key_2_x col_A col_B col_key_y col_key_2_y col_C col_D
0 K0 K0 A0 B0 K0 K0 C0 D0
1 K2 K0 A2 B2 K0 K0 C0 D0
2 K1 K1 A1 B1 K1 K1 C1 D1
3 K3 K1 A3 B3 K1 K1 C1 D1
4 NaN NaN NaN NaN K2 K0 C2 D2
5 NaN NaN NaN NaN K3 K1 C3 D3
>>> pd.merge(A, B, how='outer', left_on="col_key", right_on="col_key_2")
col_key_x col_key_2_x col_A col_B col_key_y col_key_2_y col_C col_D
0 K0 K0 A0 B0 K0 K0 C0 D0
1 K0 K0 A0 B0 K2 K0 C2 D2
2 K1 K1 A1 B1 K1 K1 C1 D1
3 K1 K1 A1 B1 K3 K1 C3 D3
4 K2 K0 A2 B2 NaN NaN NaN NaN
5 K3 K1 A3 B3 NaN NaN NaN NaN
La jointure Full Outer
Enfin, la jointure Full Outer (externe pleine) retourne tous les éléments des deux dataframes dont la condition n’est pas vérifiée dans l’un des dataframes.
>>> pd.merge(A, B, how='outer', on='col_key', indicator=True) .query('_merge!="both"').drop(columns='_merge')
Empty DataFrame
Columns: [col_key, col_key_2_x, col_A, col_B, col_key_2_y, col_C, col_D]
Index: []
>>> pd.merge(A, B, how='outer', left_on="col_key_2", right_on="col_key", indicator=True) .query('_merge!="both"').drop(columns='_merge')
col_key_x col_key_2_x col_A col_B col_key_y col_key_2_y col_C col_D
4 NaN NaN NaN NaN K2 K0 C2 D2
5 NaN NaN NaN NaN K3 K1 C3 D3
>>> pd.merge(A, B, how='outer', left_on="col_key", right_on="col_key_2", indicator=True) .query('_merge!="both"').drop(columns='_merge')
col_key_x col_key_2_x col_A col_B col_key_y col_key_2_y col_C col_D
4 K2 K0 A2 B2 NaN NaN NaN NaN
5 K3 K1 A3 B3 NaN NaN NaN NaN
Le mot de la fin
Avant de conclure, je vous recommande d’être très vigilant à ce qu’il n’y ait pas de doublon dans vos colonnes de comparaisons ! Cela vous évitera bien des surprises.
Sinon, pour vous épargner la relecture de cet article à chaque que vous en aurez besoin, voici un schéma récapitulatif :
Je n’ai pas traité les jointures cross, self et union, mais si on devait les résumer :
- cross : utiliser un produit cartésien ;
- self :
pd.merge(A, A, on='col_key'); - union :
pd.concat([A, B], ignore_index=True).
La librairie Pandas est très puissante et nous permet de traiter un même problème par beaucoup d’approches différentes. Il convient donc, dans un souci d’optimisation, de toujours évaluer lesquelles sont les plus efficaces.
Aussi, mes propositions pour résoudre des problèmes de fusions de données ne sont pas parfaites, mais elles répondront sans aucun doute à tous vos besoins.
Si vous voulez en discuter, n’hésitez pas à commenter.
bonjour,
merci pour cet article plus clair que la doc officielle de PANDAS. Je cherche la solution équivalente au SQL qui permet de choisir les zones des 2 tables souhaitées dans le résultat. En effet, il n’est pas nécessaire dans le dataframe résultat d’avoir les colonnes permettant la jointure.
Cordialement