panda multiple method groupby functions example columns column python group-by aggregate-functions pandas

python - multiple - pandas groupby count



Aplicar funciones mĂșltiples a varias columnas groupby (2)

Los docs muestran cómo aplicar múltiples funciones en un grupo por objeto a la vez usando un dict con los nombres de las columnas de salida como las claves:

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 agrupado por grupo. 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 serán operadas varias veces). Además, algunas funciones dependerán de otras columnas en el grupo por objeto (como funciones de suma). 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 está tomando mucho tiempo, (creo que lleva mucho tiempo iterar a través de un grupo por objeto). Tendré que cambiarlo para que recorra todo el grupo por objeto en una sola ejecución, pero me pregunto si hay una forma integrada en pandas para hacerlo de una manera un tanto 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 esperaba recibo un KeyError (ya que las claves tienen que ser una columna si se llama a agg desde un DataFrame).

¿Existe alguna forma de hacer lo que me gustaría hacer, o la posibilidad de que se agregue esta funcionalidad, o solo tendré que repetir el grupo manualmente?

Gracias


La segunda mitad de la respuesta actualmente aceptada está desactualizada y tiene dos inconvenientes. Primero y más importante, ya no puede pasar un diccionario de diccionarios al método agg groupby. Segundo, nunca uses .ix .

Si desea trabajar con dos columnas separadas al mismo tiempo, le sugiero que utilice el método de apply , cuya implicidad pasa un DataFrame a la función aplicada. Usemos un marco de datos similar al de arriba

df = pd.DataFrame(np.random.rand(4,4), columns=list(''abcd'')) df[''group''] = [0, 0, 1, 1] df a b c d group 0 0.418500 0.030955 0.874869 0.145641 0 1 0.446069 0.901153 0.095052 0.487040 0 2 0.843026 0.936169 0.926090 0.041722 1 3 0.635846 0.439175 0.828787 0.714123 1

Un diccionario asignado de nombres de columna a funciones de agregación sigue siendo una forma perfectamente buena de realizar una agregación.

df.groupby(''group'').agg({''a'':[''sum'', ''max''], ''b'':''mean'', ''c'':''sum'', ''d'': lambda x: x.max() - x.min()}) a b c d sum max mean sum <lambda> group 0 0.560541 0.507058 0.418546 1.707651 0.129667 1 0.187757 0.157958 0.887315 0.533531 0.652427

Si no le gusta el nombre feo de la columna lambda, puede usar una función normal y proporcionar un nombre personalizado al atributo especial __name__ como este:

def max_min(x): return x.max() - x.min() max_min.__name__ = ''Max minus Min'' df.groupby(''group'').agg({''a'':[''sum'', ''max''], ''b'':''mean'', ''c'':''sum'', ''d'': max_min}) a b c d sum max mean sum Max minus Min group 0 0.560541 0.507058 0.418546 1.707651 0.129667 1 0.187757 0.157958 0.887315 0.533531 0.652427

Usar apply y devolver una serie

Ahora, si tenía varias columnas que necesitaban interactuar juntas, entonces no puede usar agg , que implícitamente pasa una serie a la función de agregación. Al usar, apply el grupo completo como un DataFrame 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:

def f(x): d = {} d[''a_sum''] = x[''a''].sum() d[''a_max''] = x[''a''].max() d[''b_mean''] = x[''b''].mean() d[''c_d_prodsum''] = (x[''c''] * x[''d'']).sum() return pd.Series(d, index=[''a_sum'', ''a_max'', ''b_mean'', ''c_d_prodsum'']) df.groupby(''group'').apply(f) a_sum a_max b_mean c_d_prodsum group 0 0.560541 0.507058 0.418546 0.118106 1 0.187757 0.157958 0.887315 0.276808

Si está enamorado de MultiIndexes, aún puede devolver una Serie con una como esta:

def f_mi(x): d = [] d.append(x[''a''].sum()) d.append(x[''a''].max()) d.append(x[''b''].mean()) d.append((x[''c''] * x[''d'']).sum()) return pd.Series(d, index=[[''a'', ''a'', ''b'', ''c_d''], [''sum'', ''max'', ''mean'', ''prodsum'']]) df.groupby(''group'').apply(f_mi) a b c_d sum max mean prodsum group 0 0.560541 0.507058 0.418546 0.118106 1 0.187757 0.157958 0.887315 0.276808


Para la primera parte puede pasar un dict de nombres de columna para claves y una lista de funciones para los valores:

In [28]: df Out[28]: A B C D E GRP 0 0.395670 0.219560 0.600644 0.613445 0.242893 0 1 0.323911 0.464584 0.107215 0.204072 0.927325 0 2 0.321358 0.076037 0.166946 0.439661 0.914612 1 3 0.133466 0.447946 0.014815 0.130781 0.268290 1 In [26]: f = {''A'':[''sum'',''mean''], ''B'':[''prod'']} In [27]: df.groupby(''GRP'').agg(f) Out[27]: A B sum mean prod GRP 0 0.719580 0.359790 0.102004 1 0.454824 0.227412 0.034060

ACTUALIZACIÓN 1:

Debido a que la función de agregado funciona en Series, las referencias a los otros nombres de columna se pierden. Para evitar esto, puede hacer referencia al marco de datos completo e indexarlo usando los índices de grupo dentro de la función lambda.

Aquí hay una solución hacky:

In [67]: f = {''A'':[''sum'',''mean''], ''B'':[''prod''], ''D'': lambda g: df.loc[g.index].E.sum()} In [69]: df.groupby(''GRP'').agg(f) Out[69]: A B D sum mean prod <lambda> GRP 0 0.719580 0.359790 0.102004 1.170219 1 0.454824 0.227412 0.034060 1.182901

Aquí, la columna resultante ''D'' se compone de los valores ''E'' sumados.

ACTUALIZACIÓN 2:

Este es un método que creo que hará todo lo que pidas. Primero crea una función lambda personalizada. A continuación, g hace referencia al grupo. Cuando se agrega, g será una serie. Al pasar g.index a df.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 g[] que selecciona solo aquellas filas que cumplen los criterios.

In [95]: cust = lambda g: g[df.loc[g.index][''C''] < 0.5].sum() In [96]: f = {''A'':[''sum'',''mean''], ''B'':[''prod''], ''D'': {''my name'': cust}} In [97]: df.groupby(''GRP'').agg(f) Out[97]: A B D sum mean prod my name GRP 0 0.719580 0.359790 0.102004 0.204072 1 0.454824 0.227412 0.034060 0.570441