Obtenga estadísticas para cada grupo (como conteo, media, etc.) usando pandas GroupBy?

439

Tengo un marco de datos dfy uso varias columnas para groupby:

df['col1','col2','col3','col4'].groupby(['col1','col2']).mean()

De la manera anterior, casi obtengo la tabla (marco de datos) que necesito. Lo que falta es una columna adicional que contenga el número de filas en cada grupo. En otras palabras, quiero decir, pero también me gustaría saber cuántos números se usaron para obtener estos medios. Por ejemplo, en el primer grupo hay 8 valores y en el segundo 10 y así sucesivamente.

En pocas palabras: ¿Cómo llego grupos sabia estadísticas de una trama de datos?

romano
fuente

Respuestas:

427

En el groupbyobjeto, la aggfunción puede tomar una lista para aplicar varios métodos de agregación a la vez. Esto debería darte el resultado que necesitas:

df[['col1', 'col2', 'col3', 'col4']].groupby(['col1', 'col2']).agg(['mean', 'count'])
Boud
fuente
2
Creo que necesita la referencia de columna para ser una lista. Quizás te refieres a: df[['col1','col2','col3','col4']].groupby(['col1','col2']).agg(['mean', 'count'])
rysqui
43
Esto crea cuatro columnas de conteo, pero ¿cómo obtener solo una? (La pregunta pide "una columna adicional" y eso es lo que me gustaría también.)
Jaan
16
Consulte mi respuesta si desea obtener solo una countcolumna por grupo.
Pedro M Duarte
¿Qué sucede si tengo un recuento llamado separado y en lugar de contar las filas del tipo agrupado, necesito agregar a lo largo de la columna Conteos?
Abhishek Bhatia
@Jaan result = df['col1','col2','col3','col4'].groupby(['col1', 'col2']).mean() ; counts = times.groupby(['col1', 'col2']).size() ; result['count'] = counts
alvitawa
913

Respuesta rápida:

La forma más sencilla de obtener recuentos de filas por grupo es llamando .size(), lo que devuelve un Series:

df.groupby(['col1','col2']).size()


Por lo general, desea este resultado como un DataFrame(en lugar de a Series) para que pueda hacer:

df.groupby(['col1', 'col2']).size().reset_index(name='counts')


Si desea averiguar cómo calcular los recuentos de filas y otras estadísticas para cada grupo, continúe leyendo a continuación.


Ejemplo detallado:

Considere el siguiente marco de datos de ejemplo:

In [2]: df
Out[2]: 
  col1 col2  col3  col4  col5  col6
0    A    B  0.20 -0.61 -0.49  1.49
1    A    B -1.53 -1.01 -0.39  1.82
2    A    B -0.44  0.27  0.72  0.11
3    A    B  0.28 -1.32  0.38  0.18
4    C    D  0.12  0.59  0.81  0.66
5    C    D -0.13 -1.65 -1.64  0.50
6    C    D -1.42 -0.11 -0.18 -0.44
7    E    F -0.00  1.42 -0.26  1.17
8    E    F  0.91 -0.47  1.35 -0.34
9    G    H  1.48 -0.63 -1.14  0.17

Primero usemos .size()para obtener los recuentos de filas:

In [3]: df.groupby(['col1', 'col2']).size()
Out[3]: 
col1  col2
A     B       4
C     D       3
E     F       2
G     H       1
dtype: int64

Luego, usemos .size().reset_index(name='counts')para obtener los recuentos de filas:

In [4]: df.groupby(['col1', 'col2']).size().reset_index(name='counts')
Out[4]: 
  col1 col2  counts
0    A    B       4
1    C    D       3
2    E    F       2
3    G    H       1


Incluyendo resultados para más estadísticas

Cuando desea calcular estadísticas sobre datos agrupados, generalmente se ve así:

In [5]: (df
   ...: .groupby(['col1', 'col2'])
   ...: .agg({
   ...:     'col3': ['mean', 'count'], 
   ...:     'col4': ['median', 'min', 'count']
   ...: }))
Out[5]: 
            col4                  col3      
          median   min count      mean count
