Loading lesson…
Courses›Control Flow and Functions for Data Work

Control Flow and Functions for Data Work

DetailedOverviewQuizFlashcard

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.

Five scores, letters C B A F A, pass mark 70, mean 82

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)

  1. Home check — run the starter; confirm the four printed lines above.
  2. 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 leaves grades alone, so np.mean(grades) stays 82.0. The passers' own mean is a different number: 85.5 at mark 70, 90.0 at mark 80.
  3. Break it — try [72.0, 85.0][[True, False]] and read the TypeError. 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.