Students meet the Palmer Penguins data, guess species with hand-written if rules, measure accuracy on 342 penguins, look at the mistakes, tune the rules, and write rules of their own with a third measurement.
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 10 min · Modify 15 min · Make 10 min
Read the unit primer first. This course runs in the Python playground (real Python in the browser, Pyodide); later lessons load scikit-learn, which takes several seconds the first time, so open the site's Check your school page once on a student device at school. penguins.csv is the Palmer Penguins data (CC0): 342 penguins of three species with bill length and depth, flipper length, body mass, sex, island and year (2 penguins with no measurements are left out). The loader is the same csv.DictReader pattern as Data detective, with float() (the bug task forgets it). The rules reach 95% accuracy; the mistakes are mostly Adelies with long bills guessed as Chinstrap. The second Investigate question sets up the course: a person chose 206, and machine learning finds such things from data.
Using the mistakes to improve the rules. Ask students what the wrong guesses have in common before they change a number.
| What you will see | A question to ask | The fix |
|---|---|---|
| TypeError: '>' not supported between instances of 'str' and 'int'. | Were the measurements turned into numbers when they were read? | float(row["bill_length_mm"]). |
| KeyError: 'bill_depth_mm' in the Make. | Is it read from row, or from the penguin dictionary? | Read row["bill_depth_mm"] in the loader and store it, such as "depth": float(...). |
| The accuracy is above 100%. | Is correct counted once per penguin? | Add 1 only inside the if, once per penguin. |
One possible solution for each task. Other correct programs are fine.
import csv
penguins = []
with open("penguins.csv") as f:
for row in csv.DictReader(f):
penguins.append({"species": row["species"], "bill": float(row["bill_length_mm"]), "flipper": float(row["flipper_length_mm"])})
def guess(bill, flipper):
if flipper > 206:
return "Gentoo"
if bill > 44:
return "Chinstrap"
return "Adelie"
correct = 0
for p in penguins:
if guess(p["bill"], p["flipper"]) == p["species"]:
correct = correct + 1
print(str(correct) + " of " + str(len(penguins)) + " right: " + str(round(100 * correct / len(penguins))) + "%")
for p in penguins:
g = guess(p["bill"], p["flipper"])
if g != p["species"]:
print(p["species"] + " guessed as " + g + ": bill " + str(p["bill"]) + ", flipper " + str(p["flipper"]))
Open task: any program that does what the step asks.
import csv
penguins = []
with open("penguins.csv") as f:
for row in csv.DictReader(f):
penguins.append({"species": row["species"], "bill": float(row["bill_length_mm"]), "flipper": float(row["flipper_length_mm"]), "depth": float(row["bill_depth_mm"])})
def guess(p):
if p["depth"] < 16:
return "Gentoo"
if p["bill"] > 44:
return "Chinstrap"
return "Adelie"
correct = 0
for p in penguins:
if guess(p) == p["species"]:
correct = correct + 1
print(str(round(100 * correct / len(penguins))) + "% right")
Stretch: Print a table of guesses against real species (a confusion matrix) with a dictionary of dictionaries.
Support: Give students the loop that prints mistakes, and ask them only to write the if.
What does it mean that the rules are 95% accurate on the penguins?
They give the right species for 95 of every 100 penguins in the data (324 of 342).
Next lesson builds a model that learns from the examples themselves: nearest neighbour.