2013-02-18 13 views
44

otrzymuje następujące dataframePandy sortować według kruszywa grupowej i kolumny

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 

Chciałbym uporządkować je w grupach (A) przez zagregowane sumy B, a następnie przez wartość w C (nie zagregowane) . Więc w zasadzie uzyskać kolejność grup A z

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 

a następnie Prawda/Fałsz, tak że ostatecznie wygląda tak:

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 

Jak można to zrobić?

Odpowiedz

45

GroupBy A :

In [0]: grp = df.groupby('A') 

W każdej grupie suma wartości B i wyświetl wartość s za pomocą transformacji. Następnie posortuj według B:

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 

Indeksuj oryginalny df, przekazując indeks z góry. Spowoduje to ponowne zamówienie wartości A o łącznej sumy wartości B:

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 

Wreszcie, sortowanie wartości „C” w ramach grup „A” za pomocą opcji sort=False zachować kolejność rodzajem z etapu 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 

Clean up indeks df za pomocą reset_index z drop=True:

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

Ponadto założyłem, że 'groupby's' sort = False' zwraca zwrócony losowo, niekoniecznie uporządkowany porządek (domyślam się, że kojarzyłem je ze słownikami python z jakiegoś powodu). Ale ta odpowiedź oznacza, że ​​flaga gwarantuje zachowanie pierwotnej kolejności rzędów ramek danych? – beardc

+1

Jestem w 99% pewien, że zachowuje kolejność grup po ich pierwszym wyświetleniu. Nie mam żadnego kodu, żeby to poprzeć, ale niektóre szybkie testy potwierdzają tę intuicję. – Zelazny7

+2

Dzięki @ Zelazny7 za tę odpowiedź. Dokładnie tego chcę. Jednak wydaje się, że w najnowszym pakiecie pand, aby osiągnąć to samo 'Out [7]', 'inplace = True' powinno być dodane do argumentów w' Input [7] '. – MoonKnight

8

Jednym ze sposobów, aby to zrobić, aby wstawić obojętne kolumnę z kwot w celu sortowania:

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 

a może byś upuść atrapę rzędu:

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

jestem pewien * * Widziałem jakiś sprytny sposób, aby to zrobić tutaj (w zasadzie pozwala kluczem do sortowania) , ale nie mogę tego znaleźć. –

+0

Cieszę się, że istnieje lepszy sposób na zrobienie 'df.A.map (dict (zip (sum_B_over_A.index, sum_B_over_A)))' :) (powinno być 'get_value', no?). Też nie wiedziałem o kroplach kolumnowych, wielkie dzięki. (choć z jakiegoś powodu wolę wersję bez kolumny dummy) – beardc

+0

@BirdJaguarIV whoops literówka :). Tak, wydaje się to głupie przy użyciu manekina (tbh mogłem być bardziej sprytny z moim zastosowaniem [12], aby zrobić to w jednym, i może być bardziej wydajny, ale zdecydowałem, że nie chciałbym być osobą czytając to...).Tak jak mówię, myślę, że istnieje sprytny sposób na zrobienie tego rodzaju złożonego sortowania: s –

20

Oto bardziej zwięzły podejście ...

df['a_bsum'] = df.groupby('A')['B'].transform(sum) 
df.sort(['a_bsum','C'], ascending=[True, False]).drop('a_bsum', axis=1) 

Pierwsza linia dodaje kolumnę do ramki danych z sumą grupową. Druga linia wykonuje sortowanie, a następnie usuwa dodatkową kolumnę.

Wynik:

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 

UWAGA: sort jest przestarzała, użyj sort_values zamiast

Powiązane problemy