Varias agregaciones de la misma columna usando pandas GroupBy.agg ()

128

¿Existe una forma integrada de pandas para aplicar dos funciones de agregación diferentes f1, f2a la misma columna df["returns"], sin tener que llamar agg()varias veces?

Marco de datos de ejemplo:

import pandas as pd
import datetime as dt

pd.np.random.seed(0)
df = pd.DataFrame({
         "date"    :  [dt.date(2012, x, 1) for x in range(1, 11)], 
         "returns" :  0.05 * np.random.randn(10), 
         "dummy"   :  np.repeat(1, 10)
}) 

La forma sintácticamente incorrecta, pero intuitivamente correcta, de hacerlo sería:

# Assume `f1` and `f2` are defined for aggregating.
df.groupby("dummy").agg({"returns": f1, "returns": f2})

Obviamente, Python no permite claves duplicadas. ¿Hay alguna otra forma de expresar la entrada agg()? ¿Quizás una lista de tuplas [(column, function)]funcionaría mejor para permitir múltiples funciones aplicadas a la misma columna? Pero agg()parece que solo acepta un diccionario.

¿Hay alguna solución para esto además de definir una función auxiliar que solo aplique ambas funciones dentro de ella? (¿Cómo funcionaría esto con la agregación de todos modos?)

ely
fuente
Relacionado - Agregación en pandas
jezrael
2
Desde 0.25 en adelante, pandas proporciona una sintaxis más intuitiva para múltiples agregaciones, así como también cambia el nombre de las columnas de salida. Consulte la documentación sobre agregaciones con nombre .
cs95
Para su información, esta pregunta se hizo en pandas 0.8.x en
9/2012
1
Para su información, la respuesta aceptada también está en desuso: no pase agg () un dictado de dictados.
cs95
@ cs95: Sé que está obsoleto, estoy diciendo que SO se está llenando de viejas soluciones obsoletas de versiones anteriores. SO no tiene una forma de marcar eso, aparte de los comentarios.
smci

Respuestas:

159

Simplemente puede pasar las funciones como una lista:

In [20]: df.groupby("dummy").agg({"returns": [np.mean, np.sum]})
Out[20]:         
           mean       sum
dummy                    
1      0.036901  0.369012

o como diccionario:

In [21]: df.groupby('dummy').agg({'returns':
                                  {'Mean': np.mean, 'Sum': np.sum}})
Out[21]: 
        returns          
           Mean       Sum
dummy                    
1      0.036901  0.369012
bmu
fuente
5
¿Hay alguna forma de especificar los nombres de las columnas de resultados?
Ben
3
@Ben Creo que debes usar un cambio de nombre después. ejemplo de Tom Augspurger (ver celda 25)
Stewbaca
1
@Ben: agregué un ejemplo
bmu
10
@sparc_spread Pasar múltiples funciones como una lista está bien descrito en la documentación de pandas . Cambiar el nombre y pasar varias funciones como un diccionario quedará obsoleto en una versión futura de pandas. Los detalles están en el registro de cambios de 0,20 , que también resumí en otra parte de SO .
joelostblom
3
Ya se ha dicho, pero el uso de diccionarios para cambiar el nombre de las columnas de salida de edad está en desuso. En su lugar, puede especificar una lista de tuplas. Vea esta respuesta.
cs95
101

TLDR; Pandas groupby.aggtiene una sintaxis nueva y más sencilla para especificar (1) agregaciones en múltiples columnas y (2) múltiples agregaciones en una columna. Entonces, para hacer esto para pandas> = 0.25 , use

df.groupby('dummy').agg(Mean=('returns', 'mean'), Sum=('returns', 'sum'))

           Mean       Sum
dummy                    
1      0.036901  0.369012

O

df.groupby('dummy')['returns'].agg(Mean='mean', Sum='sum')

           Mean       Sum
dummy                    
1      0.036901  0.369012

Pandas> = 0,25: Agregación con nombre

Pandas ha cambiado el comportamiento de GroupBy.agga favor de una sintaxis más intuitiva para especificar agregaciones con nombre. Consulte la sección de documentos 0.25 sobre Mejoras , así como los problemas relevantes de GitHub GH18366 y GH26512 .

De la documentación,

Para admitir la agregación específica de columna con control sobre los nombres de las columnas de salida, pandas acepta la sintaxis especial en GroupBy.agg(), conocida como "agregación con nombre", donde

  • Las palabras clave son los nombres de las columnas de salida.
  • Los valores son tuplas cuyo primer elemento es la columna a seleccionar y el segundo elemento es la agregación a aplicar a esa columna. Pandas proporciona a pandas.NamedAgg namedtuple los campos ['columna', 'aggfunc'] para que quede más claro cuáles son los argumentos. Como de costumbre, la agregación puede ser un alias invocable o de cadena.

