LEFT
| RIGHT
| FULL
) (INNER
| OUTER
) avec pandas?merge
? join
? concat
? update
? Qui? Quelle? Pourquoi?!... et plus. J'ai vu ces questions récurrentes poser des questions sur les différentes facettes de la fonctionnalité de fusion de pandas. La plupart des informations relatives à la fusion et à ses divers cas d'utilisation sont aujourd'hui fragmentées en dizaines d'articles mal rédigés et incompréhensibles. Le but ici est de rassembler certains des points les plus importants pour la postérité.
Ce QnA est censé être le prochain volet d’une série de guides utiles sur les idiomes pandas courants (voir ce post sur le pivot) , et ce post sur la concaténation =, que je vais aborder plus tard).
S'il vous plaît noter que ce post est pas est destiné à remplacer le documentation , donc s'il vous plaît lisez-le aussi! Certains exemples sont tirés de là.
Cet article a pour objectif de donner aux lecteurs un aperçu de la fusion SQL avec des pandas, de la façon de l’utiliser et de ne pas l'utiliser.
En particulier, voici ce que ce post va traverser:
Les bases - types de jointures (LEFT, RIGHT, OUTER, INNER)
merge
et join
Ce que cet article ne passera pas:
Note
Sauf indication contraire, la plupart des exemples utilisent par défaut les opérations INNER JOIN lors de la démonstration de diverses fonctionnalités.En outre, tous les DataFrames ici peuvent être copiés et répliqués pour que vous puissiez les utiliser. Voir aussi cet article pour savoir comment lire les DataFrames dans 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 de ici .
merge
!Configuration
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é a le même nom (pour le moment).
Un INNER JOIN est représenté par
Note
Ceci, ainsi que les chiffres à venir suivent tous cette convention:
- blue indique les lignes présentes dans le résultat de la fusion
- red indique les lignes exclues du résultat (c'est-à-dire supprimées)
- green indique les valeurs manquantes remplacées par des NaN dans le résultat
Pour effectuer une INNER JOIN, appelez pd.merge
en spécifiant le DataFrame de gauche, le DataFrame de droite et la clé de jointure.
pd.merge(left, right, on='key')
key value_x value_y
0 B 0.400157 1.867558
1 D 2.240893 -0.977278
Cela retourne uniquement les lignes de left
et right
qui partagent une clé commune (dans cet exemple, "B" et "D).
Dans les versions plus récentes de pandas (version 0.21 ou plus), merge
est maintenant une fonction de premier ordre. Vous pouvez donc appeler DataFrame.merge
.
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
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 le placement des NaN ici. Si vous spécifiez how='left'
, seules les clés de left
sont utilisées et les données manquantes de right
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 le FULL OUTER JOIN, donné par
spécifiez 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 cadres et des NaN sont insérés pour les lignes manquantes dans les deux.
La documentation résume bien ces différentes fusions:
Si vous avez besoin de JOINDRES excluant GAUCHE et JOINDRE excluant RIGHT en deux étapes.
Pour LEFT-Excluding JOIN, représenté par
Commencez par exécuter un LEFT OUTER JOIN puis en filtrant (excluant!) Les lignes provenant de 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 droit-exclure JOIN,
(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 faire une fusion qui ne conserve que les clés de gauche ou de droite, mais pas les deux (IOW, effectuant un ANTI-JOIN),
Vous pouvez le faire de la même façon ...
(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
Si les colonnes de clé 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
Lors de la fusion sur keyLeft
à partir de left
et keyRight
à partir de right
, si vous ne voulez que l'un des keyLeft
ou 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
En comparant cela avec la sortie de la commande juste avant (la première est la sortie de left2.merge(right2, left_on='keyLeft', right_on='keyRight', how='inner')
), vous remarquerez que keyLeft
est manquant. Vous pouvez déterminer la colonne à conserver en fonction de l'index de la trame défini comme clé. Cela peut être important lorsque, par exemple, vous effectuez une opération OUTER JOIN.
DataFrames
Par exemple, considérons
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 des 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 faites une jointure gauche-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é, ceci 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
Pour joindre 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'])
merge*
utilesOutre merge
, DataFrame.update
et DataFrame.combine_first
sont également utilisés dans certains cas pour mettre à jour un DataFrame avec un autre.
pd.merge_ordered
est une fonction utile pour les JOINs commandés.
pd.merge_asof
(lire: merge_asOf) est utile pour approximatif se joint.
Cette section ne couvre que les bases, et est conçue pour aiguiser votre appétit. Pour plus d'exemples et de cas, voir documentation sur merge
, join
et concat
ainsi que les liens vers les spécifications de la fonction.
merge
s)Configuration
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
Typiquement, une fusion sur 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
Si votre index est nommé, les utilisateurs v0.23 peuvent également spécifier le nom de 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
Il est possible (et assez simple) d'utiliser l'index de l'un et la colonne de l'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 pour 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
Outre ces options, il existe une autre option succincte. Vous pouvez utiliser DataFrame.join
qui, par défaut, se joint à l'index. DataFrame.join
effectue une jointure LEFT OUTER JOIN par défaut, donc how='inner'
est 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 j’avais besoin de spécifier les arguments lsuffix
et rsuffix
puisque join
aurait sinon une erreur de sortie:
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 avaient un nom différent.
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, vous pouvez utiliser pd.concat
comme alternative aux jointures basées sur un index:
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'un FULL OUTER JOIN (valeur 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, voir cette publication canonique sur pd.concat
par @piRSquared .
merge
ing plusieurs DataFramesSouvent, la situation se produit lorsque plusieurs DataFrames doivent être fusionnés. Naïvement, cela peut être fait en enchaînant 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
pour les jointures à plusieurs voies sur unique clés, et DataFrame.join
pour les jointures à plusieurs voies sur non-unique . 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]
Si vos clés (ici, la clé peut être une colonne ou un index) sont uniques, vous pouvez utiliser pd.concat
. Notez que pd.concat
rejoint les 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 complète 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).
concat
est rapide, mais présente des inconvénients. 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 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 effectue 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