Level 4 · Smooth the daily revenue chart

The request

From Laura Bennett, Head of Finance

“The daily revenue chart on our dashboard jumps all over the place, so nobody can tell when the holiday rush really started or peaked. Can you smooth it into a 7-day average and tell me the busiest single day and the busiest week of 2025?”

Your goal

Build daily revenue for 2025 from completed orders (line revenue = quantity × unit_price × (1 − discount_pct / 100)), with one value per calendar day. Then take a 7-day rolling mean. Set result to a dict: "busiest_day", the date with the highest daily revenue, and "busiest_week_end", the date where the 7-day mean is highest, both as "YYYY-MM-DD" text, and "max_7day", that highest 7-day mean, rounded to 2.

  • Use the orders DataFramenot done yet
  • Use the order_items DataFramenot done yet
  • Store a dict in resultnot done yet
Daily revenue, October to December 2025
Daily revenue, October to December 2025$0$5k$10k$15k$20k$25krevenueOct 01Oct 12Oct 23Nov 03Nov 14Nov 25Dec 06Dec 17Dec 28
Daily revenue, October to December 2025$0$5k$10k$15k$20k$25krevenueOct 01Oct 23Nov 14Dec 06Dec 28

One point per day. Single big orders make spikes that say little about the trend.

Show the numbers
revenue
Oct 01$7,274
Oct 02$2,112
Oct 03$4,680
Oct 04$2,781
Oct 05$3,477
Oct 06$2,683
Oct 07$3,333
Oct 08$6,183
Oct 09$9,080
Oct 10$13,463
Oct 11$2,566
Oct 12$4,713
Oct 13$6,800
Oct 14$9,910
Oct 15$6,526
Oct 16$3,814
Oct 17$8,680
Oct 18$7,353
Oct 19$1,924
Oct 20$2,019
Oct 21$6,832
Oct 22$3,267
Oct 23$1,675
Oct 24$6,266
Oct 25$12,136
Oct 26$6,223
Oct 27$6,582
Oct 28$5,525
Oct 29$5,992
Oct 30$3,587
Oct 31$7,148
Nov 01$13,803
Nov 02$9,265
Nov 03$8,846
Nov 04$4,958
Nov 05$6,880
Nov 06$6,287
Nov 07$6,646
Nov 08$5,912
Nov 09$7,314
Nov 10$13,755
Nov 11$6,441
Nov 12$10,065
Nov 13$5,885
Nov 14$5,975
Nov 15$16,267
Nov 16$8,817
Nov 17$10,937
Nov 18$15,139
Nov 19$6,540
Nov 20$12,074
Nov 21$10,664
Nov 22$6,574
Nov 23$14,407
Nov 24$5,660
Nov 25$4,173
Nov 26$4,801
Nov 27$11,165
Nov 28$15,976
Nov 29$19,382
Nov 30$17,757
Dec 01$23,299
Dec 02$7,703
Dec 03$6,354
Dec 04$13,797
Dec 05$8,518
Dec 06$13,459
Dec 07$15,360
Dec 08$11,234
Dec 09$3,319
Dec 10$9,526
Dec 11$11,364
Dec 12$9,434
Dec 13$22,028
Dec 14$8,486
Dec 15$7,621
Dec 16$16,117
Dec 17$13,730
Dec 18$6,980
Dec 19$6,694
Dec 20$14,178
Dec 21$9,817
Dec 22$6,037
Dec 23$5,782
Dec 24$5,647
Dec 25$1,597
Dec 26$6,528
Dec 27$5,232
Dec 28$6,396
Dec 29$1,635
Dec 30$3,416
Dec 31$7,059

Your code

Python