col1 col2                                   
A    B    -0.810 -1.32     4 -0.372500     4
C    D    -0.110 -1.65     3 -0.476667     3
E    F     0.475 -0.47     2  0.455000     2
G    H    -0.630 -0.63     1  1.480000     1

El resultado anterior es un poco molesto de tratar debido a las etiquetas de columna anidadas y también porque los recuentos de filas son por columna.

Para obtener más control sobre la salida, generalmente divido las estadísticas en agregaciones individuales que luego combino usando join. Se parece a esto:

In [6]: gb = df.groupby(['col1', 'col2'])
   ...: counts = gb.size().to_frame(name='counts')
   ...: (counts
   ...:  .join(gb.agg({'col3': 'mean'}).rename(columns={'col3': 'col3_mean'}))
   ...:  .join(gb.agg({'col4': 'median'}).rename(columns={'col4': 'col4_median'}))
   ...:  .join(gb.agg({'col4': 'min'}).rename(columns={'col4': 'col4_min'}))
   ...:  .reset_index()
   ...: )
   ...: 
Out[6]: 
  col1 col2  counts  col3_mean  col4_median  col4_min
0    A    B       4  -0.372500       -0.810     -1.32
1    C    D       3  -0.476667       -0.110     -1.65
2    E    F       2   0.455000        0.475     -0.47
3    G    H       1   1.480000       -0.630     -0.63



Notas al pie

El código utilizado para generar los datos de prueba se muestra a continuación:

In [1]: import numpy as np
   ...: import pandas as pd 
   ...: 
   ...: keys = np.array([
   ...:         ['A', 'B'],
   ...:         ['A', 'B'],
   ...:         ['A', 'B'],
   ...:         ['A', 'B'],
   ...:         ['C', 'D'],
   ...:         ['C', 'D'],
   ...:         ['C', 'D'],
   ...:         ['E', 'F'],
   ...:         ['E', 'F'],
   ...:         ['G', 'H'] 
   ...:         ])
   ...: 
   ...: df = pd.DataFrame(
   ...:     np.hstack([keys,np.random.randn(10,4).round(2)]), 
   ...:     columns = ['col1', 'col2', 'col3', 'col4', 'col5', 'col6']
   ...: )
   ...: 
   ...: df[['col3', 'col4', 'col5', 'col6']] = \
   ...:     df[['col3', 'col4', 'col5', 'col6']].astype(float)
   ...: 


Descargo de responsabilidad:

Si algunas de las columnas que está agregando tienen valores nulos, entonces realmente desea ver la fila del grupo que cuenta como una agregación independiente para cada columna. De lo contrario, es posible que se equivoque sobre cuántos registros se están utilizando realmente para calcular cosas como la media porque los pandas dejarán caer NaNentradas en el cálculo de la media sin informarle al respecto.

