Grupo de pandas y ventana móvil

Aug 21 2020

Estoy tratando de calcular la suma de un campo durante un período de tiempo específico, después de aplicar la función de agrupación.

Mi conjunto de datos se ve así:

Date          Company   Country    Sold
01.01.2020       A          BE       1
02.01.2020       A          BE       0
03.01.2020       A          BE       1
03.01.2020       A          BE       1
04.01.2020       A          BE       1
05.01.2020       B          DE       1
06.01.2020       B          DE       0

Me gustaría agregar una nueva columna por cada fila, que calcule la suma de Vendido (por cada grupo "Empresa, País" durante los últimos 7 días, sin incluir el día actual

Date          Company   Country    Sold      LastWeek_Count
01.01.2020       A          BE       1           0
02.01.2020       A          BE       0           1
03.01.2020       A          BE       1           1
03.01.2020       A          BE       1           1
04.01.2020       A          BE       1           3
05.01.2020       B          DE       1           0
06.01.2020       B          DE       0           1

Intenté lo siguiente, pero también incluye la fecha actual y da valores diferentes para la misma fecha, es decir, 03.01.2020

df['LastWeek_Count'] = df.groupby(['Company', 'Country']).rolling(7, on ='Date')['Sold'].sum().reset_index()

¿Hay una función de compilación en pandas que pueda usar para realizar estos cálculos?

Respuestas

Terry Aug 21 2020 at 09:27

Una forma sería consolidar primero el valor Vendido de cada grupo (['Fecha', 'Compañía', 'País']) en una sola línea usando un DF temporal.
Después de eso, aplique su .groupbycon .rollingun intervalo de 8 filas.
Después de calcular la suma, reste el valor de cada línea con el valor en la columna Vendido y agregue esa columna en el DF original con.merge

#convert Date column to datetime
df['Date'] = pd.to_datetime(df['Date'], format='%d.%m.%Y')
#create a temporary DataFrame
df2 = df.groupby(['Date', 'Company', 'Country'])['Sold'].sum().reset_index()
#calc the lastweek
df2['LastWeek_Count'] = (df2.groupby(['Company', 'Country'])
                            .rolling(8, min_periods=1, on = 'Date')['Sold']
                            .sum().reset_index(drop=True)
                        ) 
#subtract the value of 'lastweek' from the current 'Sold'
df2['LastWeek_Count'] = df2['LastWeek_Count'] - df2['Sold']
#add th2 new column in the original DF
df.merge(df2.drop(columns=['Sold']), on = ['Date', 'Company', 'Country'])
#output:
    Date        Company Country Sold    LastWeek_Count
0   2020-01-01  A       BE      1       0.0
1   2020-01-02  A       BE      0       1.0
2   2020-01-03  A       BE      1       1.0
3   2020-01-03  A       BE      1       1.0
4   2020-01-04  A       BE      1       3.0
5   2020-01-05  B       DE      1       0.0
6   2020-01-06  B       DE      0       1.0
1 DavidErickson Aug 21 2020 at 07:10

Puede usar una .rollingventana de 8y luego restar la suma de la Fecha (para cada fila agrupada) para obtener efectivamente los 7 días anteriores. Para estos datos de muestra, también debemos pasar min_periods=1(de lo contrario, obtendrá NaNvalores, pero para su conjunto de datos real, deberá decidir qué desea hacer con las ventanas que son < 8).

Luego, desde la .rollingventana de 8, simplemente haga otra .groupbyde las columnas relevantes pero también incluya Dateesta vez y tome el maxvalor de la LastWeek_Countcolumna recién creada. Debe tomar el max, porque tiene varios registros por día, por lo que al tomar el máximo, está tomando la cantidad total agregada por Date.

Luego, cree una serie que tome el agrupado por sumpor Date. En el paso final, reste la suma por fecha del máximo móvil de 8 días, que es una solución para obtener la suma de los 7 días anteriores, ya que no hay un parámetro para una compensación con .rolling:

df['Date'] = pd.to_datetime(df['Date'], dayfirst=True)
df['LastWeek_Count'] = df.groupby(['Company', 'Country']).rolling(8, min_periods=1, on='Date')['Sold'].sum().reset_index()['Sold']
df['LastWeek_Count'] = df.groupby(['Company', 'Country', 'Date'])['LastWeek_Count'].transform('max')
s = df.groupby(['Company', 'Country', 'Date'])['Sold'].transform('sum')
df['LastWeek_Count'] = (df['LastWeek_Count']-s).astype(int)

Out[17]: 
        Date Company Country  Sold  LastWeek_Count
0 2020-01-01       A      BE     1               0
1 2020-01-02       A      BE     0               1
2 2020-01-03       A      BE     1               1
3 2020-01-03       A      BE     1               1
4 2020-01-04       A      BE     1               3
5 2020-01-05       B      DE     1               0
6 2020-01-06       B      DE     0               1