2015-01-08 3 views
57

Я хочу сгруппировать свой dataframe двумя столбцами, а затем отсортировать агрегированные результаты внутри групп.pandas groupby сортировать по группам

In [167]: 
df 

Out[167]: 
count job source 
0 2 sales A 
1 4 sales B 
2 6 sales C 
3 3 sales D 
4 7 sales E 
5 5 market A 
6 3 market B 
7 2 market C 
8 4 market D 
9 1 market E 

In [168]: 
df.groupby(['job','source']).agg({'count':sum}) 

Out[168]: 
      count 
job  source 
market A 5 
     B 3 
     C 2 
     D 4 
     E 1 
sales A 2 
     B 4 
     C 6 
     D 3 
     E 7 

Теперь я хотел бы отсортировать колонку count в порядке убывания в каждой из групп. А затем возьмите только три верхние строки. Для того, чтобы получить что-то вроде:

  count 
job  source 
market A 5 
     D 4 
     B 3 
sales E 7 
     C 6 
     B 4 

ответ

52

То, что вы хотите сделать, это на самом деле снова GroupBy (по результатам первого GroupBy): вроде и занимают первые три элемента в каждой группе.

Исходя из результата первого GroupBy:

In [60]: df_agg = df.groupby(['job','source']).agg({'count':sum}) 

сгруппируем по первому уровню индекса:

In [63]: g = df_agg['count'].groupby(level=0, group_keys=False) 

Тогда мы хотим сортировать («порядок») каждой группы и взять первые три элемента:

In [64]: res = g.apply(lambda x: x.order(ascending=False).head(3)) 

Однако для этого, есть функция быстрого доступа, чтобы сделать это, nlargest:

In [65]: g.nlargest(3) 
Out[65]: 
job  source 
market A   5 
     D   4 
     B   3 
sales E   7 
     C   6 
     B   4 
dtype: int64 
+0

Был ли способ подытожить все, что не содержится в трех лучших результатах для каждой группы, и добавить их в группу источников, называемую «другое» для каждой работы? – JoeDanger

+7

'order' устарел использовать' sort_values' вместо –

40

Вы также можете просто сделать это на одном дыхании, выполнив сортировку первого и использовать голову, чтобы принять первые 3 из каждой группы.

In[34]: df.sort_values(['job','count'],ascending=False).groupby('job').head(3) 

Out[35]: 
    count  job source 
4  7 sales  E 
2  6 sales  C 
1  4 sales  B 
5  5 market  A 
8  4 market  D 
6  3 market  B 
+2

Гарантирует ли 'groupby', что заказ сохраняется? –

+8

Кажется, это так; из [документации groupby] (http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.groupby.html): __groupby сохраняет порядок строк в каждой группе__ –

6

Вот другой пример с топ 3 на упорядоченном и сортировки внутри группы:

In [43]: import pandas as pd                                      

In [44]: df = pd.DataFrame({"name":["Foo", "Foo", "Baar", "Foo", "Baar", "Foo", "Baar", "Baar"], "count_1":[5,10,12,15,20,25,30,35], "count_2" :[100,150,100,25,250,300,400,500]}) 

In [45]: df                                           
Out[45]: 
    count_1 count_2 name 
0  5  100 Foo 
1  10  150 Foo 
2  12  100 Baar 
3  15  25 Foo 
4  20  250 Baar 
5  25  300 Foo 
6  30  400 Baar 
7  35  500 Baar 


### Top 3 on sorted order: 
In [46]: df.groupby(["name"])["count_1"].nlargest(3)                                
Out[46]: 
name 
Baar 7 35 
     6 30 
     4 20 
Foo 5 25 
     3 15 
     1 10 
dtype: int64 


### Sorting within groups based on column "count_1": 
In [48]: df.groupby(["name"]).apply(lambda x: x.sort_values(["count_1"], ascending = False)).reset_index(drop=True) 
Out[48]: 
    count_1 count_2 name 
0  35  500 Baar 
1  30  400 Baar 
2  20  250 Baar 
3  12  100 Baar 
4  25  300 Foo 
5  15  25 Foo 
6  10  150 Foo 
7  5  100 Foo 
0

Если вам не нужно суммировать столбец, а затем использовать ответ @ Тваштара в. Если вам нужно суммировать, вы можете использовать ответ @joris или тот, который очень похож на него.

df.groupby(['job']).apply(lambda x: (x.groupby('source') 
             .sum() 
             .sort_values('count', ascending=False)) 
            .head(3)) 
Смежные вопросы