Pedro M Duarte
fuente
1
Oye, realmente me gusta tu solución, particularmente la última, donde usas el método de encadenamiento. Sin embargo, como a menudo es necesario, para aplicar diferentes funciones de agregación a diferentes columnas, también se pueden concatenar los marcos de datos resultantes utilizando pd.concat. Esto puede ser más fácil de leer que el encadenamiento
subsiguiente
44
buena solución, pero In [5]: counts_df = pd.DataFrame(df.groupby('col1').size().rename('counts'))tal vez sea mejor establecer el tamaño () como una nueva columna si desea manipular el marco de datos para un análisis posterior, que debería sercounts_df = pd.DataFrame(df.groupby('col1').size().reset_index(name='counts')
LancelotHolmes
2
¡Gracias por el bit "Incluyendo resultados para más estadísticas"! Dado que mi próxima búsqueda fue sobre aplanar el índice múltiple resultante en columnas, vincularé a la respuesta aquí: stackoverflow.com/a/50558529/1026
Nickolay
¡Excelente! ¿Podría darme una pista sobre cómo agregar isnulla esta consulta para tenerla también en una columna? 'col4': ['median', 'min', 'count', 'isnull']
Peter.k
38

Una función para gobernarlos a todos: GroupBy.describe

Retornos count, mean, std, y otras estadísticas útiles por grupo.

df.groupby(['col1', 'col2'])['col3', 'col4'].describe()

# Setup
np.random.seed(0)
df = pd.DataFrame({'A' : ['foo', 'bar', 'foo', 'bar',
                          'foo', 'bar', 'foo', 'foo'],
                   'B' : ['one', 'one', 'two', 'three',
                          'two', 'two', 'one', 'three'],
                   'C' : np.random.randn(8),
                   'D' : np.random.randn(8)})

from IPython.display import display

with pd.option_context('precision', 2):
    display(df.groupby(['A', 'B'])['C'].describe())

           count  mean   std   min   25%   50%   75%   max
A   B                                                     
bar one      1.0  0.40   NaN  0.40  0.40  0.40  0.40  0.40
    three    1.0  2.24   NaN  2.24  2.24  2.24  2.24  2.24
    two      1.0 -0.98   NaN -0.98 -0.98 -0.98 -0.98 -0.98
foo one      2.0  1.36  0.58  0.95  1.15  1.36  1.56  1.76
    three    1.0 -0.15   NaN -0.15 -0.15 -0.15 -0.15 -0.15
    two      2.0  1.42  0.63  0.98  1.20  1.42  1.65  1.87

Para obtener estadísticas específicas, solo selecciónelas,

df.groupby(['A', 'B'])['C'].describe()[['count', 'mean']]

           count      mean
A   B                     
bar one      1.0  0.400157
    three    1.0  2.240893
    two      1.0 -0.977278
foo one      2.0  1.357070
    three    1.0 -0.151357
    two      2.0  1.423148

describefunciona para varias columnas (cambiar ['C']a['C', 'D'] —o elimínelo por completo— y vea qué sucede, el resultado es un marco de datos con columnas MultiIndexed).

También obtienes estadísticas diferentes para datos de cadena. Aquí hay un ejemplo,

df2 = df.assign(D=list('aaabbccc')).sample(n=100, replace=True)

with pd.option_context('precision', 2):
    display(df2.groupby(['A', 'B'])
               .describe(include='all')
               .dropna(how='all', axis=1))

              C                                                   D                
          count  mean       std   min   25%   50%   75%   max count unique top freq
A   B                                                                              
bar one    14.0  0.40  5.76e-17  0.40  0.40  0.40  0.40  0.40    14      1   a   14
    three  14.0  2.24  4.61e-16  2.24  2.24  2.24  2.24  2.24    14      1   b   14
    two     9.0 -0.98  0.00e+00 -0.98 -0.98 -0.98 -0.98 -0.98     9      1   c    9
foo one    22.0  1.43  4.10e-01  0.95  0.95  1.76  1.76  1.76    22      2   a   13
    three  15.0 -0.15  0.00e+00 -0.15 -0.15 -0.15 -0.15 -0.15    15      1   c   15
    two    26.0  1.49  4.48e-01  0.98  0.98  1.87  1.87  1.87    26      2   b   15

Para más información, consulte la documentación .

cs95
fuente
No todas las distribuciones son normales. IQR sería increíble.
Brad
7

Podemos hacerlo fácilmente usando groupby y count. Pero, debemos recordar usar reset_index ().

df[['col1','col2','col3','col4']].groupby(['col1','col2']).count().\
reset_index()
Nimesh
fuente
3
Esta solución funciona siempre que no haya un valor nulo en las columnas, de lo contrario puede ser engañoso (el recuento será menor que el número real de observación por grupo).
Adrien Pacifico
4

Para obtener múltiples estadísticas, contraiga el índice y conserve los nombres de columna:

df = df.groupby(['col1','col2']).agg(['mean', 'count'])
df.columns = [ ' '.join(str(i) for i in col) for col in df.columns]
df.reset_index(inplace=True)
df

Produce:

** ingrese la descripción de la imagen aquí **

Jake Drew
fuente
1

Cree un objeto de grupo y llame a métodos como el siguiente ejemplo:

grp = df.groupby(['col1',  'col2',  'col3']) 

grp.max() 
grp.mean() 
grp.describe() 
Mahendra
fuente
1

Por favor intente este código

new_column=df[['col1', 'col2', 'col3', 'col4']].groupby(['col1', 'col2']).count()
df['count_it']=new_column
df

Creo que ese código agregará una columna llamada 'cuenta' que cuenta de cada grupo

Ichsan
fuente