2015-10-07 37 views
7

Sto cercando un modo elegante per aggiungere tutte le righe da un DataFrame a un altro DataFrame (entrambi i DataFrame con lo stesso indice e la stessa struttura di colonne), ma nei casi in cui lo stesso valore dell'indice appare in entrambi i DataFrame, utilizzare la riga dal secondo frame di dati.pandas DataFrame concat/update ("upsert")?

Così, per esempio, se comincio con:

df1: 
        A  B 
    date 
    '2015-10-01' 'A1' 'B1' 
    '2015-10-02' 'A2' 'B2' 
    '2015-10-03' 'A3' 'B3' 

df2: 
    date   A  B 
    '2015-10-02' 'a1' 'b1' 
    '2015-10-03' 'a2' 'b2' 
    '2015-10-04' 'a3' 'b3' 

vorrei che il risultato sia:

    A  B 
    date 
    '2015-10-01' 'A1' 'B1' 
    '2015-10-02' 'a1' 'b1' 
    '2015-10-03' 'a2' 'b2' 
    '2015-10-04' 'a3' 'b3' 

Questo è analogo a quello che penso si chiama "upsert" in qualche Sistemi SQL --- una combinazione di aggiornamento e inserimento, nel senso che ogni riga da df2 è (a) utilizzata per aggiornare una riga esistente in df1 se la chiave di riga esiste già in df1 o (b) inserita in df1 a Alla fine se la chiave di riga non esiste già.

sono venuto con la seguente

pd.concat([df1, df2])  # concat the two DataFrames 
    .reset_index()  # turn 'date' into a regular column 
    .groupby('date')  # group rows by values in the 'date' column 
    .tail(1)    # take the last row in each group 
    .set_index('date') # restore 'date' as the index 

che sembra funzionare, ma questo si basa sul l'ordine delle righe in ogni gruppo groupby essere sempre lo stesso delle DataFrames originali, che non ho controllato, e sembra spiacevolmente contorto.

Qualcuno ha qualche idea per una soluzione più semplice?

risposta

10

Una soluzione consiste nel conatenare df1 con nuove righe in df2 (vale a dire dove l'indice non corrisponde). Quindi aggiornare i valori con quelli da df2.

df = pd.concat([df1, df2[~df2.index.isin(df1.index)]]) 
df.update(df2) 

>>> df 
      A B 
2015-10-01 A1 B1 
2015-10-02 a1 b1 
2015-10-03 a2 b2 
2015-10-04 a3 b3 

EDIT: Per il suggerimento di @chrisb, questo può essere ulteriormente semplificato come segue:

pd.concat([df1[~df1.index.isin(df2.index)], df2]) 

Grazie Chris!

+0

Nice. Mi sto anche interrogando sull'efficienza. Questo approccio sembra decisamente più efficiente della mia soluzione groupby, ma sembra che implicherebbe ancora il passaggio dei dati sia in df1 che in df2 più volte (in termini di ciò che i panda devono fare internamente, intendo). Se qualcuno ha pensieri su un approccio ancora più efficiente, mi piacerebbe sentirli! – embeepea

+2

È possibile evitare l'aggiornamento scrivendo nell'ordine inverso; 'pd.concat ([df1 [~ df1.index.isin (df2.index)], df2])' – chrisb

+2

@embeepea bene YMMV. ma in realtà è abbastanza efficiente, implica un set op (sull'indice) e 1 take (l'indicizzazione) e una singola copia (il concat). Per esempio. Righe 1MM, richiede 150 ms sulla mia macchina. – Jeff