Control Flow and Functions for Data Work
Control Flow and Functions for Data Work
Five quiz scores just came in: [72, 85, 90, 68, 95]. The pass mark is 70.
Staff need three answers before the meeting: who passed, what letter each
student earns, and what the class average actually says. This lesson answers
all three with three tools — a branch, a loop, and a mask. Every number below
comes from real code you can run on this page.
The data picture: five slots, five yes/no answers
import numpy as np
grades = np.array([72.0, 85.0, 90.0, 68.0, 95.0])
One array, five slots. The question "who passed?" has five answers — one per slot. Hold that shape in mind: whatever filters these grades must also come back in fives.
The teal dots passed. The open dot is 68, the fail. The line is the pass mark, 70. The mean, 82, sits above that line and still hides the fail.
Branches ask one question at a time
def letter(score):
if score >= 90:
return 'A'
elif score >= 80:
return 'B'
elif score >= 70:
return 'C'
else:
return 'F'
letter(85.0)
# → 'B'
Order matters: 95.0 is also >= 80 and >= 70, but the first true branch
wins, so it returns 'A'. Read the chain top-down as "the best letter this
score qualifies for."
Loops repeat the question over every slot
letters = [letter(float(g)) for g in grades]
letters
# → ['C', 'B', 'A', 'F', 'A']
The loop invariant, in words: after each step, letters holds the letter for
every score seen so far. Say it before you trust a loop — if you cannot state
it, the loop is not finished.
Masks answer all five slots at once
passed = grades[grades >= 70]
passed.tolist()
# → [72.0, 85.0, 90.0, 95.0]
grades >= 70 builds five True/False answers; the brackets keep only the True
slots. No loop, no branch, no counter to get wrong. Prefer a mask when the
question is the same for every slot; keep the loop when each slot needs its
own decision (like letters, where four different branches fire).
The average — and what it hides
round(float(np.mean(grades)), 1)
# → 82.0
Mean 82.0 looks like a solid B — and one student failed. An average compresses five stories into one number; always ask what it is hiding before you report it. Here it hides the F.
Run the starter above
Open the Python practice cell on this page and run the starter. Your output must read:
letters = ['C', 'B', 'A', 'F', 'A']
passed = [72.0, 85.0, 90.0, 95.0] count = 4
mean = 82.0
All L2 numbers match.
The assert lines check the real answers. If a number is wrong, the cell fails
out loud. Empty output is also a failure — the cell says so honestly.
Failure modes
= is not a question.
if score = 90: is a SyntaxError — assignment stores, == compares. Python
refuses to guess which you meant.
Masks need arrays.
[72.0, 85.0][[True, False]] raises TypeError: plain lists do not understand
boolean masks. Convert with np.array first — L1's object lesson, applied.
Never == a raw float mean.
Compare round(..., 1) == 82.0, not the unrounded float. Binary floating point
can hide dust past the decimal you cannot see.
Try this (in the practice cell)
- Home check — run the starter; confirm the four printed lines above.
- Raise the bar — change the pass mark to 80 and re-run:
grades[grades >= 80]→[85.0, 90.0, 95.0], count 3. The mask builds a new array and leavesgradesalone, sonp.mean(grades)stays 82.0. The passers' own mean is a different number: 85.5 at mark 70, 90.0 at mark 80. - Break it — try
[72.0, 85.0][[True, False]]and read theTypeError. Masks need arrays; wrong object fails, not silently succeeds.
Next: a loop builds one answer per slot, but a whole price table — every product against every fee — would need a loop inside a loop. Lesson 3 builds it with one expression.