web-dev-qa-db-fra.com

Pandas Fusion de 101

  • Comment effectuer une (LEFT | RIGHT | FULL) (INNER | OUTER) avec pandas?
  • Comment puis-je ajouter des NaN pour les lignes manquantes après la fusion?
  • Comment puis-je me débarrasser de NaN après la fusion?
  • Puis-je fusionner sur l'index?
  • Cross rejoindre 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 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à.

225
cs95

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)

    • fusion avec des noms de colonne différents
    • éviter les colonnes de fusion en double dans la sortie
  • Fusion avec index dans différentes conditions
    • en utilisant efficacement votre index nommé
    • touche de fusion en tant qu'index d'un et colonne d'un autre
  • Multiway fusionne sur des colonnes et des index (uniques et non uniques)
  • Alternatives notables à merge et join

Ce que cet article ne passera pas:

  • Discussions et horaires liés aux performances (pour l'instant). Principalement des mentions notables de meilleures alternatives, le cas échéant.
  • Gestion des suffixes, suppression des colonnes supplémentaires, changement de nom des sorties et autres cas d'utilisation spécifiques. Il y a d'autres (lisez: mieux) posts qui traitent de cela, alors réfléchissez-y!

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 .

Assez parlé, montre-moi juste comment utiliser 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:

enter image description here

Autres JOINs - JOIN LEFT-Exclure, RIGHT-Exclure et FULL-Exclure/ANTI JOIN

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

Noms différents pour les colonnes clés

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

Eviter la duplication de la colonne clé dans la sortie

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.

Fusionner une seule colonne de l'un des 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

Fusion de plusieurs colonnes

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'])

Autres opérations et fonctions merge* utiles

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.


* -JOIN basé sur l'index (+ index-column merges)

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

Prise en charge des noms d'index

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

Fusion de l'index d'un, de la (des) colonne (s) d'un autre

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 .


Généraliser: mergeing plusieurs DataFrames

Souvent, 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]

Fusion multiple 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.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).

Fusion multiple sur des clés avec des doublons

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
299
cs95

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

on pd.concat([df0, df1])

17
eliu