Agregar y cambiar el nombre de una columna en un DataFrame Multiindex

El propósito de esta publicación es entender cómo agregar una columna a un nivel en un MultiIndex.DataFrame usando apply() y shift()

Crear el dataframe

 import pandas as pd df = pd.DataFrame( [ [5777, 100, 5385, 200, 5419, 4887, 100, 200], [4849, 0, 4539, 0, 3381, 0, 0, ], [4971, 0, 3824, 0, 4645, 3424, 0, 0, ], [4827, 200, 3459, 300, 4552, 3153, 100, 200, ], [5207, 0, 3670, 0, 4876, 3358, 0, 0, ], ], index=pd.to_datetime(['2010-01-01', '2010-01-02', '2010-01-03', '2010-01-04', '2010-01-05']), columns=pd.MultiIndex.from_tuples( [('Portfolio A', 'GBP', 'amount'), ('Portfolio A', 'GBP', 'injection'), ('Portfolio B', 'EUR', 'amount'), ('Portfolio B', 'EUR', 'injection'), ('Portfolio A', 'USD', 'amount'), ('Portfolio A', 'USD', 'injection'), ('Portfolio B', 'JPY', 'amount'), ('Portfolio B', 'JPY', 'injection')]) ).sortlevel(axis=1) print df 

Me gustaría usar el siguiente método para agregar una nueva columna a cada moneda en el nivel 2 llamada daily_added_value:

 def do_nothing(group): return group def calc_daily_added_value(group): g = (group['amount'] - group['amount'].shift(periods=1, freq=None, axis=0) -df['injection'].shift(periods=1, freq=None, axis=0)).round(decimals=2) g.index = ['daily_added_value'] return g pd.concat([df.T.groupby(level=0).apply(f).T for f in [calc_daily_added_value,do_nothing ]], axis=1).sort_index(axis=1) 

Sin embargo, esto KeyError: 'amount' un error de clave: KeyError: 'amount'

¿Cuál es la syntax correcta para el método calc_daily_added_value() ?


Siguiendo con la respuesta a continuación todavía hay un problema

Añadiendo los trabajos diarios de retorno.

 dav = df.loc[:, pd.IndexSlice[:, :, 'daily_added_value']] amount = df.loc[:, pd.IndexSlice[:, :, 'amount']] dr = (dav.values / amount.shift()) * 100 dr.columns.set_levels(['daily_return'], level=2, inplace=True) df = pd.concat([df, dr], axis=1).sortlevel(axis=1) 

Agregar las devoluciones compuestas acumuladas FALLAS

 dr = df.loc[:, pd.IndexSlice[:, :, 'daily_return']] drc = 100*((1+dr / 100).cumprod()-1) drc.columns.set_levels(['daily_return_cumulative'], level=2, inplace=True) df = pd.concat([df, drc], axis=1).sort_index(axis=1) df.head() 

esto falla porque le faltan los valores. pero, si agrego esto, ¿se convierte en una matriz?

Lo que es extraño aquí, sin embargo, es que drc es de hecho un DataFrame de forma correcta, etc. y parece contener resultados correctos.

Esto falla en esta línea:

 drc.columns.set_levels(['daily_return_cumulative'], level=2, inplace=True) 

El error es ValueError: On level 2, label max (2) >= length of level (1). NOTE: this index is in an inconsistent state ValueError: On level 2, label max (2) >= length of level (1). NOTE: this index is in an inconsistent state

¿Cómo se puede volver a colocar el índice en un estado consistente?

Salta el groupby lo que no es necesario.

 amount = df.loc[:, pd.IndexSlice[:, :, 'amount']] inject = df.loc[:, pd.IndexSlice[:, :, 'injection']] dav = amount - amount.shift() - inject.shift().values #dav.columns.set_levels(['daily_added_value'], level=2, inplace=True) pd.concat([df, dav], axis=1).sort_index(axis=1).T 

Nota: usé T para obtener una imagen que encajaba fácilmente

introduzca la descripción de la imagen aquí

Parece que hay un error en set_levels y, como tal, no se recomienda utilizarlo.

Solución para cambiar el nombre de la columna MultiIndex en el DataFrame dav

 def map_level(df, dct, level=2): index = df.index index.set_levels([[dct.get(item, item) for item in names] if i==level else names for i, names in enumerate(index.levels)], inplace=True) dct = {'amount':'daily_added_value'} map_level(dav.T, dct, level=2)