Remuestreo dentro de un MultiIndex Pandas

Tengo algunos datos jerárquicos que se convierten en datos de series de tiempo que se parecen a esto:

df = pandas.DataFrame( {'value_a': values_a, 'value_b': values_b}, index=[states, cities, dates]) df.index.names = ['State', 'City', 'Date'] df value_a value_b State City Date Georgia Atlanta 2012-01-01 0 10 2012-01-02 1 11 2012-01-03 2 12 2012-01-04 3 13 Savanna 2012-01-01 4 14 2012-01-02 5 15 2012-01-03 6 16 2012-01-04 7 17 Alabama Mobile 2012-01-01 8 18 2012-01-02 9 19 2012-01-03 10 20 2012-01-04 11 21 Montgomery 2012-01-01 12 22 2012-01-02 13 23 2012-01-03 14 24 2012-01-04 15 25 

Me gustaría hacer un tiempo de remuestreo por ciudad, así que algo como

 df.resample("2D", how="sum") 

saldría

  value_a value_b State City Date Georgia Atlanta 2012-01-01 1 21 2012-01-03 5 25 Savanna 2012-01-01 9 29 2012-01-03 13 33 Alabama Mobile 2012-01-01 17 37 2012-01-03 21 41 Montgomery 2012-01-01 25 45 2012-01-03 29 49 

tal como está, df.resample('2D', how='sum') me atrapa

 TypeError: Only valid with DatetimeIndex or PeriodIndex 

Justo lo suficiente, pero esperaba que esto funcionara:

 >>> df.swaplevel('Date', 'State').resample('2D', how='sum') TypeError: Only valid with DatetimeIndex or PeriodIndex 

¿En qué momento me estoy quedando sin ideas … hay algún modo de astackr y desastackr que pueda ayudarme?

pd.Grouper permite especificar una “instrucción groupby para un objeto de destino”. En particular, puede usarlo para agrupar por fechas incluso si df.index no es un DatetimeIndex :

 df.groupby(pd.Grouper(freq='2D', level=-1)) 

El level=-1 le dice a pd.Grouper que busque las fechas en el último nivel del MultiIndex. Además, puede usar esto junto con otros valores de nivel del índice:

 level_values = df.index.get_level_values result = (df.groupby([level_values(i) for i in [0,1]] +[pd.Grouper(freq='2D', level=-1)]).sum()) 

Parece un poco incómodo, pero using_Grouper resulta ser mucho más rápido que mi sugerencia original, using_reset_index :

 import numpy as np import pandas as pd import datetime as DT def using_Grouper(df): level_values = df.index.get_level_values return (df.groupby([level_values(i) for i in [0,1]] +[pd.Grouper(freq='2D', level=-1)]).sum()) def using_reset_index(df): df = df.reset_index(level=[0, 1]) return df.groupby(['State','City']).resample('2D').sum() def using_stack(df): # http://stackoverflow.com/a/15813787/190597 return (df.unstack(level=[0,1]) .resample('2D').sum() .stack(level=[2,1]) .swaplevel(2,0)) def make_orig(): values_a = range(16) values_b = range(10, 26) states = ['Georgia']*8 + ['Alabama']*8 cities = ['Atlanta']*4 + ['Savanna']*4 + ['Mobile']*4 + ['Montgomery']*4 dates = pd.DatetimeIndex([DT.date(2012,1,1)+DT.timedelta(days = i) for i in range(4)]*4) df = pd.DataFrame( {'value_a': values_a, 'value_b': values_b}, index = [states, cities, dates]) df.index.names = ['State', 'City', 'Date'] return df def make_df(N): dates = pd.date_range('2000-1-1', periods=N) states = np.arange(50) cities = np.arange(10) index = pd.MultiIndex.from_product([states, cities, dates], names=['State', 'City', 'Date']) df = pd.DataFrame(np.random.randint(10, size=(len(index),2)), index=index, columns=['value_a', 'value_b']) return df df = make_orig() print(using_Grouper(df)) 

