Pandas fusionnant 101

366
  • Comment effectuer un ( LEFT| RIGHT| FULL) (INNER |OUTER ) jointure avec des pandas?
  • Comment ajouter des NaN pour les lignes manquantes après la fusion?
  • Comment puis-je me débarrasser des NaN après la fusion?
  • Puis-je fusionner sur l'index?
  • Rejoignez-vous avec des pandas?
  • Comment fusionner plusieurs DataFrames?
  • merge? join? concat? update? Qui? Quelle? Pourquoi?!

... et plus. J'ai vu ces questions récurrentes poser des questions sur diverses facettes de la fonctionnalité de fusion des pandas. La plupart des informations concernant la fusion et ses différents cas d'utilisation sont aujourd'hui fragmentées en des dizaines de messages mal libellés et non consultables. Le but ici est de rassembler certains des points les plus importants pour la postérité.

Ce QnA est censé être le prochain épisode d'une série de guides d'utilisation utiles sur les idiomes courants des pandas (voir ce post sur le pivotement et ce post sur la concaténation , que j'aborderai plus tard).

Veuillez noter que ce message n'est pas destiné à remplacer la documentation , alors lisez-le également! Certains exemples en sont extraits.

cs95
la source

Réponses:

522

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:

  • Les bases - types de jointures (GAUCHE, DROITE, EXTÉRIEURE, INTÉRIEURE)

    • fusion avec différents noms de colonne
    • éviter la colonne de clé de fusion en double dans la sortie
  • Fusion avec l'index dans différentes conditions
    • en utilisant efficacement votre index nommé
    • fusionner la clé comme index de l'un et colonne d'un autre
  • Fusion multi-voies sur les colonnes et les index (uniques et non uniques)
  • Alternatives notables mergeetjoin

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 mergele 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 leftet rightqui 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 leftsont 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 rightsont utilisées et les données manquantes de leftsont 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:

entrez la description de l'image ici

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 leftuniquement,

(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, lefta keyLeftet righta keyRightau lieu de key-, vous devrez spécifier left_onet right_oncomme 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 à keyLeftpartir de leftet à keyRightpartir de right, si vous ne souhaitez que l'un keyLeftou 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 keyLeftmanque. 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_onet 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, joinetconcat ainsi que les liens vers les spécifications de la fonction.


Index-based * -JOIN (+ index-column merges)

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_onet right_onsi 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 leftest 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.joinvaleurs par défaut des jointures sur l'index. DataFrame.joinfait 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 lsuffixet rsuffixcar joinsinon 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 mergeappels:

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.concatles jointures multidirectionnelles sur des clés uniques et DataFrame.joinles 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.concatjoint 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

concatest 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 joincar il peut gérer des clés non uniques (notez que joinjoint les DataFrames sur leur index; il appelle mergesous 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
cs95
la source
49

Une vue visuelle supplémentaire de pd.concat([df0, df1], kwargs). Notez que la signification de kwarg axis=0ou axis=1n'est pas aussi intuitive que df.mean()oudf.apply(func)


sur pd.concat ([df0, df1])

eliu
la source
9
Ceci est un joli diagramme. Puis-je vous demander comment vous l'avez produit?
cs95
6
google doc intégré "insert ==> dessin ... ==> nouveau" (à partir de 2019-mai). Mais, pour être clair: la seule raison pour laquelle j'ai utilisé google doc pour cette image est parce que mes notes sont stockées dans google doc, et je voudrais une image qui peut être modifiée rapidement dans google doc lui-même. En fait, maintenant que vous l'avez mentionné, l'outil de dessin de Google Doc est assez soigné.
eliu
Wow, c'est super. Venant du monde SQL, la jointure "verticale" n'est pas une jointure dans ma tête, car la structure de la table est toujours fixe. Maintenant même pense que les pandas devraient se consolider concatet mergeavec un paramètre de direction étant horizontalou vertical.
Ufos
2
@Ufos N'est-ce pas exactement ce axis=1que axis=0c'est?
cs95
2
oui, il y a maintenant mergeet concatet 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" est 0et lequel est 1.
Ufos