</>SyntaxPilot
Notes › Data Analytics with Python › Episode 6

Python for Data 3: if/elif/else, for and while Loops, and Comprehensions

▶ Watch the videoTry the code in the compilerAll Data Analytics with Python notes

These are the notes for Data Analytics with Python, episode 6. Until now every program ran each line once, top to bottom. Here programs learn to decide (if, elif, else) and to repeat (for and while loops, range, enumerate, zip, break, continue), to loop over dictionaries, and to build whole lists and dicts in one line with comprehensions and the conditional expression. Every term is defined the first time it appears and collected in the Glossary. Every program below was run for real (Python 3.13); outputs are unedited.

Making decisions: if

Ramesh's target is 300 cups. He wants a warning on slow days and silence on good days. A program that runs every line every time can't do that: it needs to choose.

A condition is any expression that gives True or False (like cups < 300). The if statement runs a block of code only when its condition is True. A block is a group of lines indented (pushed right, by convention 4 spaces) under a line that ends with a colon.

cups = 280
if cups < 300:
    print("Slow day: try a samosa offer")
print("Report done")
Slow day: try a samosa offer
Report done

With cups = 410 the output is only Report done: the condition is False, so the indented block is skipped. The last line isn't indented, so it always runs.

Indentation is grammar in Python, not decoration. Two classic errors:

cups = 280
if cups < 300
    print("Slow day")
  File "main.py", line 2
    if cups < 300
                 ^
SyntaxError: expected ':'
cups = 280
if cups < 300:
print("Slow day")
  File "main.py", line 3
    print("Slow day")
    ^^^^^
IndentationError: expected an indented block after 'if' statement on line 2

if, elif, else

Python checks top to bottom and runs exactly one block: the first one whose condition is True. Order conditions from strictest to loosest.

cups = 450
if cups >= 400:
    label = "busy"
elif cups >= 300:
    label = "normal"
else:
    label = "slow"
print(cups, label)
450 busy

With cups = 300: 300 normal. If the >= 300 test came first, 450 would wrongly be labelled normal.

for loops

Copying an if block for 7 days (or 365) is unreadable and error-prone. A for loop runs the same block once for every item in a collection, like a ticket checker who has one instruction ("check the ticket") and repeats it for every passenger. Each run is an iteration; the loop variable (cups below) holds the current item.

week = [320, 280, 410, 300, 350, 390, 450]
for cups in week:
    if cups < 300:
        print(cups, "slow")
    else:
        print(cups, "ok")
print("Loop finished")
320 ok
280 slow
410 ok
300 ok
350 ok
390 ok
450 ok
Loop finished

The print lines are indented twice: they belong to the if/else, which belongs to the loop. Loop finished isn't indented, so it runs once, after the loop. A for loop works on any iterable: lists, tuples, strings (letter by letter), sets, dicts.

range

range(stop), range(start, stop) and range(start, stop, step) make a sequence of whole numbers, starting at start (default 0) and stopping before stop, like slices.

for n in range(5):
    print(n, end=" ")
print()
print(list(range(1, 8)))
print(list(range(0, 50, 10)))
0 1 2 3 4 
[1, 2, 3, 4, 5, 6, 7]
[0, 10, 20, 30, 40]

end=" " makes print end with a space instead of a new line; print() alone prints a new line. list(range(...)) turns the range into a list so you can see it.

enumerate and zip instead of index loops

Looping over positions with range(len(...)) invites off-by-one bugs:

week = [320, 280, 410]
for i in range(len(week) + 1):
    print(week[i])
320
280
410
Traceback (most recent call last):
  File "main.py", line 3, in <module>
    print(week[i])
          ~~~~^^^
IndexError: list index out of range

Cleaner tools:

days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
cups = [320, 280, 410, 300, 350, 390, 450]
for day, c in zip(days, cups):
    print(day, c)
for n, day in enumerate(days[:3], start=1):
    print(f"Day {n}: {day}")
Mon 320
Tue 280
Wed 410
Thu 300
Fri 350
Sat 390
Sun 450
Day 1: Mon
Day 2: Tue
Day 3: Wed

for day, c in ... unpacks each pair into two variables. zip stops at the shortest list.

Accumulators: totals inside a loop

An accumulator is a variable that collects a result across a loop (a running total or count). x += 5 is augmented assignment, shorthand for x = x + 5 (also -=, *=).

cups = [320, 280, 410, 300, 350, 390, 450]
weather = ["sunny", "rainy", "sunny", "rainy",
           "sunny", "sunny", "sunny"]
rain_total = 0
rain_days = 0
for c, w in zip(cups, weather):
    if w == "rainy":
        rain_total += c
        rain_days += 1
print("Rainy days:", rain_days)
print("Rainy average:", rain_total / rain_days)
Rainy days: 2
Rainy average: 290.0

while loops