rendimientos

  value_a value_b State City Date Alabama Mobile 2012-01-01 17 37 2012-01-03 21 41 Montgomery 2012-01-01 25 45 2012-01-03 29 49 Georgia Atlanta 2012-01-01 1 21 2012-01-03 5 25 Savanna 2012-01-01 9 29 2012-01-03 13 33 

Aquí hay un punto de referencia que compara using_Grouper , using_reset_index , using_stack en un using_stack de 5000 filas:

 In [30]: df = make_df(10) In [34]: len(df) Out[34]: 5000 In [32]: %timeit using_Grouper(df) 100 loops, best of 3: 6.03 ms per loop In [33]: %timeit using_stack(df) 10 loops, best of 3: 22.3 ms per loop In [31]: %timeit using_reset_index(df) 1 loop, best of 3: 659 ms per loop 

Una alternativa usando stack / unstack

 df.unstack(level=[0,1]).resample('2D', how='sum').stack(level=[2,1]).swaplevel(2,0) value_a value_b State City Date Georgia Atlanta 2012-01-01 1 21 Alabama Mobile 2012-01-01 17 37 Montgomery 2012-01-01 25 45 Georgia Savanna 2012-01-01 9 29 Atlanta 2012-01-03 5 25 Alabama Mobile 2012-01-03 21 41 Montgomery 2012-01-03 29 49 Georgia Savanna 2012-01-03 13 33 

Notas:

  1. No tengo idea sobre la comparación de rendimiento
  2. Posible error de pandas: la stack (nivel = [2,1]) funcionó, pero la stack (nivel = [1,2]) falló

Esto funciona:

 df.groupby(level=[0,1]).apply(lambda x: x.set_index('Date').resample('2D', how='sum')) value_a value_b State City Date Alabama Mobile 2012-01-01 17 37 2012-01-03 21 41 Montgomery 2012-01-01 25 45 2012-01-03 29 49 Georgia Atlanta 2012-01-01 1 21 2012-01-03 5 25 Savanna 2012-01-01 9 29 2012-01-03 13 33 

Si la columna Fecha es una cadena, entonces conviértala a datetime de antemano:

 df['Date'] = pd.to_datetime(df['Date']) 