Loading the editor…

    ResultPreview of orders · press Run to see yours
    Preview of orders
    order_idcustomer_idorder_datestatuschannel
    10011252023-01-01 20:51:19completedweb
    10021562023-01-02 07:58:46completedweb
    1003222023-01-02 10:25:27completedmarketplace
    1004482023-01-02 11:21:51completedmarketplace

    Mote checks your result

    Press Run. Each row that matches the goal becomes a firefly in your lantern.

    DataFrames

    Loaded for you as pandas DataFrames, plus pd and np.

    orders12,000 rows · 7 columns
    • order_idinteger
    • customer_idinteger
    • order_datetimestamp
    • statusvarchar
    • channelvarchar
    • shipping_feedecimal
    • coupon_codevarchar
    First 5 rows of orders
    order_idcustomer_idorder_datestatuschannelshipping_feecoupon_code
    10011252023-01-01 20:51:19completedweb0.00NaN
    10021562023-01-02 07:58:46completedweb0.00NaN
    1003222023-01-02 10:25:27completedmarketplace4.99WELCOME10
    1004482023-01-02 11:21:51completedmarketplace4.99WELCOME10
    1005822023-01-02 11:43:36completedweb0.00WELCOME10
    order_items30,338 rows · 6 columns
    • order_item_idinteger
    • order_idinteger
    • product_idinteger
    • quantityinteger
    • unit_pricedecimal
    • discount_pctdecimal
    First 5 rows of order_items
    order_item_idorder_idproduct_idquantityunit_pricediscount_pct
    11001121931.990.00
    21001154311.990.00
    31002154411.990.00
    410021941149.000.00
    51002193219.990.00
    customers2,018 rows · 9 columns
    • customer_idinteger
    • first_namevarchar
    • last_namevarchar
    • emailvarchar
    • cityvarchar
    • statevarchar
    • signup_datedate
    • segmentvarchar
    • referral_sourcevarchar
    First 5 rows of customers
    customer_idfirst_namelast_nameemailcitystatesignup_datesegmentreferral_source
    1OwenHudsonowen.hudson@icloud.comSan FranciscoCA2022-06-02consumersearch
    2DouglasMendozadmendoza69@gmail.comAtlantaGA2022-06-02consumerNaN
    3HenryBrookshbrooks@lakesidecafe.comLos AngelesCA2022-06-04businessNaN
    4BrianGrahambgraham@bluecedarhomes.comSeattleWA2022-06-06businesspinterest
    5KyleJimenezkjimenez27@yahoo.comIndianapolisIN2022-06-07consumerNaN
    products120 rows · 8 columns
    • product_idinteger
    • namevarchar
    • categoryvarchar
    • subcategoryvarchar
    • unit_pricedecimal
    • unit_costdecimal
    • launched_ondate
    • is_activeboolean
    First 5 rows of products
    product_idnamecategorysubcategoryunit_priceunit_costlaunched_onis_active
    101Vale Velvet LoveseatFurnitureSofas1659.00NaN2023-09-11true
    102Alder Oak Coffee TableFurnitureTables919.00503.502024-05-16true
    103Orchard Rattan Dining ChairFurnitureChairs789.00429.872023-05-27true
    104Upland Oak BookcaseFurnitureStorage429.00214.702022-12-20true
    105Avery Leather Bed FrameFurnitureBeds449.00232.702021-08-31true
    returns1,673 rows · 5 columns
    • return_idinteger
    • order_item_idinteger
    • returned_ondate
    • reasonvarchar
    • refund_amountdecimal
    First 5 rows of returns
    return_idorder_item_idreturned_onreasonrefund_amount
    1142023-01-10damaged83.69
    2422023-01-10wrong_item19.99
    31052023-01-14wrong_item10.99
    4242023-01-16changed_mind26.99
    51262023-01-16late20.99
    employees60 rows · 8 columns
    • employee_idinteger
    • first_namevarchar
    • last_namevarchar
    • departmentvarchar
    • titlevarchar
    • manager_idinteger
    • hire_datedate
    • salaryinteger
    First 5 rows of employees
    employee_idfirst_namelast_namedepartmenttitlemanager_idhire_datesalary
    1CatherineHaleExecutiveChief Executive OfficerNaN2016-03-01285000
    2RobertKimExecutiveChief Operating Officer12017-01-09235000
    3MeganCollinsSalesHead of Sales12018-04-16172000
    4ChloeGutierrezSalesAccount Executive32020-01-2172000
    5FrankBellSalesAccount Executive32023-05-2173000
    web_sessions40,000 rows · 7 columns
    • session_idinteger
    • customer_idinteger
    • started_attimestamp
    • devicevarchar
    • sourcevarchar
    • pages_viewedinteger
    • convertedboolean
    First 5 rows of web_sessions
    session_idcustomer_idstarted_atdevicesourcepages_viewedconverted
    1NaN2023-01-01 23:02:24mobilepaid1false
    2182023-01-01 15:07:49desktoppaid1false
    3NaN2023-01-01 16:55:46desktopdirect7false
    4NaN2023-01-01 12:43:28mobileorganic3false
    5NaN2023-01-01 13:06:21mobilesocial2false
    marketing_spend3,276 rows · 3 columns
    • spend_datedate
    • channelvarchar
    • amountdecimal
    First 5 rows of marketing_spend
    spend_datechannelamount
    2023-01-01paid492.99
    2023-01-01email37.38
    2023-01-01social200.53
    2023-01-02paid709.13
    2023-01-02email64.13

    The lesson

    Why daily charts are noisy

    A day’s revenue depends on a few dozen orders. One business customer buying a sofa set can double a Tuesday. Weekdays and weekends differ too. Plot every day and the line zigzags so much that the real movement, the slow build of the holiday season, hides behind single spikes. Readers then point at the tallest spike and tell a story about it.

    From orders to days

    order_date is a timestamp with a time of day, so grouping by it gives one group per order time. resample cuts a datetime index into calendar periods and fills days without orders with zero:

    daily = lines.set_index("order_date").resample("D")["revenue"].sum()
    len(daily)   # 365: one value per day of 2025

    "D" is days, "W" weeks, "MS" months starting on the 1st.

    A 7-day rolling mean

    rolling(7) slides a window of seven rows along the Series. .mean() averages each window and labels it with its last day:

    smooth = daily.rolling(7).mean()
    smooth.head(7)   # six NaN, then the mean of Jan 1 to Jan 7

    The first six values are missing because there aren’t seven days yet. Seven is the usual window for daily business data: every window holds each weekday exactly once, so the weekly rhythm cancels out and what’s left is the trend.

    Reading the smoothed line

    The smoothed value on December 4 is the average of November 28 to December 4: the week after Thanksgiving, with Black Friday, Cyber Monday and their BFCM25 coupon orders inside it. That’s the busiest week of 2025. The single busiest day, December 1, is part of it, but a single day is a weaker signal. Notice that the smoothed line lags: it peaks a few days after the rush starts, because each point looks back over a week.

    Common mistakes

    • Rolling over rows that aren’t days. If days with no orders are missing, rolling(7) spans more than a week. Resample first so there is exactly one row per day.
    • Centring by accident, or not at all. rolling(7, center=True) labels each window with its middle day, which removes the lag but uses future days. Say which one your chart shows.
    • Smoothing away the thing you were asked about. If the question is “what happened on Black Friday?”, show the daily bars. Smoothing answers questions about trends.

    Pick what may run on this site. You can change this any time from "Cookie settings" at the bottom of every page.