"How many months to save Rs 25,000 at Rs 2,000 a month?" has no list to walk and an unknown number of rounds. A while loop repeats its block as long as its condition is True, checking the condition before every round.

savings = 0
months = 0
while savings < 25000:
    savings += 2000
    months += 1
print(f"{months} months, saved Rs {savings}")
13 months, saved Rs 26000

After 12 months he has 24,000 (still short), so one more month.

Infinite loop: if nothing inside the loop moves the condition towards False (say you forget savings += 2000), the loop never ends. Stop it with the notebook's stop button / "Interrupt kernel", or Ctrl+C in a terminal. Use for when you know what you're looping over; while when you only know when to stop.

break and continue

week = [320, 280, 410, 300, 350, 390, 450]
for day, c in enumerate(week, start=1):
    if c > 400:
        print(f"First 400+ day: day {day} ({c} cups)")
        break
log = [320, "N/A", 410, "N/A", 350]
total = 0
for c in log:
    if c == "N/A":
        continue
    total += c
print("Total of real days:", total)
First 400+ day: day 3 (410 cups)
Total of real days: 1080

Without continue, total += "N/A" would raise TypeError: unsupported operand type(s) for +=: 'int' and 'str'. Skipping unusable values is a first taste of data cleaning.

Looping over a dict

A plain for over a dict gives its keys. Use .items() to get key-value pairs and unpack them.

menu = {"chai": 12, "samosa": 15, "bun maska": 25}
for item in menu:
    print(item)
for item, price in menu.items():
    print(f"{item}: Rs {price}")
chai
samosa
bun maska
chai: Rs 12
samosa: Rs 15
bun maska: Rs 25

.values() loops over just the values.

List comprehensions

Building a filtered list takes four lines with a loop. A list comprehension does it in one:

cups = [320, 280, 410, 300, 350, 390, 450]
quiet = []
for c in cups:
    if c < 350:
        quiet.append(c)
print(quiet)
quiet = [c for c in cups if c < 350]
print(quiet)
in_hundreds = [c / 100 for c in cups]
print(in_hundreds)
[320, 280, 300]
[320, 280, 300]
[3.2, 2.8, 4.1, 3.0, 3.5, 3.9, 4.5]

Read [c for c in cups if c < 350] in three parts:

Part Meaning
c what to keep (any expression, e.g. c / 100)
for c in cups where the items come from
if c < 350 optional filter

Use a comprehension when it stays short and readable; use a normal loop for long logic. This filter idea becomes pandas filtering later in the course.

The conditional expression

A conditional expression (also called a ternary expression) picks one of two values in a single line: value_if_true if condition else value_if_false.

cups = [320, 280, 410, 300, 350, 390, 450]
c = 280
label = "slow" if c < 300 else "ok"
print(label)
labels = ["slow" if c < 300 else "ok" for c in cups]
print(labels)
slow
['ok', 'slow', 'ok', 'ok', 'ok', 'ok', 'ok']

Note the position: a filter if goes at the end of a comprehension (and drops items); a conditional expression goes at the front (and keeps every item, choosing its value).

Dict comprehensions

{key: value for ... in ...} builds a dict.

days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
cups = [320, 280, 410, 300, 350, 390, 450]
by_day = {d: c for d, c in zip(days, cups)}
print(by_day)
menu = {"chai": 12, "samosa": 15, "bun maska": 25}
new_menu = {item: p + 2 for item, p in menu.items()}
print(new_menu)
busy = {d: c for d, c in by_day.items() if c >= 400}
print(busy)
{'Mon': 320, 'Tue': 280, 'Wed': 410, 'Thu': 300, 'Fri': 350, 'Sat': 390, 'Sun': 450}
{'chai': 14, 'samosa': 17, 'bun maska': 27}
{'Wed': 410, 'Sun': 450}

(dict(zip(days, cups)) also builds the first dict.)

Mini project: a weekly report

days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
cups = [320, 280, 410, 300, 350, 390, 450]
weather = ["sunny", "rainy", "sunny", "rainy",
           "sunny", "sunny", "sunny"]
for day, c, w in zip(days, cups, weather):
    if c >= 400:
        tag = "BUSY"
    elif c >= 300:
        tag = "normal"
    else:
        tag = "slow"
    print(f"{day}  {c:>3}  {w:<5}  {tag}")
rainy = [c for c, w in zip(cups, weather) if w == "rainy"]
sunny = [c for c, w in zip(cups, weather) if w == "sunny"]
print(f"Rainy avg {sum(rainy) / len(rainy):.0f}, sunny avg {sum(sunny) / len(sunny):.0f}")
Mon  320  sunny  normal
Tue  280  rainy  slow
Wed  410  sunny  BUSY
Thu  300  rainy  normal
Fri  350  sunny  normal
Sat  390  sunny  normal
Sun  450  sunny  BUSY
Rainy avg 290, sunny avg 384

