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