Cet article vise à donner aux lecteurs une introduction sur la fusion à saveur SQL avec les pandas, comment l'utiliser et quand ne pas l'utiliser.
En particulier, voici ce que ce message va subir:
Ce que ce message ne passera pas:
- Discussions et délais liés aux performances (pour l'instant). Mentions principalement notables de meilleures alternatives, le cas échéant.
- Gestion des suffixes, suppression de colonnes supplémentaires, renommage des sorties et autres cas d'utilisation spécifiques. Il y a d'autres (lire: mieux) messages qui traitent de cela, alors comprenez-le!
Remarque
La plupart des exemples utilisent par défaut les opérations INNER JOIN lors de la démonstration de diverses fonctionnalités, sauf indication contraire.
De plus, tous les DataFrames ici peuvent être copiés et répliqués afin que vous puissiez jouer avec eux. Consultez également cet article
sur la lecture des DataFrames à partir de votre presse-papiers.
Enfin, toutes les représentations visuelles des opérations JOIN ont été dessinées à la main à l'aide de Google Drawings. Inspiration d' ici .
Assez parlé, montre-moi juste comment utiliser merge
!
Installer
np.random.seed(0)
left = pd.DataFrame({'key': ['A', 'B', 'C', 'D'], 'value': np.random.randn(4)})
right = pd.DataFrame({'key': ['B', 'D', 'E', 'F'], 'value': np.random.randn(4)})
left
key value
0 A 1.764052
1 B 0.400157
2 C 0.978738
3 D 2.240893
right
key value
0 B 1.867558
1 D -0.977278
2 E 0.950088
3 F -0.151357
Par souci de simplicité, la colonne clé porte le même nom (pour l'instant).
Un INNER JOIN est représenté par
Remarque
Ceci, ainsi que les chiffres à venir, suivent tous cette convention:
- le bleu indique les lignes présentes dans le résultat de la fusion
- le rouge indique les lignes exclues du résultat (c'est-à-dire supprimées)
- le vert indique les valeurs manquantes qui sont remplacées par des NaN dans le résultat
Pour effectuer une INNER JOIN, appelez merge
le DataFrame gauche, en spécifiant le DataFrame droit et la clé de jointure (au moins) comme arguments.
left.merge(right, on='key')
# Or, if you want to be explicit
# left.merge(right, on='key', how='inner')
key value_x value_y
0 B 0.400157 1.867558
1 D 2.240893 -0.977278
Cela renvoie uniquement les lignes de left
et right
qui partagent une clé commune (dans cet exemple, "B" et "D).
Un LEFT OUTER JOIN , ou LEFT JOIN est représenté par
Cela peut être effectué en spécifiant how='left'
.
left.merge(right, on='key', how='left')
key value_x value_y
0 A 1.764052 NaN
1 B 0.400157 1.867558
2 C 0.978738 NaN
3 D 2.240893 -0.977278
Notez soigneusement l'emplacement des NaN ici. Si vous spécifiez how='left'
, seules les clés de left
sont utilisées et les données manquantes deright
sont remplacées par NaN.
Et de même, pour un RIGHT OUTER JOIN , ou RIGHT JOIN qui est ...
... précisez how='right'
:
left.merge(right, on='key', how='right')
key value_x value_y
0 B 0.400157 1.867558
1 D 2.240893 -0.977278
2 E NaN 0.950088
3 F NaN -0.151357
Ici, les clés de right
sont utilisées et les données manquantes de left
sont remplacées par NaN.
Enfin, pour la FULL OUTER JOIN , donnée par
précisez how='outer'
.
left.merge(right, on='key', how='outer')
key value_x value_y
0 A 1.764052 NaN
1 B 0.400157 1.867558
2 C 0.978738 NaN
3 D 2.240893 -0.977278
4 E NaN 0.950088
5 F NaN -0.151357
Cela utilise les clés des deux trames et des NaN sont insérés pour les lignes manquantes dans les deux.
La documentation résume bien ces différentes fusions:
Autres JOIN - LEFT-Excluding, RIGHT-Excluding et FULL-Excluding / ANTI JOINs
Si vous avez besoin de JOIN à GAUCHE et de JOIN à DROITE en deux étapes.
Pour JOIN à GAUCHE, représenté par
Commencez par effectuer une LEFT OUTER JOIN, puis filtrez (sauf!) Les lignes provenant left
uniquement,
(left.merge(right, on='key', how='left', indicator=True)
.query('_merge == "left_only"')
.drop('_merge', 1))
key value_x value_y
0 A 1.764052 NaN
2 C 0.978738 NaN
Où,
left.merge(right, on='key', how='left', indicator=True)
key value_x value_y _merge
0 A 1.764052 NaN left_only
1 B 0.400157 1.867558 both
2 C 0.978738 NaN left_only
3 D 2.240893 -0.977278 both
Et de même, pour un JOIN excluant à DROITE,
(left.merge(right, on='key', how='right', indicator=True)
.query('_merge == "right_only"')
.drop('_merge', 1))
key value_x value_y
2 E NaN 0.950088
3 F NaN -0.151357
Enfin, si vous devez effectuer une fusion qui ne conserve que les clés de gauche ou de droite, mais pas les deux (IOW, exécution d'un ANTI-JOIN ),
Vous pouvez le faire de la même manière ...
(left.merge(right, on='key', how='outer', indicator=True)
.query('_merge != "both"')
.drop('_merge', 1))
key value_x value_y
0 A 1.764052 NaN
2 C 0.978738 NaN
4 E NaN 0.950088
5 F NaN -0.151357
Noms différents pour les colonnes clés
Si les colonnes clés sont nommées différemment - par exemple, left
a keyLeft
et right
a keyRight
au lieu de key
-, vous devrez spécifier left_on
et right_on
comme arguments au lieu de on
:
left2 = left.rename({'key':'keyLeft'}, axis=1)
right2 = right.rename({'key':'keyRight'}, axis=1)
left2
keyLeft value
0 A 1.764052
1 B 0.400157
2 C 0.978738
3 D 2.240893
right2
keyRight value
0 B 1.867558
1 D -0.977278
2 E 0.950088
3 F -0.151357
left2.merge(right2, left_on='keyLeft', right_on='keyRight', how='inner')
keyLeft value_x keyRight value_y
0 B 0.400157 B 1.867558
1 D 2.240893 D -0.977278
Éviter la colonne de clé en double dans la sortie
Lors de la fusion à keyLeft
partir de left
et à keyRight
partir de right
, si vous ne souhaitez que l'un keyLeft
ou l'autre keyRight
(mais pas les deux) dans la sortie, vous pouvez commencer par définir l'index comme étape préliminaire.
left3 = left2.set_index('keyLeft')
left3.merge(right2, left_index=True, right_on='keyRight')
value_x keyRight value_y
0 0.400157 B 1.867558
1 2.240893 D -0.977278
Comparez cela avec la sortie de la commande juste avant (c'est-à-dire la sortie de left2.merge(right2, left_on='keyLeft', right_on='keyRight', how='inner')
), vous remarquerez qu'il keyLeft
manque. Vous pouvez déterminer quelle colonne conserver en fonction de l'index de l'image qui est défini comme clé. Cela peut être important lorsque, par exemple, vous effectuez une opération OUTER JOIN.
Fusion d'une seule colonne de l'un des DataFrames
Par exemple, considérez
right3 = right.assign(newcol=np.arange(len(right)))
right3
key value newcol
0 B 1.867558 0
1 D -0.977278 1
2 E 0.950088 2
3 F -0.151357 3
Si vous devez fusionner uniquement "new_val" (sans aucune des autres colonnes), vous pouvez généralement simplement sous-définir les colonnes avant de fusionner:
left.merge(right3[['key', 'newcol']], on='key')
key value newcol
0 B 0.400157 0
1 D 2.240893 1
Si vous effectuez une jointure extérieure gauche, une solution plus performante impliquerait map
:
# left['newcol'] = left['key'].map(right3.set_index('key')['newcol']))
left.assign(newcol=left['key'].map(right3.set_index('key')['newcol']))
key value newcol
0 A 1.764052 NaN
1 B 0.400157 0.0
2 C 0.978738 NaN
3 D 2.240893 1.0
Comme mentionné, c'est similaire, mais plus rapide que
left.merge(right3[['key', 'newcol']], on='key', how='left')
key value newcol
0 A 1.764052 NaN
1 B 0.400157 0.0
2 C 0.978738 NaN
3 D 2.240893 1.0
Fusion sur plusieurs colonnes
Pour rejoindre plusieurs colonnes, spécifiez une liste pour on
(ou left_on
et right_on
, selon le cas).
left.merge(right, on=['key1', 'key2'] ...)
Ou, dans le cas où les noms sont différents,
left.merge(right, left_on=['lkey1', 'lkey2'], right_on=['rkey1', 'rkey2'])
Autres merge*
opérations et fonctions utiles
Cette section ne couvre que les bases et est conçue pour vous mettre en appétit. Pour plus d' exemples et de cas, consultez la documentation sur merge
, join
etconcat
ainsi que les liens vers les spécifications de la fonction.
Index-based * -JOIN (+ index-column merge
s)
Installer
np.random.seed([3, 14])
left = pd.DataFrame({'value': np.random.randn(4)}, index=['A', 'B', 'C', 'D'])
right = pd.DataFrame({'value': np.random.randn(4)}, index=['B', 'D', 'E', 'F'])
left.index.name = right.index.name = 'idxkey'
left
value
idxkey
A -0.602923
B -0.402655
C 0.302329
D -0.524349
right
value
idxkey
B 0.543843
D 0.013135
E -0.326498
F 1.385076
En règle générale, une fusion sur un index ressemblerait à ceci:
left.merge(right, left_index=True, right_index=True)
value_x value_y
idxkey
B -0.402655 0.543843
D -0.524349 0.013135
Prise en charge des noms d'index
Si votre index est nommé, les utilisateurs de la v0.23 peuvent également spécifier le nom du niveau on
(ou left_on
et right_on
si nécessaire).
left.merge(right, on='idxkey')
value_x value_y
idxkey
B -0.402655 0.543843
D -0.524349 0.013135
Fusion sur l'index d'un, colonne (s) d'un autre
Il est possible (et assez simple) d'utiliser l'index de l'un et la colonne d'un autre pour effectuer une fusion. Par exemple,
left.merge(right, left_on='key1', right_index=True)
Ou vice versa ( right_on=...
et left_index=True
).
right2 = right.reset_index().rename({'idxkey' : 'colkey'}, axis=1)
right2
colkey value
0 B 0.543843
1 D 0.013135
2 E -0.326498
3 F 1.385076
left.merge(right2, left_index=True, right_on='colkey')
value_x colkey value_y
0 -0.402655 B 0.543843
1 -0.524349 D 0.013135
Dans ce cas particulier, l'index de left
est nommé, vous pouvez donc également utiliser le nom d'index avec left_on
, comme ceci:
left.merge(right2, left_on='idxkey', right_on='colkey')
value_x colkey value_y
0 -0.402655 B 0.543843
1 -0.524349 D 0.013135
DataFrame.join
En plus de cela, il existe une autre option succincte. Vous pouvez utiliser les DataFrame.join
valeurs par défaut des jointures sur l'index. DataFrame.join
fait un LEFT OUTER JOIN par défaut, c'est donc how='inner'
nécessaire ici.
left.join(right, how='inner', lsuffix='_x', rsuffix='_y')
value_x value_y
idxkey
B -0.402655 0.543843
D -0.524349 0.013135
Notez que je devais spécifier les arguments lsuffix
et rsuffix
car join
sinon cela entraînerait une erreur:
left.join(right)
ValueError: columns overlap but no suffix specified: Index(['value'], dtype='object')
Puisque les noms de colonne sont les mêmes. Ce ne serait pas un problème s'ils étaient nommés différemment.
left.rename(columns={'value':'leftvalue'}).join(right, how='inner')
leftvalue value
idxkey
B -0.402655 0.543843
D -0.524349 0.013135
pd.concat
Enfin, comme alternative aux jointures basées sur un index, vous pouvez utiliser pd.concat
:
pd.concat([left, right], axis=1, sort=False, join='inner')
value value
idxkey
B -0.402655 0.543843
D -0.524349 0.013135
Omettez join='inner'
si vous avez besoin d'une FULL OUTER JOIN (par défaut):
pd.concat([left, right], axis=1, sort=False)
value value
A -0.602923 NaN
B -0.402655 0.543843
C 0.302329 NaN
D -0.524349 0.013135
E NaN -0.326498
F NaN 1.385076
Pour plus d'informations, consultez cet article canonique sur pd.concat
@piRSquared .
Généralisation: merge
ing plusieurs DataFrames
Souvent, la situation se produit lorsque plusieurs DataFrames doivent être fusionnés ensemble. Naïvement, cela peut être fait en enchaînant les merge
appels:
df1.merge(df2, ...).merge(df3, ...)
Cependant, cela devient rapidement incontrôlable pour de nombreux DataFrames. De plus, il peut être nécessaire de généraliser pour un nombre inconnu de DataFrames.
Ici, je présente pd.concat
les jointures multidirectionnelles sur des clés uniques et DataFrame.join
les jointures multidirectionnelles sur des clés non uniques . Tout d'abord, la configuration.
# Setup.
np.random.seed(0)
A = pd.DataFrame({'key': ['A', 'B', 'C', 'D'], 'valueA': np.random.randn(4)})
B = pd.DataFrame({'key': ['B', 'D', 'E', 'F'], 'valueB': np.random.randn(4)})
C = pd.DataFrame({'key': ['D', 'E', 'J', 'C'], 'valueC': np.ones(4)})
dfs = [A, B, C]
# Note, the "key" column values are unique, so the index is unique.
A2 = A.set_index('key')
B2 = B.set_index('key')
C2 = C.set_index('key')
dfs2 = [A2, B2, C2]
Fusion multi-voies sur des clés uniques (ou index)
Si vos clés (ici, la clé peut être une colonne ou un index) sont uniques, vous pouvez les utiliser pd.concat
. Notez que pd.concat
joint DataFrames sur l'index .
# merge on `key` column, you'll need to set the index before concatenating
pd.concat([
df.set_index('key') for df in dfs], axis=1, join='inner'
).reset_index()
key valueA valueB valueC
0 D 2.240893 -0.977278 1.0
# merge on `key` index
pd.concat(dfs2, axis=1, sort=False, join='inner')
valueA valueB valueC
key
D 2.240893 -0.977278 1.0
Omettre join='inner'
pour une jointure externe complète. Notez que vous ne pouvez pas spécifier de jointures LEFT ou RIGHT OUTER (si vous en avez besoin, utilisez join
, décrit ci-dessous).
Fusion multi-voies sur des clés avec doublons
concat
est rapide, mais a ses défauts. Il ne peut pas gérer les doublons.
A3 = pd.DataFrame({'key': ['A', 'B', 'C', 'D', 'D'], 'valueA': np.random.randn(5)})
pd.concat([df.set_index('key') for df in [A3, B, C]], axis=1, join='inner')
ValueError: Shape of passed values is (3, 4), indices imply (3, 2)
Dans cette situation, nous pouvons l'utiliser join
car il peut gérer des clés non uniques (notez que join
joint les DataFrames sur leur index; il appelle merge
sous le capot et fait un LEFT OUTER JOIN sauf indication contraire).
# join on `key` column, set as the index first
# For inner join. For left join, omit the "how" argument.
A.set_index('key').join(
[df.set_index('key') for df in (B, C)], how='inner').reset_index()
key valueA valueB valueC
0 D 2.240893 -0.977278 1.0
# join on `key` index
A3.set_index('key').join([B2, C2], how='inner')
valueA valueB valueC
key
D 1.454274 -0.977278 1.0
D 0.761038 -0.977278 1.0
concat
etmerge
avec un paramètre de direction étanthorizontal
ouvertical
.axis=1
queaxis=0
c'est?merge
etconcat
et axe et peu importe. Cependant, comme @eliu le montre, c'est tout simplement le même concept de fusion avec "gauche" et "droite" et "horizontal" ou "vertical". Personnellement, je dois examiner la documentation chaque fois que je dois me souvenir de quel "axe" est0
et lequel est1
.