232 votes

Python Pandas - Trouver la différence entre deux cadres de données

J'ai deux cadres de données df1 et df2, où df2 est un sous-ensemble de df1. Comment puis-je obtenir un nouveau cadre de données (df3) qui est la différence entre les deux cadres de données ?

En d'autres termes, un cadre de données qui contient toutes les lignes/colonnes de df1 qui ne sont pas dans df2 ?

enter image description here

5 votes

La manière la plus simple de le faire dépendra de la façon dont vos cadres de données sont structurés (c'est-à-dire si les index peuvent être utilisés, etc.). C'est un bon exemple de la raison pour laquelle vous devriez toujours inclure une balise exemple reproductible dans les questions des pandas.

1 votes

J'ai ajouté l'image d'exemple de dataframe

0 votes

297voto

W-B Points 94428

En utilisant drop_duplicates

pd.concat([df1,df2]).drop_duplicates(keep=False)

Update :

Above method only working for those dataframes they do not have duplicate itself, For example

df1=pd.DataFrame({'A':[1,2,3,3],'B':[2,3,4,4]})
df2=pd.DataFrame({'A':[1],'B':[2]})

Le résultat sera le suivant, ce qui est faux

Sortie erronée :

pd.concat([df1, df2]).drop_duplicates(keep=False)
Out[655]: 
   A  B
1  2  3

Sortie correcte

Out[656]: 
   A  B
1  2  3
2  3  4
3  3  4

Comment y parvenir ?

Méthode 1 : Utiliser isin avec tuple

df1[~df1.apply(tuple,1).isin(df2.apply(tuple,1))]
Out[657]: 
   A  B
1  2  3
2  3  4
3  3  4

Méthode 2 : merge avec indicator

df1.merge(df2,indicator = True, how='left').loc[lambda x : x['_merge']!='both']
Out[421]: 
   A  B     _merge
1  2  3  left_only
2  3  4  left_only
3  3  4  left_only

13 votes

Vous pouvez également déterminer quelles colonnes doivent être prises en compte lors de la recherche de doublons : pd.concat([df1,df2]).drop_duplicates(subset = ['col1','col2'], keep=False)

2 votes

@Szpaqn remarquez que cette méthode ne traitera pas le cas spécial :-)

0 votes

Notez que cela peut provoquer le maintien de lignes inattendues dans le résultat si l'un de vos types de données est float (parce que 12.00000000001 != 12 ). Une meilleure pratique consiste à trouver l'intersection des ID dans deux cadres de données et à obtenir la différence sur cette base.

71voto

jpp Points 83462

Pour les rangs, essayez ceci, où Name est la colonne d'index commune (peut être une liste pour plusieurs colonnes communes, ou spécifier left_on y right_on ) :

m = df1.merge(df2, on='Name', how='outer', suffixes=['', '_'], indicator=True)

El indicator=True est utile car il ajoute une colonne appelée _merge avec tous les changements entre df1 y df2 Il y a trois catégories possibles : "gauche_seulement", "droite_seulement" ou "les deux".

Pour les colonnes, essayez ceci :

set(df1.columns).symmetric_difference(df2.columns)

13 votes

Un commentaire de la part du Downvoter ? merge avec indicator=True est la solution classique pour comparer des cadres de données par des champs donnés.

22voto

toecsnar42 Points 46

Réponse acceptée La méthode 1 ne fonctionnera pas pour les cadres de données contenant des NaNs, car pd.np.nan != pd.np.nan . Je ne suis pas sûr que ce soit le meilleur moyen, mais on peut l'éviter en

df1[~df1.astype(str).apply(tuple, 1).isin(df2.astype(str).apply(tuple, 1))]

Il est plus lent, car il doit convertir les données en chaîne de caractères, mais grâce à cette conversion, il est possible d'obtenir des résultats plus rapides. pd.np.nan == pd.np.nan .

