Pandy grupujące i toczące się okno
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
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
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