Pandas groupby e finestra scorrevole

Aug 21 2020

Sto cercando di calcolare la somma di un campo per un periodo di tempo specifico, dopo l'applicazione della funzione di raggruppamento.

Il mio set di dati è simile a questo:

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

Vorrei aggiungere una nuova colonna per ogni riga, che calcola la somma dei Venduti (per ogni gruppo "Azienda, Paese" per gli ultimi 7 giorni - escluso il giorno corrente

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

Ho provato quanto segue, ma include anche la data corrente e fornisce valori diversi per la stessa data, ad esempio 03.01.2020

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

Esiste una funzione incorporata nei panda che posso utilizzare per eseguire questi calcoli?

Risposte

Terry Aug 21 2020 at 09:27

Un modo potrebbe essere quello di consolidare prima il valore Venduto di ciascun gruppo (['Data', 'Azienda', 'Paese']) su una singola riga utilizzando un DF temporaneo.
Successivamente, applica il tuo .groupbycon .rollingcon un intervallo di 8 righe.
Dopo aver calcolato la somma, sottrai il valore di ogni riga con il valore nella colonna Venduto e aggiungi quella colonna nel DF originale 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

È possibile utilizzare una .rollingfinestra di 8e quindi sottrarre la somma della data (per ogni riga raggruppata) per ottenere effettivamente i 7 giorni precedenti. Per questi dati di esempio, dovremmo anche passare min_periods=1(altrimenti otterrai NaNvalori, ma per il tuo set di dati effettivo, dovrai decidere cosa vuoi fare con le finestre che sono < 8).

Quindi dalla .rollingfinestra di 8, fai semplicemente un'altra .groupbydelle colonne pertinenti ma includi anche Datequesta volta e prendi il maxvalore della LastWeek_Countcolonna appena creata. Devi prendere il max, perché hai più record al giorno, quindi prendendo il massimo, stai prendendo l'importo totale aggregato per Date.

Quindi, crea una serie che prenda il raggruppamento per sumper Date. Nel passaggio finale sottrarre la somma per data dal massimo di 8 giorni in movimento, che è una soluzione alternativa a come è possibile ottenere la somma dei 7 giorni precedenti, in quanto non esiste un parametro per un offset 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