Esta respuesta proporciona una solución para obtener una suma móvil de una columna agrupada por otra columna según una ventana de fecha. Para reproducirlo aquí:
df = pd.DataFrame( { 'ID': {0: 10001, 1: 10001, 2: 10001, 3: 10001, 4: 10002, 5: 10002, 6: 10002}, 'Date': { 0: datetime.datetime(2019, 7, 1), 1: datetime.datetime(2019, 5, 1), 2: datetime.datetime(2019, 6, 25), 3: datetime.datetime(2019, 5, 27), 4: datetime.datetime(2019, 6, 29), 5: datetime.datetime(2019, 7, 18), 6: datetime.datetime(2019, 7, 15) }, 'Amount': {0: 50, 1: 15, 2: 10, 3: 20, 4: 25, 5: 35, 6: 40}, } ) amounts = df.groupby(["ID"]).apply(lambda g: g.sort_values('Date').rolling('28d', on='Date').sum()) df['amount_4wk_rolling'] = df["Date"].map(amounts.set_index('Date')['Amount'])
Producción:
+-------+------------+--------+--------------------+ | ID | Date | Amount | amount_4wk_rolling | +-------+------------+--------+--------------------+ | 10001 | 01/07/2019 | 50 | 60 | | 10001 | 01/05/2019 | 15 | 15 | | 10001 | 25/06/2019 | 10 | 10 | | 10001 | 27/05/2019 | 20 | 35 | | 10002 | 29/06/2019 | 25 | 25 | | 10002 | 18/07/2019 | 35 | 100 | | 10002 | 15/07/2019 | 40 | 65 | +-------+------------+--------+--------------------+
Sin embargo, si dos de las fechas son iguales, aparece el error:
pandas.errors.InvalidIndexError: Reindexing only valid with uniquely valued Index objects
Esto tiene sentido, ya que puedo ver en la línea final que se está utilizando Date
para establecer un índice que ahora ya no es único. Sin embargo, como realmente no entiendo qué hace esa línea final, estoy un poco perplejo al tratar de desarrollar una solución alternativa.
¿Alguien podría ayudar?
Según los comentarios a la pregunta, parece que OP ya encontró una solución. Sin embargo, este es un intento de proporcionar otra forma de resolver esto, que es resolver la causa raíz del error: valores de fecha duplicados.
Para resolverlo, podemos agregar la agregación por fecha dentro de la aplicación. En el fragmento a continuación, los valores de Amount
se agregan usando sum
, pero es posible que en algunos contextos se deba usar otra agregación, por ejemplo, min
o max
. Esta es la parte relevante:
.apply( lambda g: ( g .groupby('Date', as_index=False) .agg({'Amount': 'sum'}) .rolling('28d', on='Date') .sum() ) )
Y el fragmento completo a continuación:
import pandas as pd import datetime df = pd.DataFrame( { 'ID': {0: 10001, 1: 10001, 2: 10001, 3: 10001, 4: 10002, 5: 10002, 6: 10002}, 'Date': { 0: datetime.datetime(2019, 7, 1), 1: datetime.datetime(2019, 5, 1), 2: datetime.datetime(2019, 6, 25), 3: datetime.datetime(2019, 5, 27), 4: datetime.datetime(2019, 6, 29), 5: datetime.datetime(2019, 7, 18), 6: datetime.datetime(2019, 7, 18) }, 'Amount': {0: 50, 1: 15, 2: 10, 3: 20, 4: 25, 5: 35, 6: 40}, } ) amounts = ( df .groupby(["ID"]) .apply( lambda g: ( g .groupby('Date', as_index=False) .agg({'Amount': 'sum'}) .rolling('28d', on='Date') .sum() ) ) ) df['amount_4wk_rolling'] = df["Date"].map(amounts.set_index('Date')['Amount']) # this yields # ID Date Amount amount_4wk_rolling # 0 10001 2019-07-01 50 60.0 # 1 10001 2019-05-01 15 15.0 # 2 10001 2019-06-25 10 10.0 # 3 10001 2019-05-27 20 35.0 # 4 10002 2019-06-29 25 25.0 # 5 10002 2019-07-18 35 100.0 # 6 10002 2019-07-18 40 100.0
El problema es el índice de amounts
de primer nivel:
>>> df ID Date Amount 0 10001 2019-07-01 50 1 10001 2019-05-01 15 2 10001 2019-06-25 10 3 10001 2019-05-27 20 4 10002 2019-06-29 25 5 10002 2019-07-18 35 # <- dup date 6 10002 2019-07-18 40 # <- dup date >>> amounts Amount Date ID ID 10001 1 15.0 2019-05-01 10001.0 3 35.0 2019-05-27 20002.0 2 10.0 2019-06-25 10001.0 0 60.0 2019-07-01 20002.0 10002 4 25.0 2019-06-29 10002.0 5 60.0 2019-07-18 20004.0 6 100.0 2019-07-18 30006.0
Si asigna amounts
en las columnas Date
para fusionar sus datos en df
, obtuvo su error porque Pandas no sabe qué valores debe usar para 2019-07-18. Si observa detenidamente, el segundo nivel del índice de amounts
es el índice de su marco de datos original.
Entonces, si elimina el índice de primer nivel establecido por groupby
, puede usar la asignación directa:
df['amount_4wk_rolling'] = amounts.droplevel(0)['Amount'] print(df) # Output: ID Date Amount amount_4wk_rolling 0 10001 2019-07-01 50 60.0 1 10001 2019-05-01 15 15.0 2 10001 2019-06-25 10 10.0 3 10001 2019-05-27 20 35.0 4 10002 2019-06-29 25 25.0 5 10002 2019-07-18 35 60.0 6 10002 2019-07-18 40 100.0