2012-03-24 3 views
15

Ho la seguente dataframe:panda - ottenere la maggior parte del valore recente di una determinata colonna indicizzata da un'altra colonna (ottenere il massimo valore di una determinata colonna indicizzata da un'altra colonna)

obj_id data_date value 
0 4  2011-11-01 59500  
1 2  2011-10-01 35200 
2 4  2010-07-31 24860 
3 1  2009-07-28 15860 
4 2  2008-10-15 200200 

voglio ottenere un sottoinsieme di questi dati in modo che ho solo il più recente (più grande 'data_date') 'value' per ogni 'obj_id'.

Ho violato una soluzione, ma è sporco. Mi stavo chiedendo se qualcuno ha un modo migliore. Sono sicuro che mi manchi un modo semplice per farlo attraverso i panda.

mio metodo è essenzialmente raggruppare, ordinare, recuperare, e ricombinare come segue:

row_arr = [] 
for grp, grp_df in df.groupby('obj_id'): 
    row_arr.append(dfg.sort('data_date', ascending = False)[:1].values[0]) 

df_new = DataFrame(row_arr, columns = ('obj_id', 'data_date', 'value')) 

risposta

2

La aggregate() method su oggetti GroupBy può essere utilizzato per creare un nuovo dataframe da un oggetto groupby in un unico passaggio. (Io non sono a conoscenza di un modo più pulito per estrarre la prima/ultima riga di una dataframe però.)

In [12]: df.groupby('obj_id').agg(lambda df: df.sort('data_date')[-1:].values[0]) 
Out[12]: 
     data_date value 
obj_id     
1  2009-07-28 15860 
2  2011-10-01 35200 
4  2011-11-01 59500 

è inoltre possibile eseguire l'aggregazione in singole colonne, nel qual caso la funzione di aggregazione lavora su un oggetto Series .

In [25]: df.groupby('obj_id')['value'].agg({'diff': lambda s: s.max() - s.min()}) 
Out[25]: 
      diff 
obj_id   
1   0 
2  165000 
4  34640 
4

mi piace la risposta di crewbum, probabilmente questo è più veloce (mi dispiace, non ancora testato, ma evito di smistamento tutto):

df.groupby('obj_id').agg(lambda df: df.values[df['data_date'].values.argmax()]) 

si utilizza la funzione numpys "argmax" per trovare la rowindex in cui appare il massimo.

+0

ho provato la velocità su un dataframe con 24735 righe, raggruppati in 16 gruppi (A proposito: DataSet da planethunter.org) ed ha ottenuto 12,5 ms (argmax) vs 17.5 ms (in ordine) come un risultato di% timeit. Quindi entrambe le soluzioni sono abbastanza veloci :-) e il mio set di dati sembra essere troppo piccolo ;-) – Maximilian

8

Se il numero di "obj_id" è molto alto, è necessario ordinare l'intero dataframe e quindi eliminare i duplicati per ottenere l'ultimo elemento.

sorted = df.sort_index(by='data_date') 
result = sorted.drop_duplicates('obj_id', take_last=True).values 

Questo dovrebbe essere più veloce (scusate non ho provarlo) perché non c'è bisogno di fare una funzione agg personalizzato, che è lento quando v'è un gran numero di chiavi. Si potrebbe pensare che sia peggio ordinare l'intero dataframe, ma in pratica in ordinamenti python sono veloci e i loop nativi sono lenti.

+0

Questo ha funzionato come un incantesimo, l'altro ha risposto a tutti ha avuto problemi per me, e anche questo è stato molto più veloce. –

+0

Per me è stato più di un ordine di grandezza più veloce della risposta di pdifranc. Questa domanda esiste in varie forme su SO. Li indicherò a questa risposta. Solo una nota 'FutureWarning: the take_last = La parola chiave True è deprecata, usa keep = 'last' invece'. –

0

Credo di aver trovato una soluzione più appropriata basata su quelli in questa discussione. Tuttavia, la mia utilizza la funzione apply di un dataframe invece dell'aggregato. Restituisce anche un nuovo dataframe con le stesse colonne dell'originale.

df = pd.DataFrame({ 
'CARD_NO': ['000', '001', '002', '002', '001', '111'], 
'DATE': ['2006-12-31 20:11:39','2006-12-27 20:11:53','2006-12-28 20:12:11','2006-12-28 20:12:13','2008-12-27 20:11:53','2006-12-30 20:11:39']}) 

print df 
df.groupby('CARD_NO').apply(lambda df:df['DATE'].values[df['DATE'].values.argmax()]) 

originale

CARD_NO     DATE 
0  000 2006-12-31 20:11:39 
1  001 2006-12-27 20:11:53 
2  002 2006-12-28 20:12:11 
3  002 2006-12-28 20:12:13 
4  001 2008-12-27 20:11:53 
5  111 2006-12-30 20:11:39 

dataframe restituito:

CARD_NO 
000  2006-12-31 20:11:39 
001  2008-12-27 20:11:53 
002  2006-12-28 20:12:13 
111  2006-12-30 20:11:39 
12

Questa è un'altra soluzione possibile. Credo che sia il più veloce.

df.loc[df.groupby('obj_id').data_date.idxmax(),:] 
+2

Questo è un buon approccio che ha funzionato per me in questo e in altri contesti. – alexbw

+0

Una buona soluzione generale ma piuttosto lenta rispetto ad altri metodi – josh

0

Aggiornamento della risposta di thetainted1 poiché alcune funzioni hanno avvertenze future ora come ha sottolineato tommy.carstensen.Ecco cosa ha funzionato per me:

sorted = df.sort_values(by='data_date') 

result = sorted.drop_duplicates('obj_id', keep='last')