pandas groupby tri dans les groupes

167

Je souhaite regrouper mon dataframe sur deux colonnes, puis trier les résultats agrégés au sein des groupes.

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

Je voudrais maintenant trier la colonne de comptage par ordre décroissant dans chacun des groupes. Et puis ne prenez que les trois premières rangées. Pour obtenir quelque chose comme:

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

Réponses:

147

Ce que vous voulez faire est en fait à nouveau un groupby (sur le résultat du premier groupby): trier et prendre les trois premiers éléments par groupe.

À partir du résultat du premier groupe par:

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

Nous regroupons par le premier niveau de l'indice:

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

Ensuite, nous voulons trier ('ordonner') chaque groupe et prendre les trois premiers éléments:

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

Cependant, pour cela, il existe une fonction de raccourci pour ce faire 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
joris
la source
Y aurait-il un moyen de résumer tout ce qui n'est pas contenu dans les trois premiers résultats par groupe et de les ajouter à un groupe source appelé «autre» pour chaque travail?
JoeDanger
32
orderest déconseillé à la sort_valuesplace
zthomas.nc
Merci pour la bonne réponse. Pour une étape supplémentaire, y aurait-il un moyen d'attribuer l'ordre de tri en fonction des valeurs de la colonne groupby? Par exemple, triez par ordre croissant si la valeur est «Acheter» et triez par ordre décroissant si la valeur est «Vendre».
Bowen Liu
174

Vous pouvez également le faire en une seule fois, en effectuant le tri en premier et en utilisant la tête pour prendre les 3 premiers de chaque groupe.

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
tvashtar
la source
14
Garantit-il groupbyque l'ordre est conservé?
toto_tico
52
Il semble que oui; de la documentation de groupby : groupby préserve l'ordre des lignes dans chaque groupe
toto_tico
10
toto_tico- C'est correct, mais il faut faire attention lors de l'interprétation de cette déclaration. L'ordre des lignes DANS UN SEUL GROUPE est conservé, cependant groupby a une instruction sort = True par défaut, ce qui signifie que les groupes eux-mêmes peuvent avoir été triés sur la clé. En d'autres termes, si mon dataframe a des clés (en entrée) 3 2 2 1, .. le groupe par objet affichera les 3 groupes dans l'ordre 1 2 3 (triés). Utilisez sort = False pour vous assurer que l'ordre des groupes et l'ordre des lignes sont préservés.
user2103050
4
head (3) donne plus de 3 résultats?
Nabin
27

Voici un autre exemple de sélection des 3 premiers dans l'ordre trié et de tri au sein des groupes:

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
Surya
la source
9

Essayez plutôt ceci

moyen simple de faire 'groupby' et de trier par ordre décroissant

df.groupby(['companyName'])['overallRating'].sum().sort_values(ascending=False).head(20)
SSCSWAPNIL
la source
8

Si vous n'avez pas besoin de faire la somme d'une colonne, utilisez la réponse de @ tvashtar. Si vous avez besoin de faire la somme, vous pouvez utiliser la réponse de @joris ou celle-ci qui lui est très similaire.

df.groupby(['job']).apply(lambda x: (x.groupby('source')
                                      .sum()
                                      .sort_values('count', ascending=False))
                                     .head(3))
Ted Petrou
la source