Students rebuild nearest neighbour and the fair test with scikit-learn (train_test_split, fit, score, predict), match each line to their hand-built version, compare 5 neighbours and a decision tree, and search for the best measurements.
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. scikit-learn loads the first time a program imports it (several seconds; about 19 MB, cached by the browser afterwards). X is a pandas table of measurements and y the species; train_test_split(..., random_state=1) shuffles and splits; fit stores the training examples (for nearest neighbour) or learns rules (for a tree); score is accuracy on the given data (the bug task scores on training data: 100%); predict needs a table with the same columns. Results: 94% test accuracy with 1 neighbour, 93% with 5, 93% for a tree of depth 3. In the Make, adding body mass drops nearest neighbour to about 70% (grams swamp the distance) while the tree reaches 97%: a real lesson about scale, which the info question explains.
Mapping library lines to their hand-built versions. Ask students to annotate each line with the lesson it came from before running.
| What you will see | A question to ask | The fix |
|---|---|---|
| ValueError: Expected 2D array, got 1D array instead. | Is X a table (double square brackets) or one column? | penguins[["bill_length_mm", "flipper_length_mm"]], with two pairs of brackets. |
| UserWarning: X does not have valid feature names. | Was the mystery penguin made with columns=X.columns? | pd.DataFrame([[47.0, 215.0]], columns=X.columns). |
| The program seems stuck when it starts. | Is it the first run with scikit-learn? | Wait: the library loads once, then runs are quick. |
One possible solution for each task. Other correct programs are fine.
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
penguins = pd.read_csv("penguins.csv")
X = penguins[["bill_length_mm", "flipper_length_mm"]]
y = penguins["species"]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=1)
model = KNeighborsClassifier(n_neighbors=5)
model.fit(X_train, y_train)
print("Test accuracy: " + str(round(model.score(X_test, y_test) * 100)) + "%")
mystery = pd.DataFrame([[47.0, 215.0]], columns=X.columns)
print("Mystery penguin: " + model.predict(mystery)[0])
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier
penguins = pd.read_csv("penguins.csv")
X = penguins[["bill_length_mm", "flipper_length_mm"]]
y = penguins["species"]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=1)
model = KNeighborsClassifier(n_neighbors=1)
model.fit(X_train, y_train)
print("Test accuracy: " + str(round(model.score(X_test, y_test) * 100)) + "%")
mystery = pd.DataFrame([[47.0, 215.0]], columns=X.columns)
print("Mystery penguin: " + model.predict(mystery)[0])
tree = DecisionTreeClassifier(max_depth=3, random_state=1)
tree.fit(X_train, y_train)
print("Decision tree: " + str(round(tree.score(X_test, y_test) * 100)) + "%")
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier
penguins = pd.read_csv("penguins.csv")
y = penguins["species"]
choices = [
["bill_length_mm", "flipper_length_mm"],
["bill_length_mm", "bill_depth_mm"],
["bill_length_mm", "bill_depth_mm", "flipper_length_mm"],
["bill_length_mm", "bill_depth_mm", "flipper_length_mm", "body_mass_g"],
]
for columns in choices:
X = penguins[columns]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=1)
knn = KNeighborsClassifier(n_neighbors=5).fit(X_train, y_train)
tree = DecisionTreeClassifier(max_depth=3, random_state=1).fit(X_train, y_train)
print(", ".join(columns) + ": neighbours " + str(round(knn.score(X_test, y_test) * 100)) + "%, tree " + str(round(tree.score(X_test, y_test) * 100)) + "%")
Stretch: Add StandardScaler (from sklearn.preprocessing) before the neighbours model with make_pipeline, and see the body-mass result recover.
Support: Give students the DecisionTreeClassifier lines, and ask them only to score it on the test set.
What does model.fit(X_train, y_train) do?
It gives the model the training examples to learn from (for nearest neighbour, it keeps them to compare with later).
Next lesson puts the model behind a Gradio app. The decision tree's rules can be printed with sklearn.tree.export_text.