Sé que esta pregunta tiene algunos años, pero tuve el mismo problema y llegué a una solución más simple que requiere 1 línea:

 >>> import pandas as pd >>> ts = pd.read_pickle('time_series.pickle') >>> ts xxxxxx1 yyyyyyyyyyyyyyyyyyyyyy1 2012-07-01 1 2012-07-02 13 2012-07-03 1 2012-07-04 1 2012-07-05 10 2012-07-06 4 2012-07-07 47 2012-07-08 0 2012-07-09 3 2012-07-10 22 2012-07-11 3 2012-07-12 0 2012-07-13 22 2012-07-14 1 2012-07-15 2 2012-07-16 2 2012-07-17 8 2012-07-18 0 2012-07-19 1 2012-07-20 10 2012-07-21 0 2012-07-22 3 2012-07-23 0 2012-07-24 35 2012-07-25 6 2012-07-26 1 2012-07-27 0 2012-07-28 6 2012-07-29 23 2012-07-30 0 .. xxxxxxN yyyyyyyyyyyyyyyyyyyyyyN 2014-06-02 0 2014-06-03 1 2014-06-04 0 2014-06-05 0 2014-06-06 0 2014-06-07 0 2014-06-08 2 2014-06-09 0 2014-06-10 0 2014-06-11 0 2014-06-12 0 2014-06-13 0 2014-06-14 0 2014-06-15 0 2014-06-16 0 2014-06-17 0 2014-06-18 0 2014-06-19 0 2014-06-20 0 2014-06-21 0 2014-06-22 0 2014-06-23 0 2014-06-24 0 2014-06-25 4 2014-06-26 0 2014-06-27 1 2014-06-28 0 2014-06-29 0 2014-06-30 1 2014-07-01 0 dtype: int64 >>> ts.unstack().T.resample('W', how='sum').T.stack() xxxxxx1 yyyyyyyyyyyyyyyyyyyyyy1 2012-06-25/2012-07-01 1 2012-07-02/2012-07-08 76 2012-07-09/2012-07-15 53 2012-07-16/2012-07-22 24 2012-07-23/2012-07-29 71 2012-07-30/2012-08-05 38 2012-08-06/2012-08-12 258 2012-08-13/2012-08-19 144 2012-08-20/2012-08-26 184 2012-08-27/2012-09-02 323 2012-09-03/2012-09-09 198 2012-09-10/2012-09-16 348 2012-09-17/2012-09-23 404 2012-09-24/2012-09-30 380 2012-10-01/2012-10-07 367 2012-10-08/2012-10-14 163 2012-10-15/2012-10-21 338 2012-10-22/2012-10-28 252 2012-10-29/2012-11-04 197 2012-11-05/2012-11-11 336 2012-11-12/2012-11-18 234 2012-11-19/2012-11-25 143 2012-11-26/2012-12-02 204 2012-12-03/2012-12-09 296 2012-12-10/2012-12-16 146 2012-12-17/2012-12-23 85 2012-12-24/2012-12-30 198 2012-12-31/2013-01-06 214 2013-01-07/2013-01-13 229 2013-01-14/2013-01-20 192 ... xxxxxxN yyyyyyyyyyyyyyyyyyyyyyN 2013-12-09/2013-12-15 3 2013-12-16/2013-12-22 0 2013-12-23/2013-12-29 0 2013-12-30/2014-01-05 1 2014-01-06/2014-01-12 3 2014-01-13/2014-01-19 6 2014-01-20/2014-01-26 11 2014-01-27/2014-02-02 0 2014-02-03/2014-02-09 1 2014-02-10/2014-02-16 4 2014-02-17/2014-02-23 3 2014-02-24/2014-03-02 1 2014-03-03/2014-03-09 4 2014-03-10/2014-03-16 0 2014-03-17/2014-03-23 0 2014-03-24/2014-03-30 9 2014-03-31/2014-04-06 1 2014-04-07/2014-04-13 1 2014-04-14/2014-04-20 1 2014-04-21/2014-04-27 2 2014-04-28/2014-05-04 8 2014-05-05/2014-05-11 7 2014-05-12/2014-05-18 5 2014-05-19/2014-05-25 2 2014-05-26/2014-06-01 8 2014-06-02/2014-06-08 3 2014-06-09/2014-06-15 0 2014-06-16/2014-06-22 0 2014-06-23/2014-06-29 5 2014-06-30/2014-07-06 1 dtype: int64 

ts.unstack().T.resample('W', how='sum').T.stack() es todo lo que tomó! Muy fácil y parece bastante performante. El pickle que estoy leyendo es 331M, por lo que esta es una estructura de datos bastante robusta; el remuestreo toma solo un par de segundos en mi MacBook Pro.

Tuve el mismo problema, me .resample la cabeza por un tiempo, pero luego leí la documentación de la función .resample en los documentos 0.19.2 , y veo que hay un nuevo kwarg llamado “nivel” que puedes usar para especificar un Nivel en un MultiIndex.

Edición: Más detalles en la sección “Novedades” .

No he comprobado la eficacia de esto, pero mi manera instintiva de realizar operaciones de fecha y hora en un multi-índice fue mediante una especie de proceso manual de “dividir-aplicar-combinar” utilizando un diccionario de comprensión.

Suponiendo que su DataFrame no está indexado. (Puedes hacer .reset_index() primero), esto funciona de la siguiente manera:

  1. Agrupar por las columnas sin fecha
  2. Establecer “Fecha” como índice y volver a muestrear cada fragmento
  3. Vuelva a montar utilizando pd.concat

El código final se ve así:

 pd.concat({g: x.set_index("Date").resample("2D").mean() for g, x in house.groupby(["State", "City"])})