2013-02-18 9 views
44

Angesichts der folgenden DatenrahmenPandas Art von Gruppe zu aggregieren und Spalte

In [31]: rand = np.random.RandomState(1) 
     df = pd.DataFrame({'A': ['foo', 'bar', 'baz'] * 2, 
          'B': rand.randn(6), 
          'C': rand.rand(6) > .5}) 

In [32]: df 
Out[32]:  A   B  C 
     0 foo 1.624345 False 
     1 bar -0.611756 True 
     2 baz -0.528172 False 
     3 foo -1.072969 True 
     4 bar 0.865408 False 
     5 baz -2.301539 True 

Ich mag würde es in Gruppen sortieren (A) durch die aggregierte Summe von B, und dann durch den Wert in C (nicht aggregierten) . Deshalb sollte man im Grunde die Reihenfolge der A Gruppen mit

In [28]: df.groupby('A').sum().sort('B') 
Out[28]:    B C 
     A    
     baz -2.829710 1 
     bar 0.253651 1 
     foo 0.551377 1 

Und dann von True/False, so dass es letztlich wie folgt aussieht:

In [30]: df.ix[[5, 2, 1, 4, 3, 0]] 
Out[30]: A   B  C 
    5 baz -2.301539 True 
    2 baz -0.528172 False 
    1 bar -0.611756 True 
    4 bar 0.865408 False 
    3 foo -1.072969 True 
    0 foo 1.624345 False 

Wie dies geschehen kann?

Antwort

45

GROUPBY A :

Innerhalb jeder Gruppe Summe über B und Broadcast den Wert s mit transformieren. Dann nach B sortieren:

In [1]: grp[['B']].transform(sum).sort('B') 
Out[1]: 
      B 
2 -2.829710 
5 -2.829710 
1 0.253651 
4 0.253651 
0 0.551377 
3 0.551377 

Indizieren Sie das ursprüngliche df, indem Sie den Index von oben übergeben.

In [2]: sort1 = df.ix[grp[['B']].transform(sum).sort('B').index] 

In [3]: sort1 
Out[3]: 
    A   B  C 
2 baz -0.528172 False 
5 baz -2.301539 True 
1 bar -0.611756 True 
4 bar 0.865408 False 
0 foo 1.624345 False 
3 foo -1.072969 True 

Schließlich sortieren ‚C‘ Werte innerhalb der Gruppen von ‚A‘ mit der sort=False Option, die eine Sortierreihenfolge von Schritt zu erhalten: Dies wird den A-Wert durch die Gesamtsumme der B-Werte neu zu bestellen 1:

In [4]: f = lambda x: x.sort('C', ascending=False) 

In [5]: sort2 = sort1.groupby('A', sort=False).apply(f) 

In [6]: sort2 
Out[6]: 
     A   B  C 
A 
baz 5 baz -2.301539 True 
    2 baz -0.528172 False 
bar 1 bar -0.611756 True 
    4 bar 0.865408 False 
foo 3 foo -1.072969 True 
    0 foo 1.624345 False 

reinigen df Index unter Verwendung reset_index mit drop=True up:

In [7]: sort2.reset_index(0, drop=True) 
Out[7]: 
    A   B  C 
5 baz -2.301539 True 
2 baz -0.528172 False 
1 bar -0.611756 True 
4 bar 0.865408 False 
3 foo -1.072969 True 
0 foo 1.624345 False 
+1

Ich nahm auch an, dass 'groupby's' sort = False 'Flag eine beliebige, nicht unbedingt sortierte Reihenfolge zurückgibt (ich nehme an, dass ich sie aus irgendeinem Grund mit Python-Wörterbüchern assoziiere). Aber diese Antwort impliziert, dass das Flag die ursprüngliche Reihenfolge der Datenframezeilen garantiert erhält? – beardc

+1

