The request

From Laura Bennett, Head of Finance
Your goal
Step 1 of 3. Set result to a dict with "sd", the standard deviation of completed["order_value"] (pandas .std()), and "se", the standard error of the mean for a sample of 100 orders (sd divided by the square root of 100), both rounded to 2 decimals. Compute se from the unrounded sd.
- Use the
ordersDataFramenot done yet - Use the
order_itemsDataFramenot done yet - Store a dict in
resultnot done yet
Step 2 of 3. Simulate the sampling distribution. With the rng and values in the starter, draw 1,000 samples of 100 orders each with rng.choice(values, size=100, replace=False) (in a loop, in that order) and take each sample's mean. Set result to a dict with "mean_of_means" and "sd_of_means" (pandas .mean() and .std() of the 1,000 means), rounded to 2 decimals.
- Use the
ordersDataFramenot done yet - Use the
order_itemsDataFramenot done yet - Store a dict in
resultnot done yet
Step 3 of 3. How many orders does a sample need so its mean is within ±$25 of the population mean, 95% of the time? Use margin = 1.96 × sd / √n with sd = completed["order_value"].std(), solve for n, and set result to n rounded up to a whole number (an int).
- Use the
ordersDataFramenot done yet - Use the
order_itemsDataFramenot done yet - Store a number in
resultnot 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.
What Laura 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
The standard error
Last level, one sample of 200 orders came out $28 below the true mean, and other samples landed anywhere from $385 to $581. The standard error (SE) puts one number on that wobble: it is the standard deviation of the sample mean across all the samples you could draw.
sd = completed["order_value"].std() # 795.75, spread of single orders
se = sd / math.sqrt(100) # 79.57, spread of 100-order averages
The formula is SE = sd / √n. Two things set it: how spread out the values are (sd) and how many you sample (n). It falls with the square root of n, so four times the sample only halves the error.
Checking it by simulation
A formula is easier to trust once you have watched it work. With the whole population in memory you can draw many samples and look at their means directly:
rng = np.random.default_rng(42)
values = completed["order_value"].to_numpy()
means = pd.Series([rng.choice(values, size=100, replace=False).mean() for _ in range(1000)])
means.std() # close to the formula's 79.57
The list of 1,000 means is the sampling distribution of the mean. Its centre sits on the true mean, and its spread is the standard error. It is also far more bell-shaped than the order values themselves (skew 0.38 against 3.95): averages of many values smooth out the tail. That is the central limit theorem at work, and it is why a 1.96 multiplier works for means even when the data is skewed.
From standard error to margin of error
About 95% of sample means fall within 1.96 standard errors of the true mean, so the 95% margin of error of a sample mean is 1.96 × SE. For 100 orders that is 1.96 × 79.57 ≈ ±$156. Flip the formula around to plan a sample:
n = math.ceil((1.96 * sd / 25) ** 2) # orders needed for ±$25
Round up: a sample of 3,892.1 orders doesn’t exist, and rounding down would miss the target.
What the standard error doesn’t cover
The standard error measures random sampling error only. A sample picked the wrong way (last level’s email visitors) has a small standard error and a big bias. Use it with random samples.
Common mistakes
- Confusing sd and SE. The sd describes single values and does not shrink with more data; the SE describes an average and does.
- Expecting linear gains. Doubling the sample cuts the error by only 29%.
- Mixing
ddof. pandas.std()divides by n − 1, numpy’snp.stdby n. Pick one and say so; here everything uses pandas. - Rounding n down. Sample sizes always round up.
Lantern lit!
Practised: standard errorsampling distributionsimulationsample sizemargin of error
- Solved
- No hints
- First run
