Los documentos muestran cómo aplicar múltiples funciones en un objeto groupby a la vez usando un dict con los nombres de las columnas de salida como las teclas:
In [563]: grouped['D'].agg({'result1' : np.sum,
.....: 'result2' : np.mean})
.....:
Out[563]:
result2 result1
A
bar -0.579846 -1.739537
foo -0.280588 -1.402938
Sin embargo, esto solo funciona en un objeto groupby de la Serie. Y cuando un dict se pasa de manera similar a un grupo por DataFrame, espera que las claves sean los nombres de columna a los que se aplicará la función.
Lo que quiero hacer es aplicar múltiples funciones a varias columnas (pero ciertas columnas se operarán varias veces). Además, algunas funciones dependerán de otras columnas en el objeto groupby (como las funciones sumif). Mi solución actual es ir columna por columna y hacer algo como el código anterior, usando lambdas para funciones que dependen de otras filas. Pero esto lleva mucho tiempo (creo que toma mucho tiempo iterar a través de un objeto groupby). Tendré que cambiarlo para que repita todo el objeto groupby en una sola ejecución, pero me pregunto si hay una forma integrada en los pandas para hacer esto de manera limpia.
Por ejemplo, he intentado algo como
grouped.agg({'C_sum' : lambda x: x['C'].sum(),
'C_std': lambda x: x['C'].std(),
'D_sum' : lambda x: x['D'].sum()},
'D_sumifC3': lambda x: x['D'][x['C'] == 3].sum(), ...)
pero como se esperaba, obtengo un KeyError (ya que las claves deben ser una columna si agg
se llama desde un DataFrame).
¿Hay alguna forma integrada de hacer lo que me gustaría hacer, o existe la posibilidad de que se agregue esta funcionalidad, o solo tendré que recorrer el grupo manualmente?
Gracias
Respuestas:
La segunda mitad de la respuesta actualmente aceptada está desactualizada y tiene dos desvalorizaciones. Primero y más importante, ya no puede pasar un diccionario de diccionarios al
agg
método groupby. Segundo, nunca lo uses.ix
.Si desea trabajar con dos columnas separadas al mismo tiempo, sugeriría usar el
apply
método que implícitamente pasa un DataFrame a la función aplicada. Usemos un marco de datos similar al de arribaUn diccionario asignado de los nombres de columna a las funciones de agregación sigue siendo una forma perfectamente buena de realizar una agregación.
Si no le gusta el nombre de la columna lambda fea, puede usar una función normal y proporcionar un nombre personalizado al
__name__
atributo especial como este:Usar
apply
y devolver una serieAhora, si tenía varias columnas que necesitaban interactuar juntas, entonces no puede usarlas
agg
, lo que implícitamente pasa una Serie a la función de agregación. Cuando se usaapply
todo el grupo como un DataFrame se pasa a la función.Recomiendo hacer una única función personalizada que devuelva una serie de todas las agregaciones. Use el índice de la serie como etiquetas para las nuevas columnas:
Si está enamorado de MultiIndexes, aún puede devolver una Serie con una como esta:
fuente
a
dentro del grupo,0
¿no debería ser así0.418500 + 0.446069 = 0.864569
? Lo mismo es cierto para otras celdas, los números no parecen sumar. ¿Podría ser un marco de datos subyacente ligeramente diferente que se utilizó en los ejemplos posteriores?Para la primera parte, puede pasar un dict de nombres de columna para claves y una lista de funciones para los valores:
ACTUALIZACIÓN 1:
Debido a que la función de agregado funciona en Series, se pierden las referencias a los otros nombres de columna. Para evitar esto, puede hacer referencia al marco de datos completo e indexarlo utilizando los índices de grupo dentro de la función lambda.
Aquí hay una solución alternativa:
Aquí, la columna 'D' resultante se compone de los valores 'E' sumados.
ACTUALIZACIÓN 2:
Aquí hay un método que creo que hará todo lo que pidas. Primero haga una función lambda personalizada. A continuación, g hace referencia al grupo. Al agregar, g será una Serie. Pasando
g.index
adf.ix[]
selecciona el grupo actual de df. Luego pruebo si la columna C es menor que 0.5. La serie booleana devuelta se pasa a lag[]
que selecciona solo aquellas filas que cumplen los criterios.fuente
{funcname: func}
como valores en lugar de listas para mantener mis nombres personalizados. Pero en cualquier caso, no puedo pasar unlambda
que usa otras columnas (comolambda x: x['D'][x['C'] < 3].sum()
arriba: "KeyError: 'D'"). ¿Alguna idea de si eso es posible?KeyError: 'D'
df['A'].ix[g.index][df['C'] < 0].sum()
. Sin embargo, esto está empezando a ser bastante complicado: creo que para facilitar la lectura, puede ser preferible el bucle manual, además, no estoy seguro de que haya una manera de darle mi nombre preferido en elagg
argumento (en lugar de<lambda>
). Voy a mantener la esperanza de que alguien pueda conocer de una manera más sencilla ...{'D': {'my name':lambda function}}
y hará que la clave dict interna sea el nombre de la columna.Como alternativa (principalmente en estética) a la respuesta de Ted Petrou, descubrí que prefería una lista un poco más compacta. No considere aceptarlo, es solo un comentario mucho más detallado sobre la respuesta de Ted, más el código / datos. Python / pandas no es mi primer / mejor, pero encontré esto para leer bien:
Me parece más una reminiscencia de
dplyr
tuberías ydata.table
comandos encadenados. Sin decir que son mejores, solo que me son más familiares. (Ciertamente reconozco el poder y, para muchos, la preferencia de utilizardef
funciones más formalizadas para este tipo de operaciones. Esta es solo una alternativa, no necesariamente mejor).Generé datos de la misma manera que Ted, agregaré una semilla para la reproducibilidad.
fuente
Pandas >= 0.25.0
, agregaciones nombradasDesde la versión pandas
0.25.0
o superior, nos estamos alejando de la agregación y el cambio de nombre basados en el diccionario, y nos estamos moviendo hacia agregaciones con nombre que aceptan atuple
. Ahora podemos agregar simultáneamente + cambiar el nombre a un nombre de columna más informativo:Ejemplo :
Aplicar
GroupBy.agg
con agregación con nombre:fuente
Nuevo en la versión 0.25.0.
Para admitir la agregación específica de columna con control sobre los nombres de columna de salida, pandas acepta la sintaxis especial en GroupBy.agg () , conocida como "agregación con nombre" , donde
pandas.NamedAgg es solo una tupla nombrada. También se permiten tuplas simples.
No se pasan argumentos de palabras clave adicionales a las funciones de agregación. Solo los pares de (columna, aggfunc) deben pasarse como ** kwargs. Si sus funciones de agregación requieren argumentos adicionales, aplíquelos parcialmente con functools.partial ().
La agregación con nombre también es válida para las agrupaciones de grupo por serie. En este caso no hay selección de columna, por lo que los valores son solo las funciones.
fuente
La respuesta de Ted es asombrosa. Terminé usando una versión más pequeña de eso en caso de que alguien esté interesado. Útil cuando busca una agregación que depende de valores de varias columnas:
crear un marco de datos
agrupando y agregando con apply (usando múltiples columnas)
agrupación y agregación con agregado (usando múltiples columnas)
Me gusta este enfoque ya que todavía puedo usar el agregado. Tal vez la gente me diga por qué se necesita aplicar para llegar a varias columnas al hacer agregaciones en grupos.
Parece obvio ahora, pero siempre que no seleccione la columna de interés directamente después del grupo , tendrá acceso a todas las columnas del marco de datos desde su función de agregación.
solo acceso a la columna seleccionada
acceso a todas las columnas ya que la selección es, después de todo, la magia
o de manera similar
Espero que esto ayude.
fuente