Lab 06 — NumPy II: Masks, Aggregation, Random, Broadcasting¶
Session: Week 3 · Session 6 · 90 min CLO: CLO-1 Difficulty: Beginner
Learning objectives¶
By the end of this lab you can:
- Filter arrays with boolean masks and combine conditions with
&,|,~. - Aggregate with
min,max,mean,std,percentile. - Generate reproducible random data and sample from it.
- Use broadcasting rules to avoid explicit loops.
Problem statement¶
A weather station records hourly temperatures for a month (30 days × 24 hours). A single NumPy array must answer: which days froze (any hour below 0 °C), the warmest hour of the month, the 10th percentile temperature, and a simulated "sensor noise" addition — all without a single Python loop.
Dataset requirements¶
Synthetic, generated inline with np.random and a fixed seed.
Step-by-step tasks¶
- Generate:
np.random.seed(11); buildtemps = np.random.normal(loc=15, scale=8, size=(30, 24))and clip to a plausible range withnp.clip(temps, -5, 40). Print shape. - Mask: build
froze = temps.min(axis=1) < 0— a boolean per day. Printnp.sum(froze)(days that froze) and the indices withnp.argwhere(froze).ravel(). - Filter: print the actual minimum temperature(s) of any freezing day
using
temps[froze].min(). - Aggregate: report month
min,max,mean,std, and the 10th and 90th percentiles vianp.percentile. - Warmest hour: use
np.unravel_index(temps.argmax(), temps.shape)to get (day, hour). Explain whatunravel_indexdoes in a comment. - Broadcasting: normalize the array with
normalized = (temps - temps.mean(axis=0)) / temps.std(axis=0)— no loop, and both operands broadcast. Verifynormalized.mean()is ~0 andnormalized.std()is ~1 (allow rounding). - Noise simulation: add
np.random.normal(0, 0.5, temps.shape)noise to a copy — same shape, element-wise — and confirm the copy differs from the original by more than0somewhere.
Starter code¶
import numpy as np
np.random.seed(11)
temps = np.random.normal(loc=15, scale=8, size=(30, 24))
temps = np.clip(temps, -5, 40)
print("shape:", temps.shape)
# your code here for tasks 2-7
Expected output¶
shape: (30, 24)frozehas someTruevalues;np.sum(froze)printed.- A single scalar for the coldest freezing hour.
- Four aggregation scalars + two percentiles.
- A
(day, hour)tuple for the warmest hour. normalized.mean()≈ 0.0 andnormalized.std()≈ 1.0.- A
Truefor "noisy copy differs".
Questions¶
- Why does
temps[froze]flatten to 1-D? What shape did you expect? - When would you use
np.percentileinstead ofmean? - In step 6, what shapes are
temps.mean(axis=0)andtemps.std(axis=0)? How does broadcasting align them with(30, 24)? - Why is
&used instead ofandin NumPy masks? - What does
np.unravel_indexconvert, and why is it needed?
Challenge task¶
Find the longest streak of consecutive hours with temperature above
25 °C anywhere in the month (flatten the array first). Hint: build a
boolean array, then use np.diff on the indices where the condition is
True. Print the streak length and the (day, hour) where it starts.
Comment your approach.
Marking rubric¶
| Criteria | Max | Notes |
|---|---|---|
| Data generated + clipped + shape | 2 | (30, 24) |
| Freeze mask + counts + indices | 3 | boolean logic correct |
| Filtered min on freezing days | 2 | uses mask |
| Aggregations + percentiles | 3 | values match seed |
unravel_index used & explained |
3 | correct (day, hour) |
| Broadcasting normalization | 3 | mean≈0, std≈1 |
| Noise addition on a copy | 2 | differs from original |
| Answers to questions | 2 | Q1, Q4 correct |
| Challenge: longest streak | 4 | correct length + start |
| Total | 24 |