Pandas fusionnant 101

Dec 06 2018
  • Comment effectuer un ( INNER| ( LEFT| RIGHT| FULL) OUTER) JOINavec 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?
  • Rejoindre les pandas?
  • Comment fusionner plusieurs DataFrames?
  • merge? join? concat? update? OMS? Quelle? Pourquoi?!

... et plus. J'ai vu ces questions récurrentes sur les différentes facettes de la fonctionnalité de fusion des pandas. La plupart des informations concernant la fusion et ses divers cas d'utilisation sont aujourd'hui fragmentées dans des dizaines de messages mal rédigés et insondables. 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 cet article sur le pivotement et cet article sur la concaténation , que je reviendrai plus tard).

Veuillez noter que cet article n'est pas destiné à remplacer la documentation , veuillez donc le lire également! Certains des exemples sont tirés de là.

Réponses

696 cs95 Dec 06 2018 at 13:41

Cet article vise à donner aux lecteurs un aperçu de la fusion SQL avec les pandas, comment l'utiliser et quand ne pas l'utiliser.

En particulier, voici ce que cet article va parcourir:

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

    • fusion avec différents noms de colonnes
    • é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é
    • la clé de fusion comme l'index de l'un et la colonne de l'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 horaires liés à la performance (pour l'instant). Mentions surtout notables de meilleures alternatives, le cas échéant.
  • Gérer les suffixes, supprimer des colonnes supplémentaires, renommer les sorties et autres cas d'utilisation spécifiques. Il y a d'autres articles (lire: mieux) qui traitent de cela, alors découvrez-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 façon de lire 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é, montrez-moi simplement comment l'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

Notez
ceci, ainsi que les chiffres à venir, tous suivent 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 NaNs 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 ne renvoie que les lignes de leftet rightqui partagent une clé commune (dans cet exemple, "B" et "D).

Une jointure externe gauche ou une jointure gauche est représentée 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 de rightsont remplacées par NaN.

Et de même, pour une jointure externe droite ou une jointure droite qui est ...

... préciser 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éciser 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:

Autres JOINs - GAUCHE-Excluant, DROITE-Excluant et FULL-Excluant / ANTI JOINs

Si vous avez besoin de JOINs d'exclusion GAUCHE et de JOIN d'exclusion DROITE en deux étapes.

Pour LEFT-Excluding JOIN, représenté par

Commencez par effectuer une JOINTURE EXTÉRIEURE GAUCHE puis filtrer (exclure!) 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 DROIT,

(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

Différents noms 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 alors 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 keyLeftde leftet keyRightde right, si vous ne voulez que l'un des deux keyLeftou 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 la colonne à conserver en fonction de l'index de l'image défini comme clé. Cela peut avoir de l'importance, par exemple, lors de l'exécution d'une opération OUTER JOIN.

Fusionner 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 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 faites une jointure externe 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 joindre sur 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 aiguiser l'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.


Basé sur un index * -JOIN (+ colonnes d'index merge)

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 l'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 version 0.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 index d'une, colonne (s) d'une autre

Il est possible (et assez simple) d'utiliser l'index d'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 de l'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 ceux-ci, il existe une autre option succincte. Vous pouvez utiliser les DataFrame.joinvaleurs par défaut pour les jointures sur l'index. DataFrame.joinfait une JOINTURE EXTÉRIEURE GAUCHE 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 j'avais besoin de spécifier les arguments lsuffixet rsuffixcar joinsinon, l'erreur serait:

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 des 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.concatpar @piRSquared .


Généralisation: mergeingérer plusieurs DataFrames

Souvent, la situation se produit lorsque plusieurs DataFrames doivent être fusionnés. Naïvement, cela peut être fait en chaî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.

Je présente ici pd.concatpour les jointures multi-voies sur des clés uniques et DataFrame.joinpour les jointures multi-voies 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 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 EXTÉRIEURES GAUCHE ou DROITE (si vous en avez besoin, utilisez join, décrit ci-dessous).

Fusion multi-voies sur les clés avec des 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
62 eliu Apr 26 2019 at 06:43

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)


5 GonçaloPeres龚燿禄 Aug 10 2020 at 17:13

Dans cette réponse, je considérerai un exemple pratique du pandas.concat.

Considérant ce qui suit DataFramesavec les mêmes noms de colonne:

Preco2018 avec taille (8784, 5)

Preco 2019 avec taille (8760, 5)

Qui ont les mêmes noms de colonnes.

Vous pouvez les combiner en utilisant pandas.concat, simplement

import pandas as pd

frames = [Preco2018, Preco2019]

df_merged = pd.concat(frames)

Ce qui donne un DataFrame avec la taille suivante (17544, 5)

Si vous voulez visualiser, cela finit par fonctionner comme ça

( Source )