1. Lighthouse

Level 9 · Running totals and spread with NumPy

The request

From Laura Bennett, Head of Finance

“For the November pack: the running total of orders over those two weeks, a typical day (mean and median), how much days vary, and how first-half 2025 order volume grew month on month, compounded from January.”

Your goal

Set result to a dictionary with keys 'total' (sum of orders, an int), 'running' (the running total, np.cumsum, as a list of 14 ints), 'mean' and 'median' (of orders, rounded to 2), 'std' and 'var' (np.std and np.var with their default settings, rounded to 2), and 'growth_index' (the month-on-month growth factors of monthly (each month divided by the month before), compounded with np.cumprod, rounded to 3, as a list of 5 floats).

  • Store a dict in resultnot done yet

Your code

Python

Loading the editor…

    ResultPress Run to see yours

    Mote checks your result

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

    The lesson

    Totals and running totals

    sum gives one number; cumsum gives the total so far at every position:

    orders.sum()        # 398
    np.cumsum(orders)   # [23, 47, 64, 86, ..., 289, 340, 398]

    The running total is what a “month to date” line on a dashboard shows. Its last value always equals the plain sum, which is a quick check. Reading it is easy too: after the first week (position 7) the store was at 152 orders; Black Friday weekend alone then added 165.

    Mean, median and spread

    np.mean(orders)     # 28.43
    np.median(orders)   # 22.5
    np.std(orders)      # 14.23
    np.var(orders)      # 202.39

    The mean adds everything and divides by 14. The median is the middle value once sorted (with an even count, the average of the two middle ones). Here they disagree a lot: three huge Black Friday days drag the mean up to 28.43, while the median, 22.5, still describes a normal day. When a few extreme values sit in the data, report the median as the “typical” value.

    The variance is the average squared distance from the mean, and the standard deviation is its square root, in the same units as the data: daily orders typically sit about 14 away from the mean. NumPy divides by n by default. pandas divides by n - 1 (the sample version), so pd.Series(orders).std() gives 14.76. Pass ddof=1 to NumPy if you need to match.

    Compounding growth with cumprod

    cumprod multiplies as it goes. That makes it the tool for compounding growth rates:

    monthly = np.array([296, 235, 326, 318, 352, 352])
    growth = monthly[1:] / monthly[:-1]   # [0.794, 1.387, 0.975, 1.107, 1.0]
    np.cumprod(growth)                    # [0.794, 1.101, 1.074, 1.189, 1.189]

    February was down 21% on January, March up 39% on February. Compounded, March sits 10% above January and June 19% above it, exactly 352 / 296. Adding the percentage changes instead of multiplying them would give the wrong answer.

    Common mistakes

    • Comparing np.std with a pandas .std() and thinking one is broken. They use different divisors.
    • Adding growth rates instead of multiplying factors.
    • Reporting the mean as “a typical day” when a few big days skew it.
    • Dividing the wrong way round (monthly[:-1] / monthly[1:]), which turns growth into decline.

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