2012-03-24 18 views
15

Ich habe folgenden Datenrahmen:Pandas - jüngsten Wert einer bestimmten Spalte von einer anderen Spalte indiziert bekommen (erhält Maximalwert einer bestimmten Spalte von einer anderen Spalte indiziert)

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 

Ich mag eine Teilmenge davon bekommen, diese Daten, so dass ich nur die neueste (größte 'data_date') 'value' für jede 'obj_id' haben.

Ich habe eine Lösung zusammen gehackt, aber es fühlt sich schmutzig an. Ich habe mich gefragt, ob jemand einen besseren Weg hat. Ich bin mir sicher, dass ich einen einfachen Weg verpassen muss, um es durch Pandas zu tun.

Mein Verfahren ist im Wesentlichen zu einer Gruppe, zu sortieren, Abrufen und rekombinieren wie folgt:

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')) 

Antwort

2

Die aggregate() method auf groupby Objekten verwendet werden, kann einen neuen Datenrahmen von einem groupby Objekt in einem einzigen Schritt zu schaffen. (Ich bin mir nicht bewusst, eine saubere Art und Weise, obwohl die erste/letzte Zeile eines Datenrahmen zu extrahieren.)

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 

können Sie auch die Aggregation auf einzelne Spalten durchführen, wobei in diesem Fall die Aggregatfunktion auf einer Serie Objekt arbeitet .

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

Ich mag Antwort des crewbum, ist dies wahrscheinlich schneller (sorry, das noch nicht getestet habe, aber ich alles vermeiden, Sortieren):

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

es numpys „argmax“ -Funktion verwendet das finden rowindex, in dem das Maximum erscheint.

+0

i die Geschwindigkeit auf einem Datenrahmen mit 24.735 Zeilen getestet, gruppiert in 16 Gruppen (btw: Dataset aus planethunter.org) und bekam 12,5 ms (argmax) vs 17,5 ms (sortiert) als ein Ergebnis von% Zeit. Beide Lösungen sind also ziemlich schnell :-) und mein Datensatz scheint zu klein zu sein ;-) – Maximilian

8

Wenn die Anzahl der "obj_id" s sehr hoch ist, sollten Sie den gesamten Datenrahmen sortieren und dann die Duplikate löschen, um das letzte Element zu erhalten.

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

Dies sollte schneller (sorry ich es nicht getestet), weil Sie eine benutzerdefinierte agg Funktion nicht zu tun haben, die langsam ist, wenn eine große Anzahl von Tasten besteht. Sie könnten denken, dass es schlechter ist, den gesamten Datenrahmen zu sortieren, aber in der Praxis sind Python-Arten schnell und native Schleifen sind langsam.

+0

Das hat einen Reiz, die anderen Antworten alle hatten Probleme für mich, und das war auch viel schneller. –

+0

Das war mehr als eine Größenordnung schneller für mich als die Antwort von pdifranc. Diese Frage existiert in verschiedenen Formen auf SO. Ich werde sie alle auf diese Antwort hinweisen. Nur eine Anmerkung 'FutureWarning: Das Schlüsselwort take_last = True ist veraltet, stattdessen keep = 'last'. –

0

Ich glaube, eine geeignetere Lösung basierend auf den in diesem Thread gefunden haben. Aber meins verwendet die Funktion zum Anwenden eines Datenrahmens anstelle des Aggregats. Es gibt auch einen neuen Datenrahmen mit den gleichen Spalten wie das Original zurück.

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()]) 

Original-

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 

Retourdatenrahmen:

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

Dies ist eine weitere mögliche Lösung. Ich glaube es ist am schnellsten.

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

Dies ist ein netter Ansatz, der in diesem und anderen Kontexten für mich funktioniert hat. – alexbw

+0

Eine nette allgemeine Lösung, aber im Vergleich zu einigen anderen Methoden eher langsam – josh

0

Aktualisierung thetainted1's Antwort, da einige der Funktionen zukünftige Warnungen haben, wie tommy.carstensen wies darauf hin.Hier ist, was für mich gearbeitet:

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

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