Course outline · 0% complete

0/27 lessons0%

Course overview →

Filtering with if, transforming with if/else

lesson 1-2 · ~11 min · 2/27

Keeping only some items

Half of real data work is filtering: keep the log lines that mention an error, the rows with a valid email, the scores above the cutoff. Python folds that job directly into comprehension syntax, so you should know both where the if goes and what it does there.

In lesson 1-1 every input item produced one output item. Often you also want to skip items. In Python for Beginners you did that with an if inside the loop:

evens = []
for n in nums:
    if n % 2 == 0:
        evens.append(n)

A comprehension does the same with an if at the end:

evens = [n for n in nums if n % 2 == 0]

Items that fail the test are simply left out of the new list. This trailing if is called the filter.

Filtering out the failing scores

The filter keeps only scores of 60 or higher. Everything below the cutoff never reaches the new list at all, so the result is shorter than the input.

scores = [45, 82, 60, 33, 91, 77]

passing = [s for s in scores if s >= 60]
print(passing)
print(len(passing), "passed out of", len(scores))

Output

[82, 60, 91, 77]
4 passed out of 6

Because passing is a real list, len(passing) gives you the count for free. Filtering and counting in two short lines like this is a very common reporting pattern.

if/else goes at the front, not the end

There is a second, different if you can use: the conditional expression A if test else B. It does not skip items, it chooses a value for every item. Because it is part of the value expression, it goes at the front:

labels = ["even" if n % 2 == 0 else "odd" for n in nums]

Compare the two shapes:

ShapeWhereEffect
[x for x in xs if test]endkeeps fewer items
[a if test else b for x in xs]frontsame count, values chosen

Mixing them up is the most common comprehension error, so pause on that table until it clicks.

Cleaning and filtering in one pass

This comprehension uses both forms at once. The trailing if drops entries that are empty once stripped, and the expression up front normalizes the survivors. Cleaning and keeping together like this is everyday data work.

raw = ["  alice ", "", "BOB", "  ", "carol"]

names = [s.strip().lower() for s in raw if s.strip()]
print(names)

Output

['alice', 'bob', 'carol']

The filter leans on a Python habit worth internalizing: an empty string is falsy, so if s.strip() reads as keep this only if something is left after trimming. The two entries that were nothing but spaces fail that test and disappear.

Labeling every score without dropping any

When every input item must produce an output item, the decision belongs at the front of the comprehension. Here each number becomes the string "pass" if it is 60 or more and "fail" otherwise, so four scores produce four labels.

scores = [45, 82, 60, 33]

labels = ["pass" if s >= 60 else "fail" for s in scores]
print(labels)

Output

['fail', 'pass', 'pass', 'fail']

The shape is [A if test else B for s in scores], where A is "pass", B is "fail", and the test is s >= 60. An if placed at the end instead would have thrown the low scores away and left only two labels, which is never what a labeling job wants. Labels have to line up one-to-one with the data they describe.

A trailing if can only shrink the result

[n for n in range(10) if n > 6] contains 3 items. range(10) yields 0 through 9, and the trailing if is a filter, so only 7, 8, and 9 survive.

That points at the general rule for the trailing form: it can remove items but it can never change the ones it keeps. The output is always the same length as the input or shorter, and every value in it appears unmodified from the source.