I have this dataframe:
Date | Name | Sum |
---|---|---|
2022-12-01 | James | 10 |
2022-12-01 | Tom | 4 |
2022-12-03 | James | 5 |
2022-12-04 | Adam | 8 |
where i want to group the top names by their sum during a 7 day period (every week from Monday to Sunday)
Expected output:
You could try the following (df
your dataframe, and column Date
with datatimes):
cal = df["Date"].dt.isocalendar()
result = (
df
.drop(columns="Date")
.assign(Week=cal.year.astype("str") + "-" + cal.week.astype("str").str.rjust(2, "0"))
.groupby(["Week", "Name"], as_index=False).sum()
.sort_values("Sum", ascending=False)
.assign(Top=lambda df: df.groupby("Week").transform("cumcount") + 1)
.pivot(index="Top", columns="Week")
.reorder_levels([1, 0], axis=1).sort_index(level=0, axis=1)
)
The first part is similiar to what @AltunE is proposing, but their answer is missing the second part.
Result for a sample dataframe
from random import choices
from string import ascii_uppercase as names
days = pd.date_range("2022-01-01", "2022-12-31", freq="d")
df = pd.DataFrame({
"Date": sorted(choices(days, k=1_000)),
"Name": choices(names, k=1_000),
"Sum": choices(range(101), k=1_000)
})
looks like:
Week 2021-52 2022-01 2022-02 ... 2022-50 2022-51 2022-52
Name Sum Name Sum Name ... Sum Name Sum Name Sum
Top ...
1 L 61.0 U 166.0 T ... 159.0 O 174.0 X 208.0
2 D 45.0 D 63.0 U ... 157.0 E 124.0 E 146.0
3 S 43.0 K 61.0 M ... 116.0 V 108.0 W 89.0
4 T 41.0 W 51.0 E ... 94.0 C 97.0 Y 82.0
5 Z 35.0 B 45.0 Y ... 80.0 J 83.0 D 53.0
6 NaN NaN V 43.0 H ... 63.0 F 72.0 U 52.0
7 NaN NaN A 35.0 D ... 51.0 Q 70.0 S 27.0
8 NaN NaN C 34.0 S ... 48.0 S 67.0 A 26.0
9 NaN NaN H 19.0 W ... 43.0 W 64.0 B 5.0
10 NaN NaN T 9.0 G ... 40.0 R 55.0 NaN NaN
11 NaN NaN R 0.0 F ... 19.0 D 43.0 NaN NaN
12 NaN NaN NaN NaN L ... 18.0 Z 40.0 NaN NaN
13 NaN NaN NaN NaN NaN ... 6.0 N 1.0 NaN NaN
14 NaN NaN NaN NaN NaN ... 5.0 NaN NaN NaN NaN
15 NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN
16 NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN
17 NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN
18 NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN
19 NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN
20 NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN
If you want the Week
labels to be more like you've shown you could do something like this
start = df["Date"] - pd.to_timedelta(df["Date"].dt.weekday, unit="D")
end = start + pd.Timedelta(days=7)
week = "Period " + start.dt.strftime("%Y/%m/%d") + " to " + end.dt.strftime("%Y/%m/%d")
and then adjust the code above with
...
.assign(Week=week)
...