2013-02-18 51 views
44

Với dataframe sauPandas sắp xếp theo nhóm tổng hợp và cột

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 

Tôi muốn sắp xếp nó vào nhóm (A) bằng tổng tổng hợp của B, và sau đó bởi các giá trị trong C (không cộng dồn) . Vì vậy, về cơ bản có được thứ tự của các A nhóm với

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 

Và sau đó bởi True/False, để nó cuối cùng trông như thế này:

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 

Làm thế nào điều này có thể được thực hiện?

Trả lời

45

groupby Một :

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

Trong mỗi nhóm, cộng tổng B và truyền giá trị s bằng cách sử dụng biến đổi. Sau đó, sắp xếp theo 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 

Chỉ mục df ban đầu bằng cách chuyển chỉ mục từ trên xuống. Điều này sẽ sắp xếp lại các giá trị A bằng tổng tổng hợp của các giá trị 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 

Cuối cùng, sắp xếp các giá trị 'C' trong các nhóm của 'A' bằng cách sử dụng tùy chọn sort=False để giữ gìn trật tự Một loại từ bước 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 

sạch lên chỉ số df bằng cách sử dụng reset_index với 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

Ngoài ra, tôi cho rằng cờ 'sort = False' của' groupby' sẽ trả về một thứ tự tùy ý, không nhất thiết phải sắp xếp (tôi đoán là tôi đã liên kết chúng với các từ điển python vì một số lý do). Nhưng câu trả lời này ngụ ý rằng cờ được bảo đảm để giữ nguyên thứ tự ban đầu của các dòng dữ liệu? – beardc

+1

Tôi chắc chắn 99% nó giữ nguyên thứ tự của các nhóm khi chúng xuất hiện lần đầu tiên. Tôi không có bất kỳ mã nào để trả lại điều này, nhưng một số thử nghiệm nhanh chóng xác nhận trực giác này. – Zelazny7

+2

Cảm ơn @ Zelazny7 vì câu trả lời này. Đó là chính xác những gì tôi muốn. Tuy nhiên, có vẻ như trong gói gấu trúc mới nhất, để đạt được cùng 'Out [7]', 'inplace = True' nên được thêm vào các đối số trong' Input [7] '. – MoonKnight

8

Một cách để làm điều này là để chèn một cột giả với số tiền để sắp xếp:

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 

và có thể bạn sẽ thả hàng giả:

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

Tôi * chắc chắn * Tôi đã nhìn thấy một số cách thông minh để làm điều này ở đây (chủ yếu cho phép một chìa khóa để sắp xếp) , nhưng tôi dường như không thể tìm thấy nó. –

+0

Vui mừng khi biết có một cách tốt hơn để làm 'df.A.map (dict (zip (sum_B_over_A.index, sum_B_over_A)))' :) (nên là 'get_value', no?). Cũng không biết về các giọt nhỏ theo cột, cảm ơn rất nhiều. (mặc dù tôi kinda thích phiên bản w/ra cột giả vì một lý do nào đó) – beardc

+0

@BirdJaguarIV whoops typo :). Vâng, nó có vẻ ngớ ngẩn khi sử dụng giả (tbh tôi có thể thông minh hơn khi áp dụng [12] để làm điều đó trong một, và nó có thể hiệu quả hơn, nhưng tôi quyết định tôi không muốn trở thành người đọc nó ...).Giống như tôi nói, tôi nghĩ rằng có một cách thông minh để thực hiện loại comlex này: s –

20

Dưới đây là một cách tiếp cận ngắn gọn hơn ...

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

Dòng đầu tiên thêm cột vào khung dữ liệu với tổng số theo nhóm. Dòng thứ hai thực hiện sắp xếp và sau đó loại bỏ cột phụ.

Kết quả:

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 

LƯU Ý: sort bị phản đối, sử dụng sort_values thay

Các vấn đề liên quan