In the f-string, :>3 right-aligns in 3 characters and :<5 left-aligns in 5, so the columns line up. Rainy days average 290 cups vs 384 on sunny days, about 94 fewer, based on one week (a real analysis would use a full year).

Common mistakes

Code Real error Why Fix
if cups < 300 SyntaxError: expected ':' if/elif/else/for/while lines end with : if cups < 300:
Block not indented IndentationError: expected an indented block after 'if' statement on line 2 Indentation defines the block Indent 4 spaces
for i in range(len(week) + 1) IndexError: list index out of range Loop runs past the end Loop over items, or use enumerate/zip
while savings < 25000: with no update (no error: never ends) The condition never becomes False Update the variable inside the loop
total += "N/A" TypeError: unsupported operand type(s) for +=: 'int' and 'str' Text in a number list continue past it, or clean first
Conditions in the wrong order (no error: wrong label) The first True branch wins Strictest condition first
x = 5 in an if instead of == SyntaxError: invalid syntax. Maybe you meant '==' or ':=' instead of '='? = assigns, == compares if x == 5:

Practice

1. Label each value in screen = [3.5, 5.2, 4.1, 2.0, 6.3] (hours) as "high" if over 4, else "ok", with a loop and with a comprehension.

Solution
labels = []
for h in screen:
    if h > 4:
        labels.append("high")
    else:
        labels.append("ok")
labels2 = ["high" if h > 4 else "ok" for h in screen]

Both give ['ok', 'high', 'high', 'ok', 'high'].

2. With while, how many years until Rs 10,000 doubles at 8% a year?

Solution
money, years = 10000, 0
while money < 20000:
    money *= 1.08
    years += 1
print(years)

Prints 10 (after 9 years it is about 19,990; after 10, about 21,589).

3. Sum log = [320, "N/A", 410, "N/A", 350] two ways.

Solution

Loop with continue (shown above), or sum([c for c in log if c != "N/A"]). Both give 1080.

4. Print the first day (name) with fewer than 300 cups using zip and break.

Solution
for day, c in zip(days, cups):
    if c < 300:
        print(day)
        break

Prints Tue.

5. Build {item: price} only for menu items cheaper than Rs 20.

Solution

{k: v for k, v in menu.items() if v < 20} → {'chai': 12, 'samosa': 15}.

Quick quiz

cups = [320, 280, 410, 300]
print([c for c in cups if c >= 300 and c < 400])

A. [320, 410, 300] B. [320, 300] C. [280] D. [320, 280, 300]

Answer

B. 320 and 300 pass both tests; 280 is under 300 and 410 is not under 400.

Interview questions

  1. for vs while? for iterates over a known collection; while repeats until a condition becomes False (unknown number of rounds).
  2. break vs continue? break exits the loop; continue skips to the next iteration.
  3. Why use enumerate and zip? They avoid manual index handling and off-by-one bugs, and read more clearly.
  4. What is a list comprehension? A one-line way to build a list: [expr for item in iterable if condition].
  5. Filter if vs conditional expression in a comprehension? A trailing if drops items; a if cond else b at the front keeps every item and chooses its value.
  6. How does Python know which lines belong to an if? Indentation: the block is the indented lines after the colon.

Glossary

Term Meaning Example
Condition An expression that is True or False cups < 300
if statement Runs a block only if its condition is True if cups < 300:
elif "else if": the next condition, tested only if the ones above were False elif cups >= 300:
else Runs when no condition above was True else:
Block Indented lines that belong together under a : line the body of an if
Indentation Spaces at the start of a line (4 by convention) print(...)
IndentationError Error for missing or inconsistent indentation expected an indented block
Loop Code that repeats a block for, while
for loop Repeats a block once per item of an iterable for c in cups:
Iteration One run of a loop's block 7 iterations for a week
Loop variable Holds the current item in a for loop c
Iterable Anything a for loop can go through list, str, dict, range
range() A sequence of whole numbers (stop excluded) range(1, 8)
end= print setting for what goes at the end of the line print(n, end=" ")
enumerate() Gives a counter with each item enumerate(days, start=1)
zip() Walks several lists side by side zip(days, cups)
Unpacking Splitting a pair/tuple into separate variables for day, c in ...
Accumulator A variable that collects a result across a loop rain_total
Augmented assignment x += y means x = x + y total += c
while loop Repeats while its condition is True while savings < 25000:
Infinite loop A loop whose condition never becomes False forgetting savings += 2000
break Leaves the loop immediately stop at the first match
continue Skips to the next iteration skip "N/A"
.items() A dict's key-value pairs, for looping for k, v in menu.items():
List comprehension One-line list builder [c for c in cups if c < 350]
Dict comprehension One-line dict builder {d: c for d, c in zip(days, cups)}
Conditional expression a if condition else b in one line "slow" if c < 300 else "ok"
Format alignment :>3 right-align, :<5 left-align in an f-string f"{c:>3}"

Summary

← Episode 5Python for Data 2: Lists, Tuples, Dictionaries and Sets