pandas groupby sort into groups

167

Quiero agrupar mi marco de datos por dos columnas y luego ordenar los resultados agregados dentro de los grupos.

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

Ahora me gustaría ordenar la columna de conteo en orden descendente dentro de cada uno de los grupos. Y luego toma solo las tres primeras filas. Para obtener algo como:

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

Respuestas:

147

Lo que quieres hacer es en realidad nuevamente un groupby (en el resultado del primer groupby): ordena y toma los primeros tres elementos por grupo.

A partir del resultado del primer grupo por:

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

Agrupamos por el primer nivel del índice:

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

Luego queremos ordenar ('ordenar') cada grupo y tomar los primeros tres elementos:

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

Sin embargo, para esto, hay una función de acceso directo para hacer esto 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
fuente
¿Habría una forma de resumir todo lo que no está contenido en los tres primeros resultados por grupo y agregarlos a un grupo fuente llamado "otro" para cada trabajo?
JoeDanger
31
orderes un uso obsoleto en su sort_valueslugar
zthomas.nc
Gracias por la gran respuesta. Para otro paso, ¿habría alguna forma de asignar el orden de clasificación en función de los valores en la columna groupby? Por ejemplo, ordene en forma ascendente si el valor es 'Comprar' y ordene en forma descendente si el valor es 'Vender'.
Bowen Liu
174

También puede hacerlo de una vez, haciendo primero la clasificación y usando la cabeza para tomar los primeros 3 de cada grupo.

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
fuente
14
¿ groupbyGarantiza que se conserva el pedido?
toto_tico
52
Parece que sí; de la documentación de groupby : groupby conserva el orden de las filas dentro de cada grupo
toto_tico
10
toto_tico- Eso es correcto, sin embargo, se debe tener cuidado al interpretar esa declaración. El orden de las filas DENTRO DE UN GRUPO ÚNICO se conserva, sin embargo, groupby tiene una instrucción sort = True de forma predeterminada, lo que significa que los grupos mismos pueden haberse ordenado en la clave. En otras palabras, si mi marco de datos tiene claves (en la entrada) 3 2 2 1, .. el grupo por objeto mostrará los 3 grupos en el orden 1 2 3 (ordenados). Use sort = False para asegurarse de que se conserva el orden de grupo y el orden de fila.
user2103050
44
head (3) da más de 3 resultados?
Nabin
27

Aquí hay otro ejemplo de tomar los 3 primeros en orden ordenado y ordenar dentro de los grupos:

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
fuente
9

Intenta esto en su lugar

manera simple de hacer 'groupby' y ordenar en orden descendente

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

Si no necesita sumar una columna, use la respuesta de @ tvashtar. Si necesita sumar, entonces puede usar la respuesta de @joris o esta que es muy similar.

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