varias seleccionar recorrer filtrar datos data columnas columna agregar python group-by dataframe pandas

seleccionar - Python Pandas ¿Cómo asignar los resultados de operación groupby a columnas en el marco de datos padre?



seleccionar dos columnas en python (5)

Tengo el siguiente marco de datos en IPython, donde cada fila es una sola acción:

In [261]: bdata Out[261]: <class ''pandas.core.frame.DataFrame''> Int64Index: 21210 entries, 0 to 21209 Data columns: BloombergTicker 21206 non-null values Company 21210 non-null values Country 21210 non-null values MarketCap 21210 non-null values PriceReturn 21210 non-null values SEDOL 21210 non-null values yearmonth 21210 non-null values dtypes: float64(2), int64(1), object(4)

Deseo aplicar una operación de agrupamiento por grupo que calcula el rendimiento medio ponderado por capítulos en todo, por cada fecha en la columna "año-año".

Esto funciona como se esperaba:

In [262]: bdata.groupby("yearmonth").apply(lambda x: (x["PriceReturn"]*x["MarketCap"]/x["MarketCap"].sum()).sum()) Out[262]: yearmonth 201204 -0.109444 201205 -0.290546

Pero luego quiero "enviar" estos valores de vuelta a los índices en el marco de datos original y guardarlos como columnas constantes donde coinciden las fechas.

In [263]: dateGrps = bdata.groupby("yearmonth") In [264]: dateGrps["MarketReturn"] = dateGrps.apply(lambda x: (x["PriceReturn"]*x["MarketCap"]/x["MarketCap"].sum()).sum()) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) /mnt/bos-devrnd04/usr6/home/espears/ws/Research/Projects/python-util/src/util/<ipython-input-264-4a68c8782426> in <module>() ----> 1 dateGrps["MarketReturn"] = dateGrps.apply(lambda x: (x["PriceReturn"]*x["MarketCap"]/x["MarketCap"].sum()).sum()) TypeError: ''DataFrameGroupBy'' object does not support item assignment

Me doy cuenta de que esta tarea ingenua no debería funcionar. Pero, ¿cuál es el modismo pandas "correcto" para asignar el resultado de una operación groupby a una nueva columna en el dataframe padre?

Al final, quiero una columna llamada "MarketReturn" que será un valor constante repetido para todos los índices que tengan fecha coincidente con la salida de la operación groupby.

Un truco para lograr esto sería el siguiente:

marketRetsByDate = dateGrps.apply(lambda x: (x["PriceReturn"]*x["MarketCap"]/x["MarketCap"].sum()).sum()) bdata["MarketReturn"] = np.repeat(np.NaN, len(bdata)) for elem in marketRetsByDate.index.values: bdata["MarketReturn"][bdata["yearmonth"]==elem] = marketRetsByDate.ix[elem]

Pero esto es lento, malo y antiponónico.


¿Esto funciona?

capWeighting = lambda x: (x["PriceReturn"]*x["MarketCap"]/x["MarketCap"].sum()).sum() bdata["MarketReturn"] = bdata.groupby("yearmonth").transform(capWeighting)

Yo uso reindex_like para esto:

summedbdata = bdata.groupby("yearmonth").apply(lambda x: (x["PriceReturn"]*x["MarketCap"]/x["MarketCap"].sum()).sum()) summedbdata.set_index(''yearmonth'').reindex_like(bdata.set_index(''yearmonth'').sort_index(), method=''ffill'')


¿Puedo sugerir el método de transform (en lugar de agregar)? Si lo usa en su ejemplo original, debe hacer lo que quiera (la transmisión).


Como regla general al usar groupby (), si usa la función .transform () pandas devolverá una tabla con la misma longitud que su original. Cuando use otras funciones como .sum () o .first (), entonces pandas devolverá una tabla donde cada fila es un grupo.

No estoy seguro de cómo funciona esto con apply, pero implementar funciones lambda elaboradas con transform puede ser bastante complicado, por lo que la estrategia que considero más útil es crear las variables que necesito, ubicarlas en el conjunto de datos original y luego realizar mis operaciones allí.

Si entiendo lo que intentas hacer correctamente (me disculpo si me equivoco) primero puedes calcular el límite total del mercado para cada grupo:

bdata[''group_MarketCap''] = bdata.groupby(''yearmonth'')[''MarketCap''].transform(''sum'')

Esto agregará una columna llamada "group_MarketCap" a sus datos originales que contendría la suma de los límites de mercado para cada grupo. Entonces puede calcular los valores ponderados directamente:

bdata[''weighted_P''] = bdata[''PriceReturn''] * (bdata[''MarketCap'']/bdata[''group_MarketCap''])

Y finalmente se calcularía el promedio ponderado para cada grupo usando la misma función de transformación:

bdata[''MarketReturn''] = bdata.groupby(''yearmonth'')[''weighted_P''].transform(''sum'')

Tiendo a construir mis variables de esta manera. A veces puedes hacer todo en un solo comando, pero eso no siempre funciona con groupby () porque la mayoría de las veces los pandas necesitan crear una instancia del nuevo objeto para operar en la escala de conjunto de datos completa (es decir, no puedes agregue dos columnas juntas si todavía no existe una).

Espero que esto ayude :)


Mientras sigo explorando todas las formas increíblemente inteligentes que se apply concatenar las piezas que se le otorgan, aquí hay otra forma de agregar una nueva columna en el elemento principal después de una operación groupby.

In [236]: df Out[236]: yearmonth return 0 201202 0.922132 1 201202 0.220270 2 201202 0.228856 3 201203 0.277170 4 201203 0.747347 In [237]: def add_mkt_return(grp): .....: grp[''mkt_return''] = grp[''return''].sum() .....: return grp .....: In [238]: df.groupby(''yearmonth'').apply(add_mkt_return) Out[238]: yearmonth return mkt_return 0 201202 0.922132 1.371258 1 201202 0.220270 1.371258 2 201202 0.228856 1.371258 3 201203 0.277170 1.024516 4 201203 0.747347 1.024516


In [97]: df = pandas.DataFrame({''month'': np.random.randint(0,11, 100), ''A'': np.random.randn(100), ''B'': np.random.randn(100)}) In [98]: df.join(df.groupby(''month'')[''A''].sum(), on=''month'', rsuffix=''_r'') Out[98]: A B month A_r 0 -0.040710 0.182269 0 -0.331816 1 -0.004867 0.642243 1 2.448232 2 -0.162191 0.442338 4 2.045909 3 -0.979875 1.367018 5 -2.736399 4 -1.126198 0.338946 5 -2.736399 5 -0.992209 -1.343258 1 2.448232 6 -1.450310 0.021290 0 -0.331816 7 -0.675345 -1.359915 9 2.722156