2016-05-19 27 views
5

Come rimuovere un dataframe panda da un altro dataframe, proprio come la sottrazione set:Come rimuovere un dataframe panda da un altro dataframe

a=[1,2,3,4,5] 
b=[1,5] 
a-b=[2,3,4] 

E ora abbiamo due panda dataframe, come rimuovere DF2 da 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 

Poi ci aspettiamo risultato df1-DF2 sarà:

In [14]: df 
Out[14]: 
    a b 
0 3 4 

come farlo?

Grazie.

+0

Eventuali duplicati di [differenza fissato per i panda] (http://stackoverflow.com/questions/18180763/set-difference-for-pandas) – AKS

+0

@ 176coding prega timeit le nostre risposte alle vostre reali set di dati - è interessante per me che è il più veloce/ – knagaev

risposta

9

Soluzione

Usa pd.concat seguito da drop_duplicates(keep=False)

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

Sembra

a b 
1 3 4 

Spiegazione

pd.concat aggiunge i due DataFrame s insieme aggiungendo uno subito dopo l'altro. in caso di sovrapposizione, verrà catturato dal metodo drop_duplicates. Tuttavia, per impostazione predefinita, drop_duplicates lascia la prima osservazione e rimuove ogni altra osservazione. In questo caso, vogliamo rimuovere ogni duplicato. Quindi, il parametro keep=False che fa esattamente questo.

Una nota speciale al ripetuto df2. Con una sola df2 qualsiasi riga in df2 non in df1 non sarà considerata un duplicato e rimarrà. Questa soluzione con un solo df2 funziona solo quando df2 è un sottoinsieme di df1. Tuttavia, se concateniamo df2 due volte, è garantito che sia un duplicato e verrà successivamente rimosso.

+0

thx, funziona, e possiamo usare 'pd.concat (df1, df2) .drop_duplicates (keep = False)' o 'df1.append (df2) .drop_duplicates (keep = False)' – 176coding

+0

@ 176codifica sperando questo risponde alla tua domanda. In caso contrario, fammi sapere cosa rimane senza risposta e farò del mio meglio per affrontarlo. – piRSquared

+1

@piRSquared La tua risposta non è corretta - hai fatto la differenza simmetrica, non la differenza (semplice). – knagaev

0

Un approccio logico impostato. Trasforma le righe di df1 e df2 in serie. Quindi utilizzare set sottrazione per definire nuovo approccio 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 
0

Un mascheramento

df1[df1.apply(lambda x: x.values.tolist() not in df2.values.tolist(), axis=1)] 

    a b 
1 3 4 
+0

non hanno bisogno di '.tolist()'. – Stefan

0

mio colpo con merge df1 e DF2 dalla questione.

Usando il parametro 'indicatore'

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 
+0

Una spiegazione di ciò che sta accadendo renderebbe questa una risposta più ricca. Hai detto che la chiave del successo di questo metodo è il parametro "indicatore", e impostando il valore su vero aggiungerai informazioni sulla posizione a ciascuna riga, che la soluzione utilizza nella fase finale per filtrare, mantenendo solo le righe che appaiono solo nei dati di sinistra frame (indicatore == 'left_only'). – Dannid

1

È possibile utilizzare .duplicated, che ha il vantaggio di essere abbastanza espressiva:

%%timeit 
combined = df1.append(df2) 
combined[~combined.duplicated(keep=False)] 

1000 loops, best of 3: 875 µs per loop 

Per fare un confronto:

%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 

In sintesi, utilizzando il confronto np.array è più veloce. Non è necessario il .tolist() lì.

Problemi correlati