Comment supprimer un cadre de données pandas d'un autre cadre de données, tout comme la soustraction d'ensemble:
a=[1,2,3,4,5]
b=[1,5]
a-b=[2,3,4]
Et maintenant, nous avons deux pandas dataframe, comment supprimer df2 de df1:
In [5]: df1=pd.DataFrame([[1,2],[3,4],[5,6]],columns=['a','b'])
In [6]: df1
Out[6]:
a b
0 1 2
1 3 4
2 5 6
In [9]: df2=pd.DataFrame([[1,2],[5,6]],columns=['a','b'])
In [10]: df2
Out[10]:
a b
0 1 2
1 5 6
Alors nous nous attendons à ce que le résultat de df1-df2 soit:
In [14]: df
Out[14]:
a b
0 3 4
Comment faire?
Je vous remercie.
Utilisez pd.concat
suivi de drop_duplicates(keep=False)
pd.concat([df1, df2, df2]).drop_duplicates(keep=False)
Ça ressemble à
a b
1 3 4
pd.concat
ajoute les deux DataFrame
s en ajoutant les uns après les autres. s'il y a un chevauchement, il sera capturé par la méthode drop_duplicates
. Cependant, drop_duplicates
laisse par défaut la première observation et supprime toutes les autres observations. Dans ce cas, nous voulons que chaque copie soit supprimée. Par conséquent, le paramètre keep=False
qui fait exactement cela.
Une note spéciale pour le df2
répété. Avec un seul df2
, toute ligne dans df2
ne figurant pas dans df1
ne sera pas considérée comme un doublon et restera. Cette solution avec un seul df2
ne fonctionne que lorsque df2
est un sous-ensemble de df1
. Cependant, si nous concatrons df2
deux fois, il est garanti qu'il s'agit d'un doublon et sera ensuite supprimé.
Vous pouvez utiliser .duplicated
, qui présente l'avantage d'être assez expressif:
%%timeit
combined = df1.append(df2)
combined[~combined.index.duplicated(keep=False)]
1000 loops, best of 3: 875 µs per loop
En comparaison:
%timeit df1.loc[pd.merge(df1, df2, on=['a','b'], how='left', indicator=True)['_merge'] == 'left_only']
100 loops, best of 3: 4.57 ms per loop
%timeit pd.concat([df1, df2, df2]).drop_duplicates(keep=False)
1000 loops, best of 3: 987 µs per loop
%timeit df2[df2.apply(lambda x: x.value not in df2.values, axis=1)]
1000 loops, best of 3: 546 µs per loop
En résumé, l’utilisation de la comparaison np.array
est la plus rapide. Vous n'avez pas besoin du .tolist()
ici.
Une approche logique définie. Tournez les lignes de df1
et df2
en ensembles. Utilisez ensuite la soustraction set
pour définir une nouvelle DataFrame
idx1 = set(df1.set_index(['a', 'b']).index)
idx2 = set(df2.set_index(['a', 'b']).index)
pd.DataFrame(list(idx1 - idx2), columns=df1.columns)
a b
0 3 4
Mon coup avec merge df1 et df2 de la question.
Utilisation du paramètre 'indicateur'
In [74]: df1.loc[pd.merge(df1, df2, on=['a','b'], how='left', indicator=True)['_merge'] == 'left_only']
Out[74]:
a b
1 3 4
Une approche de masquage
df1[df1.apply(lambda x: x.values.tolist() not in df2.values.tolist(), axis=1)]
a b
1 3 4
Je pense que le premier tolist()
doit être supprimé, mais conservez le second:
df1[df1.apply(lambda x: x.values() not in df2.values.tolist(), axis=1)]