2015-09-18 6 views
11

stesso di questo python pandas: how to find rows in one dataframe but not in another? ma con più colonnePandas: trovare le righe che non esistono in un altro dataframe base a più colonne

Questa è la configurazione:

import pandas as pd 

df = pd.DataFrame(dict(
    col1=[0,1,1,2], 
    col2=['a','b','c','b'], 
    extra_col=['this','is','just','something'] 
)) 

other = pd.DataFrame(dict(
    col1=[1,2], 
    col2=['b','c'] 
)) 

Ora, voglio selezionare il righe da df che non esistono in altri. Voglio fare la selezione col1 e col2

In SQL vorrei fare:

select * from df 
where not exists (
    select * from other o 
    where df.col1 = o.col1 and 
    df.col2 = o.col2 
) 

E in Pandas posso fare qualcosa di simile, ma ci si sente molto brutto. Una parte della bruttezza potrebbe essere evitata se df avesse la colonna id ma non è sempre disponibile.

key_col = ['col1','col2'] 
df_with_idx = df.reset_index() 
common = pd.merge(df_with_idx,other,on=key_col)['index'] 
mask = df_with_idx['index'].isin(common) 

desired_result = df_with_idx[~mask].drop('index',axis=1) 

Quindi forse c'è qualche modo più elegante?

risposta

14

Dal 0.17.0 c'è un nuovo indicator param è possibile passare a merge che sarà indica se le righe sono presenti solo a sinistra, a destra o in entrambi:

In [5]: 
merged = df.merge(other, how='left', indicator=True) 
merged 

Out[5]: 
    col1 col2 extra_col  _merge 
0  0 a  this left_only 
1  1 b   is  both 
2  1 c  just left_only 
3  2 b something left_only 

In [6]:  
merged[merged['_merge']=='left_only'] 

Out[6]: 
    col1 col2 extra_col  _merge 
0  0 a  this left_only 
2  1 c  just left_only 
3  2 b something left_only 

Così ora è possibile filtrare il df fusione selezionando solo 'left_only' righe

+1

Grazie per essere tornato a questo. Puoi farlo in una riga con 'df.merge (other, how = 'left', indicator = True) .query ('_ merge ==" left_only "')' ma non so se è meglio. – Pekka

+0

Personalmente trovo che troppa concatenazione per la produzione di una fodera può rendere il codice più difficile da leggere, ci possono essere alcuni miglioramenti di velocità e di memoria anche se – EdChum

+1

@Pekka: + per tornare alla sinistra originale in una riga: 'df .merge (other, how = 'left', indicator = True) .query ('_ merge == "left_only"'). drop (['_ merge'], axis = 1) ' – SpeedCoder5

1

interessanti

cols = ['col1','col2'] 
#get copies where the indeces are the columns of interest 
df2 = df.set_index(cols) 
other2 = other.set_index(cols) 
#Look for index overlap, ~ 
df[~df2.index.isin(other2.index)] 

Returns:

col1 col2 extra_col 
0  0 a  this 
2  1 c  just 
3  2 b something 

sembra un po 'più elegante ...

+1

Se si imposta l'indice a quei colli si possono usare [ 'difference'] (http://pandas.pydata.org/pandas -docs/stable/generated/pandas.Index.difference.html # pandas.Index.difference) per ottenere lo stesso risultato – EdChum