Ich bin zu 99% sicher, dass die Reihenfolge der Gruppen bei ihrem ersten Erscheinen erhalten bleibt. Ich habe keinen Code, um dies zu untermauern, aber einige schnelle Tests bestätigen diese Intuition. – Zelazny7

+2

Danke @ Zelazny7 für diese Antwort. Es ist genau das, was ich will. Es scheint jedoch im neuesten Pandas-Paket, um 'Out [7]' zu erreichen, 'inplace = True' zu den Argumenten in 'Input [7]' hinzuzufügen. – MoonKnight

8

Eine Möglichkeit, dies zu tun ist, eine Dummy-Spalte mit den Summen einzusetzen, um zu sortieren:

In [10]: sum_B_over_A = df.groupby('A').sum().B 

In [11]: sum_B_over_A 
Out[11]: 
A 
bar 0.253652 
baz -2.829711 
foo 0.551376 
Name: B 

in [12]: df['sum_B_over_A'] = df.A.apply(sum_B_over_A.get_value) 

In [13]: df 
Out[13]: 
    A   B  C sum_B_over_A 
0 foo 1.624345 False  0.551376 
1 bar -0.611756 True  0.253652 
2 baz -0.528172 False  -2.829711 
3 foo -1.072969 True  0.551376 
4 bar 0.865408 False  0.253652 
5 baz -2.301539 True  -2.829711 

In [14]: df.sort(['sum_B_over_A', 'A', 'B']) 
Out[14]: 
    A   B  C sum_B_over_A 
5 baz -2.301539 True  -2.829711 
2 baz -0.528172 False  -2.829711 
1 bar -0.611756 True  0.253652 
4 bar 0.865408 False  0.253652 
3 foo -1.072969 True  0.551376 
0 foo 1.624345 False  0.551376 

und vielleicht würden Sie die Dummy-Zeile löschen:

In [15]: df.sort(['sum_B_over_A', 'A', 'B']).drop('sum_B_over_A', axis=1) 
Out[15]: 
    A   B  C 
5 baz -2.301539 True 
2 baz -0.528172 False 
1 bar -0.611756 True 
4 bar 0.865408 False 
3 foo -1.072969 True 
0 foo 1.624345 False 
+0

Ich bin * sicher * Ich habe einige clevere Art und Weise gesehen dies hier zu tun (so dass im Wesentlichen ein Schlüssel zu sortieren) aber ich kann es nicht finden. –

+0

Freut mich zu wissen, dass es eine bessere Möglichkeit gibt, 'df.A.map 'zu machen (dict (zip (sum_B_over_A.index, sum_B_over_A)))) :) (sollte' get_value' sein, nein?). Ich wusste auch nicht über spaltenweise Tropfen, vielen Dank. (obwohl ich irgendwie die Version w/out die Dummy-Spalte aus irgendeinem Grund bevorzugen) – beardc

+0

@BirdJaguarIV whoops Tippfehler :). Ja, es scheint albern mit einem Dummy (tbh könnte ich schlauer mit meinem Antrag gewesen sein, um es in einem zu tun, und es könnte auch effizienter sein, aber ich entschied, ich würde nicht gerne die Person sein es lesen...).Wie ich schon sagte, ich denke, es gibt eine clevere Möglichkeit, diese Art von Complex-Sortierung durchzuführen: s –

20

Hier ist ein prägnanter Ansatz ...

Die erste Zeile fügt dem Datenrahmen eine Spalte mit der gruppenweisen Summe hinzu. Die zweite Zeile führt die Sortierung aus und entfernt dann die zusätzliche Spalte.

Ergebnis:

A  B   C 
5 baz  -2.301539 True 
2 baz  -0.528172 False 
1 bar  -0.611756 True 
4 bar  0.865408 False 
3 foo  -1.072969 True 
0 foo  1.624345 False 

HINWEIS: sort ist veraltet, verwenden Sie stattdessen sort_values

Verwandte Themen