web-dev-qa-db-fra.com

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

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

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

enter image description here

43
userPyGeo

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]})
_

Il sortira comme ci-dessous, ce qui est faux

Mauvaise sortie:

_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
_
58
WeNYoBen

Pour les lignes, essayez ceci avec cols sur la liste des colonnes que vous souhaitez comparer:

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

Pour les colonnes, essayez ceci:

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

Réponse acceptée La méthode 1 ne fonctionnera pas pour les trames de données contenant des NaN, comme pd.np.nan != pd.np.nan. Je ne suis pas sûr que ce soit la meilleure solution, mais cela peut être évité en

df1[~df1.astype(str).apply(Tuple, 1).isin(df2.astype(str).apply(Tuple, 1))]
3
toecsnar42

edit2, j'ai imaginé 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é contient déjà ce que j'ai découvert. Oui, nous ne pouvons utiliser ce code que s'il n'y a pas de doublons dans chaque deux dfs.


J'ai une méthode compliquée. Nous commençons par définir 'Nom' comme index de deux données par la question. Puisqu'on a le même 'Nom' dans deux dfs, nous pouvons simplement supprimer l'index 'plus petit' du plus grand 'df' . Voici le code.

df1.set_index('Name',inplace=True)
df2.set_index('Name',inplace=True)
newdf=df1.drop(df2.index)
3
liangli
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
1
SpeedCoder5

Peut-être une ligne simple, avec des noms de colonne identiques ou différents. Fonctionne même lorsque df2 ['Nom2'] contenait des valeurs en double.

newDf = df1.set_index('Name1').drop(df2['Name2'])
1
Cherif Diallo

Une légère variation de la solution Nice @ liangli qui ne nécessite pas de changer l'index des cadres de données existants:

newdf = df1.drop(df1.join(df2.set_index('Name').index))
0
Serge Ballesta

Trouver la différence par index. En supposant que df1 est un sous-ensemble de df2 et que les index sont reportés lors de la sous-définition

df1.loc[set(df1.index).symmetric_difference(set(df2.index))].dropna()

# Example

df1 = pd.DataFrame({"gender":np.random.choice(['m','f'],size=5), "subject":np.random.choice(["bio","phy","chem"],size=5)}, index = [1,2,3,4,5])

df2 =  df1.loc[[1,3,5]]

df1

 gender subject
1      f     bio
2      m    chem
3      f     phy
4      m     bio
5      f     bio

df2

  gender subject
1      f     bio
3      f     phy
5      f     bio

df3 = df1.loc[set(df1.index).symmetric_difference(set(df2.index))].dropna()

df3

  gender subject
2      m    chem
4      m     bio

0
DOS

En plus de la réponse acceptée, je voudrais proposer une solution plus large pouvant trouver une différence d'ensemble 2D de deux trames de données avec n'importe quel index/columns (elles pourraient ne pas coïncider pour les deux noms de données). La méthode permet également de configurer la tolérance pour les éléments float pour la comparaison de trames de données (elle utilise np.isclose)


import numpy as np
import pandas as pd

def get_dataframe_setdiff2d(df_new: pd.DataFrame, 
                            df_old: pd.DataFrame, 
                            rtol=1e-03, atol=1e-05) -> pd.DataFrame:
    """Returns set difference of two pandas DataFrames"""

    union_index = np.union1d(df_new.index, df_old.index)
    union_columns = np.union1d(df_new.columns, df_old.columns)

    new = df_new.reindex(index=union_index, columns=union_columns)
    old = df_old.reindex(index=union_index, columns=union_columns)

    mask_diff = ~np.isclose(new, old, rtol, atol)

    df_bool = pd.DataFrame(mask_diff, union_index, union_columns)

    df_diff = pd.concat([new[df_bool].stack(),
                         old[df_bool].stack()], axis=1)

    df_diff.columns = ["New", "Old"]

    return df_diff

Exemple:

In [1]

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

print("df1:\n", df1, "\n")

print("df2:\n", df2, "\n")

diff = get_dataframe_setdiff2d(df1, df2)

print("diff:\n", diff, "\n")
Out [1]

df1:
   A  C
0  2  2
1  1  1
2  2  2 

df2:
   A  B
0  1  1
1  1  1 

diff:
     New  Old
0 A  2.0  1.0
  B  NaN  1.0
  C  2.0  NaN
1 B  NaN  1.0
  C  1.0  NaN
2 A  2.0  NaN
  C  2.0  NaN 
0
Luchko