Passons en revue le code. D'abord, nous convertissons les valeurs en chaînes de caractères, et nous appliquons tuple à chaque ligne.

df1.astype(str).apply(tuple, 1)
df2.astype(str).apply(tuple, 1)

Grâce à cela, nous obtenons pd.Series objet avec une liste de tuples. Chaque tuple contient une ligne entière de df1 / df2 . Ensuite, nous appliquons isin méthode sur df1 pour vérifier si chaque tuple "est dans" df2 . Le résultat est pd.Series avec des valeurs booléennes. Vrai si le tuple de df1 est en df2 . Au final, nous annulons les résultats avec ~ et en appliquant un filtre sur df1 . Pour faire court, nous ne récupérons que les rangées provenant de df1 qui ne sont pas dans df2 .

Pour le rendre plus lisible, nous pouvons l'écrire comme suit :

df1_str_tuples = df1.astype(str).apply(tuple, 1)
df2_str_tuples = df2.astype(str).apply(tuple, 1)
df1_values_in_df2_filter = df1_str_tuples.isin(df2_str_tuples)
df1_values_not_in_df2 = df1[~df1_values_in_df2_filter]

0 votes

C'est une excellente réponse, mais elle est incompréhensible en une seule phrase. Si l'on sépare chaque étape et que l'on comprend ce qu'elle fait, la manière dont elle accomplit son travail devient très claire.

0 votes

Explication supplémentaire. J'espère que cela aidera !

14voto

Speed Coder Points 76
import pandas as pd
# given
df1 = pd.DataFrame({'Name':['John','Mike','Smith','Wale','Marry','Tom','Menda','Bolt','Yuswa',],
    'Age':[23,45,12,34,27,44,28,39,40]})
df2 = pd.DataFrame({'Name':['John','Smith','Wale','Tom','Menda','Yuswa',],
    'Age':[23,12,34,44,28,40]})

# find elements in df1 that are not in df2
df_1notin2 = df1[~(df1['Name'].isin(df2['Name']) & df1['Age'].isin(df2['Age']))].reset_index(drop=True)

# output:
print('df1\n', df1)
print('df2\n', df2)
print('df_1notin2\n', df_1notin2)

# df1
#     Age   Name
# 0   23   John
# 1   45   Mike
# 2   12  Smith
# 3   34   Wale
# 4   27  Marry
# 5   44    Tom
# 6   28  Menda
# 7   39   Bolt
# 8   40  Yuswa
# df2
#     Age   Name
# 0   23   John
# 1   12  Smith
# 2   34   Wale
# 3   44    Tom
# 4   28  Menda
# 5   40  Yuswa
# df_1notin2
#     Age   Name
# 0   45   Mike
# 1   27  Marry
# 2   39   Bolt

0 votes

Que signifie "~" ?

0 votes

'~' n'est pas destiné à l'indexation booléenne. Voir : pandas.pydata.org/pandas-docs/stable/guide_utilisateur/

8voto

liangli Points 31

Edit2, j'ai trouvé une nouvelle solution sans avoir besoin de définir un index

newdf=pd.concat([df1,df2]).drop_duplicates(keep=False)

Ok j'ai trouvé la réponse du vote le plus élevé qui contient déjà ce que j'ai compris. Oui, nous ne pouvons utiliser ce code qu'à condition qu'il n'y ait pas de doublons dans chaque deux dfs.


J'ai une méthode délicate. Tout d'abord, nous définissons 'Name' comme l'index de deux dataframes donnés par la question. Puisque nous avons le même 'Name' dans deux df, nous pouvons simplement supprimer l'index du 'petit' df du 'grand' df. Voici le code.

df1.set_index('Name',inplace=True)
df2.set_index('Name',inplace=True)
newdf=df1.drop(df2.index)

2 votes

Vous vouliez probablement dire pd.concat([df1,df2]).drop_duplicates(keep=False)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X