The request

From Catherine Hale, Chief Executive Officer
Your goal
Step 1 of 4. Build daily revenue (every calendar day) and its rolling 7-day sum (the day and the 6 days before it), computed on the full history. Set result to a DataFrame with the 3 days of 2025 with the highest rolling 7-day sum: columns order_date (the last day of the window), weekday (its English day name, e.g. 'Thursday') and revenue_7d (rounded to 2), sorted by revenue_7d descending, index reset.
- Use the
ordersDataFramenot done yet - Use the
order_itemsDataFramenot done yet - Show exactly
order_dateweekdayrevenue_7dnot done yet
Step 2 of 4. Compute revenue per calendar month for each year (2023, 2024, 2025), then the average of the three years for each month of the year. Set result to a DataFrame with columns month (the English month name, e.g. 'January') and avg_revenue (rounded to 2), 12 rows in calendar order January to December, index reset.
- Use the
ordersDataFramenot done yet - Use the
order_itemsDataFramenot done yet - Show exactly
monthavg_revenuenot done yet
Step 3 of 4. Build monthly revenue (months labelled with their first day) on the full history and its year-over-year change in percent (pct_change(12) times 100, rounded to 1). Set result to a DataFrame for October, November and December 2025 with columns month (text 'YYYY-MM', e.g. '2025-10'), short (the 3-letter month, e.g. 'Oct'), revenue (rounded to 2) and yoy_pct, sorted by month ascending, index reset.
- Use the
ordersDataFramenot done yet - Use the
order_itemsDataFramenot done yet - Show exactly
monthshortrevenueyoy_pctnot done yet
Step 4 of 4. Set result to a DataFrame with columns year, quarter (1 to 4), revenue (sum for that quarter, rounded to 2) and share_of_year (the quarter's revenue divided by that year's total, rounded to 3), 12 rows sorted by year then quarter ascending, index reset.
- Use the
ordersDataFramenot done yet - Use the
order_itemsDataFramenot done yet - Show exactly
yearquarterrevenueshare_of_yearnot done yet
Step 1 · your code
Loading the editor…
Reference solution
| order_id | customer_id | order_date | status | channel |
|---|---|---|---|---|
| 1001 | 125 | 2023-01-01 20:51:19 | completed | web |
| 1002 | 156 | 2023-01-02 07:58:46 | completed | web |
| 1003 | 22 | 2023-01-02 10:25:27 | completed | marketplace |
| 1004 | 48 | 2023-01-02 11:21:51 | completed | marketplace |
Mote checks your result
Press Run. Each row that matches the goal becomes a firefly in your lantern.
Step 2 · your code
Loading the editor…
Reference solution
| order_id | customer_id | order_date | status | channel |
|---|---|---|---|---|
| 1001 | 125 | 2023-01-01 20:51:19 | completed | web |
| 1002 | 156 | 2023-01-02 07:58:46 | completed | web |
| 1003 | 22 | 2023-01-02 10:25:27 | completed | marketplace |
| 1004 | 48 | 2023-01-02 11:21:51 | completed | marketplace |
Mote checks your result
Press Run. Each row that matches the goal becomes a firefly in your lantern.
Step 3 · your code
Loading the editor…
Reference solution
| order_id | customer_id | order_date | status | channel |
|---|---|---|---|---|
| 1001 | 125 | 2023-01-01 20:51:19 | completed | web |
| 1002 | 156 | 2023-01-02 07:58:46 | completed | web |
| 1003 | 22 | 2023-01-02 10:25:27 | completed | marketplace |
| 1004 | 48 | 2023-01-02 11:21:51 | completed | marketplace |
Mote checks your result
Press Run. Each row that matches the goal becomes a firefly in your lantern.
Step 4 · your code
Loading the editor…
Reference solution
| order_id | customer_id | order_date | status | channel |
|---|---|---|---|---|
| 1001 | 125 | 2023-01-01 20:51:19 | completed | web |
| 1002 | 156 | 2023-01-02 07:58:46 | completed | web |
| 1003 | 22 | 2023-01-02 10:25:27 | completed | marketplace |
| 1004 | 48 | 2023-01-02 11:21:51 | completed | marketplace |
Mote checks your result
Press Run. Each row that matches the goal becomes a firefly in your lantern.
What Catherine gets, and why
DataFrames
Loaded for you as pandas DataFrames, plus pd and np.
orders12,000 rows · 7 columns
order_idintegercustomer_idintegerorder_datetimestampstatusvarcharchannelvarcharshipping_feedecimalcoupon_codevarchar
| order_id | customer_id | order_date | status | channel | shipping_fee | coupon_code |
|---|---|---|---|---|---|---|
| 1001 | 125 | 2023-01-01 20:51:19 | completed | web | 0.00 | NaN |
| 1002 | 156 | 2023-01-02 07:58:46 | completed | web | 0.00 | NaN |
| 1003 | 22 | 2023-01-02 10:25:27 | completed | marketplace | 4.99 | WELCOME10 |
| 1004 | 48 | 2023-01-02 11:21:51 | completed | marketplace | 4.99 | WELCOME10 |
| 1005 | 82 | 2023-01-02 11:43:36 | completed | web | 0.00 | WELCOME10 |
order_items30,338 rows · 6 columns
order_item_idintegerorder_idintegerproduct_idintegerquantityintegerunit_pricedecimaldiscount_pctdecimal
| order_item_id | order_id | product_id | quantity | unit_price | discount_pct |
|---|---|---|---|---|---|
| 1 | 1001 | 121 | 9 | 31.99 | 0.00 |
| 2 | 1001 | 154 | 3 | 11.99 | 0.00 |
| 3 | 1002 | 154 | 4 | 11.99 | 0.00 |
| 4 | 1002 | 194 | 1 | 149.00 | 0.00 |
| 5 | 1002 | 193 | 2 | 19.99 | 0.00 |
customers2,018 rows · 9 columns
customer_idintegerfirst_namevarcharlast_namevarcharemailvarcharcityvarcharstatevarcharsignup_datedatesegmentvarcharreferral_sourcevarchar
| customer_id | first_name | last_name | city | state | signup_date | segment | referral_source | |
|---|---|---|---|---|---|---|---|---|
| 1 | Owen | Hudson | owen.hudson@icloud.com | San Francisco | CA | 2022-06-02 | consumer | search |
| 2 | Douglas | Mendoza | dmendoza69@gmail.com | Atlanta | GA | 2022-06-02 | consumer | NaN |
| 3 | Henry | Brooks | hbrooks@lakesidecafe.com | Los Angeles | CA | 2022-06-04 | business | NaN |
| 4 | Brian | Graham | bgraham@bluecedarhomes.com | Seattle | WA | 2022-06-06 | business | |
| 5 | Kyle | Jimenez | kjimenez27@yahoo.com | Indianapolis | IN | 2022-06-07 | consumer | NaN |
products120 rows · 8 columns
product_idintegernamevarcharcategoryvarcharsubcategoryvarcharunit_pricedecimalunit_costdecimallaunched_ondateis_activeboolean
| product_id | name | category | subcategory | unit_price | unit_cost | launched_on | is_active |
|---|---|---|---|---|---|---|---|
| 101 | Vale Velvet Loveseat | Furniture | Sofas | 1659.00 | NaN | 2023-09-11 | true |
| 102 | Alder Oak Coffee Table | Furniture | Tables | 919.00 | 503.50 | 2024-05-16 | true |
| 103 | Orchard Rattan Dining Chair | Furniture | Chairs | 789.00 | 429.87 | 2023-05-27 | true |
| 104 | Upland Oak Bookcase | Furniture | Storage | 429.00 | 214.70 | 2022-12-20 | true |
| 105 | Avery Leather Bed Frame | Furniture | Beds | 449.00 | 232.70 | 2021-08-31 | true |
returns1,673 rows · 5 columns
return_idintegerorder_item_idintegerreturned_ondatereasonvarcharrefund_amountdecimal
| return_id | order_item_id | returned_on | reason | refund_amount |
|---|---|---|---|---|
| 1 | 14 | 2023-01-10 | damaged | 83.69 |
| 2 | 42 | 2023-01-10 | wrong_item | 19.99 |
| 3 | 105 | 2023-01-14 | wrong_item | 10.99 |
| 4 | 24 | 2023-01-16 | changed_mind | 26.99 |
| 5 | 126 | 2023-01-16 | late | 20.99 |
employees60 rows · 8 columns
employee_idintegerfirst_namevarcharlast_namevarchardepartmentvarchartitlevarcharmanager_idintegerhire_datedatesalaryinteger
| employee_id | first_name | last_name | department | title | manager_id | hire_date | salary |
|---|---|---|---|---|---|---|---|
| 1 | Catherine | Hale | Executive | Chief Executive Officer | NaN | 2016-03-01 | 285000 |
| 2 | Robert | Kim | Executive | Chief Operating Officer | 1 | 2017-01-09 | 235000 |
| 3 | Megan | Collins | Sales | Head of Sales | 1 | 2018-04-16 | 172000 |
| 4 | Chloe | Gutierrez | Sales | Account Executive | 3 | 2020-01-21 | 72000 |
| 5 | Frank | Bell | Sales | Account Executive | 3 | 2023-05-21 | 73000 |
web_sessions40,000 rows · 7 columns
session_idintegercustomer_idintegerstarted_attimestampdevicevarcharsourcevarcharpages_viewedintegerconvertedboolean
| session_id | customer_id | started_at | device | source | pages_viewed | converted |
|---|---|---|---|---|---|---|
| 1 | NaN | 2023-01-01 23:02:24 | mobile | paid | 1 | false |
| 2 | 18 | 2023-01-01 15:07:49 | desktop | paid | 1 | false |
| 3 | NaN | 2023-01-01 16:55:46 | desktop | direct | 7 | false |
| 4 | NaN | 2023-01-01 12:43:28 | mobile | organic | 3 | false |
| 5 | NaN | 2023-01-01 13:06:21 | mobile | social | 2 | false |
marketing_spend3,276 rows · 3 columns
spend_datedatechannelvarcharamountdecimal
| spend_date | channel | amount |
|---|---|---|
| 2023-01-01 | paid | 492.99 |
| 2023-01-01 | 37.38 | |
| 2023-01-01 | social | 200.53 |
| 2023-01-02 | paid | 709.13 |
| 2023-01-02 | 64.13 |
The lesson
One table, four views of time
Every step starts from lines: one row per order line of a completed order, with its
line_revenue and the order’s order_date. From there, time can be cut in two ways. Resampling
puts rows into consecutive periods on a timeline (2 December 2025, the week ending on it, November
2025). Date parts pull one piece out of each date (its weekday, its month of the year, its
quarter) so rows from different years can be grouped together. The boss needs both.
Date parts with .dt
The .dt accessor extracts parts of a datetime column:
d = lines["order_date"]
d.dt.year # 2025
d.dt.quarter # 1 to 4
d.dt.month # 1 to 12
d.dt.month_name() # 'November'
d.dt.day_name() # 'Friday'
year, quarter and month are attributes (no brackets) and return numbers; month_name() and
day_name() are methods and return text. For any other label, strftime formats a date with
codes: "%Y-%m" gives '2025-11', "%b" gives 'Nov', "%B" the full name, "%a" a short
weekday. On a DatetimeIndex the same methods are available directly, without .dt:
table.index.strftime("%b").
Months in calendar order
Month names are text, and text sorts alphabetically: April, August, December… To keep calendar order, carry the month number next to the name and sort by the number:
parts = lines.assign(month_num=lines["order_date"].dt.month, month=lines["order_date"].dt.month_name())
parts.groupby(["month_num", "month"], as_index=False)["line_revenue"].sum().sort_values("month_num")
Grouping by both keys costs nothing (each number has one name) and gives you a column to sort on.
Another option is an ordered categorical, pd.Categorical(names, categories=[...], ordered=True),
but a month number is simpler and survives every merge.
Averaging a month over three years needs two steps: first sum per year and month, then average those totals. Averaging lines directly would give the average order line, not the average month.
Rolling windows and growth, history first
The best week of 2025 is a rolling 7-day sum over a daily series, built on the full history and filtered to 2025 afterwards. It ends on Thursday 4 December 2025 at $104,267.39, when the window holds the whole Black Friday to Cyber Monday run. In 2024 the best window was bigger, $117,208.44.
The same rule applies to year-over-year growth: pct_change(12) needs the 2024 months in the series,
so select Q4 2025 only at the end. The answer is uncomfortable: November 2025 revenue fell 8.4% on
November 2024 although order counts grew 13.7%, the kind of result you report plainly, with the
order count next to it.
Shares of each year
transform("sum") returns the group total on every row of the group, so a quarter’s share of its year
is one division. Q4 brought 0.375 of 2023 revenue, 0.381 of 2024 and 0.328 of 2025.
Common mistakes
- Sorting month names alphabetically instead of by month number.
- Writing
dt.month_namewithout brackets, which gives a method, not names. - Filtering to 2025 before
rollingorpct_change(12), which turns the start of the period into NaN. - Dividing by the grand total of all years instead of each year’s own total.
Lantern lit!
Practised: rollingday namemonth namecalendar orderstrftimequarteryear over year
- Solved
- No hints
- First run