Ahora puede pasar una tupla mediante argumentos de palabras clave. Las tuplas siguen el formato de (<colName>, <aggFunc>).

import pandas as pd

pd.__version__                                                                                                                            
# '0.25.0.dev0+840.g989f912ee'

# Setup
df = pd.DataFrame({'kind': ['cat', 'dog', 'cat', 'dog'],
                   'height': [9.1, 6.0, 9.5, 34.0],
                   'weight': [7.9, 7.5, 9.9, 198.0]
})

df.groupby('kind').agg(
    max_height=('height', 'max'), min_weight=('weight', 'min'),)

      max_height  min_weight
kind                        
cat          9.5         7.9
dog         34.0         7.5

Alternativamente, puede usar pd.NamedAgg(esencialmente una tupla con nombre) que hace las cosas más explícitas.

df.groupby('kind').agg(
    max_height=pd.NamedAgg(column='height', aggfunc='max'), 
    min_weight=pd.NamedAgg(column='weight', aggfunc='min')
)

      max_height  min_weight
kind                        
cat          9.5         7.9
dog         34.0         7.5

Es aún más simple para Series, simplemente pase aggfunc a un argumento de palabra clave.

df.groupby('kind')['height'].agg(max_height='max', min_height='min')    

      max_height  min_height
kind                        
cat          9.5         9.1
dog         34.0         6.0       

Por último, si los nombres de sus columnas no son identificadores de Python válidos, use un diccionario con desempaquetado:

df.groupby('kind')['height'].agg(**{'max height': 'max', ...})

Pandas <0,25

En versiones más recientes de pandas hasta 0.24, si usa un diccionario para especificar nombres de columna para la salida de agregación, obtendrá un FutureWarning:

df.groupby('dummy').agg({'returns': {'Mean': 'mean', 'Sum': 'sum'}})
# FutureWarning: using a dict with renaming is deprecated and will be removed 
# in a future version

El uso de un diccionario para cambiar el nombre de las columnas está obsoleto en v0.20. En versiones más recientes de pandas, esto se puede especificar más simplemente pasando una lista de tuplas. Si especifica las funciones de esta manera, todas las funciones para esa columna deben especificarse como tuplas de pares (nombre, función).

df.groupby("dummy").agg({'returns': [('op1', 'sum'), ('op2', 'mean')]})

        returns          
            op1       op2
dummy                    
1      0.328953  0.032895

O,

df.groupby("dummy")['returns'].agg([('op1', 'sum'), ('op2', 'mean')])

            op1       op2
dummy                    
1      0.328953  0.032895
cs95
fuente
4
Esta debería ser la respuesta principal debido al uso de una solución más clara y limpia con la versión más nueva de la interfaz.
NKSHELL
Los ejemplos utilizados para la agregación con nombre no resuelven el problema original de usar múltiples agregaciones en la misma columna. Por ejemplo, ¿puede agregar tanto el mínimo como el máximo para la altura sin subconjuntos primero para df.groupby('kind')['height']?
vencedor
1
@victor Agregué un TLDR en la parte superior de la respuesta que aborda directamente la pregunta. Y la respuesta a su segunda pregunta es sí, por favor, eche un vistazo a la edición de mi respuesta.
cs95
Un código más genérico para el último ejemplo de su respuesta> = 0.25 para manejar la agregación de múltiples columnas como esta hubiera sido genial. df.groupby("kind").agg(**{ 'max height': pd.NamedAgg(column='height', aggfunc=max), 'min weight': pd.NamedAgg(column='weight', aggfunc=min) })
Onur Ece
6

Algo como esto funcionaría:

In [7]: df.groupby('dummy').returns.agg({'func1' : lambda x: x.sum(), 'func2' : lambda x: x.prod()})
Out[7]: 
              func2     func1
dummy                        
1     -4.263768e-16 -0.188565
Chang ella
fuente
2
No, esto no funciona. Si observa la cadena de documentos aggregate, dice explícitamente que cuando dictse pasa a, las claves deben ser nombres de columna. Entonces, o su ejemplo es algo que escribió sin verificar este error, o Pandas rompe sus propios documentos aquí.
ely
N / MI no vio la llamada adicional a returnsallí. Entonces, ¿esta es la versión en serie del agregado? Estoy buscando hacer la versión DataFrame de agregado, y quiero aplicar varias agregaciones diferentes a cada columna a la vez.
Ely
1
Pruebe esto: df.groupby ('dummy'). Agg ({'returns': {'func1': lambda x: x.sum (), 'func2': lambda x: x.mean ()}})
Chang Ella
Da un error de afirmación sin mensaje. Por el aspecto del código (pandas.core.internals.py, líneas 406-408, versión 0.7.3), parece que hace una verificación al final para asegurarse de que no devuelve más columnas que claves dentro de la primera. capa del diccionario de agregación.
ely
Funciona bien en master. ¿Quieres intentar actualizar?
Chang She