Students total a column, divide for an average and track the longest song with a biggest-so-far variable, then restrict the average to one genre, find the oldest song and write genre stats that cope with no matches.
Load these into Waypoints Prepare: choose Load URL and paste the copied address, or download the file and import it. In Prepare, the worksheet's Teacher edition shows the answers; the student copy hides them.
Predict and Run 10 min · Investigate 15 min · Modify 15 min · Make 10 min
Read the unit primer first. Two patterns carry the lesson. The accumulator: total = 0 before the loop, total = total + x inside it (the bug task replaces instead of adding). The biggest so far: start with the first row, and replace it only when a row beats it. float() turns "3.9" into a number (minutes have decimals; years use int()). An average of some rows needs its own count, since len(songs) counts every song; with no matching rows the count is 0 and dividing by it is an error, which the Make asks students to handle. round(x, 1) is used once for tidy output.
The biggest-so-far pattern. Ask students to fill in the trace by hand before running anything; resist writing the comparison for them.
| What you will see | A question to ask | The fix |
|---|---|---|
| The total is only the last song's minutes. | Does the line add to total, or replace it? | total = total + minutes. |
| ZeroDivisionError: division by zero. | What is the count when no song matches? | Check if count == 0 before dividing, and print a message instead. |
| TypeError: can only concatenate str (not "float") to str. | Is the number being joined onto text? | Wrap it in str() before joining with +. |
One possible solution for each task. Other correct programs are fine.
import csv
with open("music.csv") as f:
songs = list(csv.DictReader(f))
total = 0
count = 0
longest = songs[0]
for song in songs:
if song["genre"] == "rock":
minutes = float(song["minutes"])
total = total + minutes
count = count + 1
if float(song["minutes"]) > float(longest["minutes"]):
longest = song
print("Average rock song: " + str(round(total / count, 1)) + " minutes")
print("Longest: " + longest["title"] + " (" + longest["minutes"] + " minutes)")
import csv
with open("music.csv") as f:
songs = list(csv.DictReader(f))
total = 0
longest = songs[0]
oldest = songs[0]
for song in songs:
minutes = float(song["minutes"])
total = total + minutes
if minutes > float(longest["minutes"]):
longest = song
if int(song["year"]) < int(oldest["year"]):
oldest = song
print("Average length: " + str(round(total / len(songs), 1)) + " minutes")
print("Longest: " + longest["title"] + " (" + longest["minutes"] + " minutes)")
print("Oldest: " + oldest["title"] + " (" + oldest["year"] + ")")
import csv
with open("music.csv") as f:
songs = list(csv.DictReader(f))
genre = input("Which genre? ").strip().lower()
matches = []
for song in songs:
if song["genre"].lower() == genre:
matches.append(song)
if len(matches) == 0:
print("No songs in that genre.")
else:
total = 0
shortest = matches[0]
for song in matches:
minutes = float(song["minutes"])
total = total + minutes
if minutes < float(shortest["minutes"]):
shortest = song
print(str(len(matches)) + " songs, " + str(round(total / len(matches), 1)) + " minutes on average")
print("Shortest: " + shortest["title"] + " (" + shortest["minutes"] + " minutes)")
Stretch: Find the longest song of each decade (the 1980s, the 1990s...), using year // 10 * 10 for the decade.
Support: Give students the rock average with the count already added, and ask them to do the oldest song by copying the longest pattern.
Why does an average of the pop songs need its own count?
len(songs) counts every song; the average of some songs divides their total by how many of them there are, counted inside the if.
Next lesson counts every genre at once with a dictionary and draws a bar chart. In pandas (Lesson 5) these become one-word methods such as .mean() and .max().