Pandy grupujące i toczące się okno

Aug 21 2020

Próbuję obliczyć sumę jednego pola za określony okres czasu, po zastosowaniu funkcji grupującej.

Mój zbiór danych wygląda następująco:

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

Chciałbym dodać nową kolumnę dla każdego wiersza, która oblicza sumę sprzedanych (dla każdej grupy "Firma, kraj" z ostatnich 7 dni - nie uwzględniając bieżącego dnia

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

Wypróbowałem następujące, ale zawiera również aktualną datę i podaje różne wartości dla tej samej daty, tj. 03.01.2020

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

Czy w pandach jest funkcja budowania, której mogę używać do wykonywania tych obliczeń?

Odpowiedzi

Terry Aug 21 2020 at 09:27

Jednym ze sposobów byłoby najpierw skonsolidować Sprzedawane wartość każdej z grup ([ „Data”, „Spółka”, „Kraj”]) w jednej linii z wykorzystaniem tymczasowej DF.
Po tym, zastosować .groupbysię .rollingw odstępie 8 rzędów.
Po obliczeniu sumy odejmij wartość każdego wiersza z wartością w kolumnie Sprzedane i dodaj tę kolumnę w oryginalnym DF z.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

Możesz użyć .rollingokna, 8a następnie odjąć sumę daty (dla każdego zgrupowanego wiersza), aby skutecznie uzyskać poprzednie 7 dni. W przypadku tych przykładowych danych powinniśmy również przekazać min_periods=1(w przeciwnym razie otrzymasz NaNwartości, ale dla twojego rzeczywistego zbioru danych będziesz musiał zdecydować, co chcesz zrobić z oknami, które są < 8).

Następnie z .rollingokna 8po prostu zrób kolejną .groupbyz odpowiednich kolumn, ale uwzględnij również Dateten czas i weź maxwartość nowo utworzonej LastWeek_Countkolumny. Musisz wziąć max, ponieważ masz wiele rekordów dziennie, więc biorąc maksimum, bierzesz całkowitą zagregowaną kwotę na Date.

Następnie należy utworzyć serię, która bierze pogrupowane według sumper Date. W ostatnim kroku odejmij sumę według daty od kroczącego maksimum z 8 dni, co jest obejściem sposobu, w jaki można uzyskać sumę poprzednich 7 dni, ponieważ nie ma parametru dla przesunięcia z .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