A First Real Classifier: A Light Touch of scikit-learn
Data Science & ML Projects (Beginner)
Chapter 7 · A First Real Classifier: A Light Touch of scikit-learn
ml1 covers in full depth, not a substitute for that course. Every shortcut this chapter takes is named explicitly, not smoothed over.
What We're Building
A classifier for the Iris dataset — the single most famous "hello world" of classification: predict which of three flower species a sample belongs to, from four simple measurements (sepal length, sepal width, petal length, petal width). It ships built directly into scikit-learn, needs zero cleaning, and is small enough to see the whole pipeline at once.
Step 1: Loading the Dataset
from sklearn.datasets import load_iris import pandas as pd iris = load_iris() df = pd.DataFrame(iris.data, columns=iris.feature_names) df["species"] = [iris.target_names[i] for i in iris.target] print(df.head())
iris.data is a NumPy array of the four measurements; iris.target is the numeric species label for each row (0, 1, or 2). Building a DataFrame and mapping the numbers back to real species names makes the data readable the same way every prior chapter's own data has been.
Step 2: A Quick Look — Why This Dataset Works So Well
import seaborn as sns import matplotlib.pyplot as plt sns.scatterplot( data=df, x="petal length (cm)", y="petal width (cm)", hue="species", palette=["#15803D", "#EAB308", "#4ade80"], ) plt.title("Petal Measurements by Species") plt.savefig("iris_scatter.png")
Plotting just two of the four features already shows the three species separating into clearly distinct clusters — a genuine, visible reason this particular dataset became the classic teaching example: the classes are almost perfectly separable, letting even a simple model succeed clearly enough to see what's actually happening.
Step 3: Train/Test Split
from sklearn.model_selection import train_test_split X = df[iris.feature_names] y = df["species"] X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) print(f"Training on {len(X_train)} rows, testing on {len(X_test)} rows.")
The full reasoning behind why a model needs to be evaluated on data it never saw during training is ml1-2's own material in full — used correctly here, not re-derived. random_state=42 just makes the split reproducible from one run to the next.
Step 4: Fitting a Simple Classifier
from sklearn.neighbors import KNeighborsClassifier model = KNeighborsClassifier(n_neighbors=5) model.fit(X_train, y_train)
n_neighbors closest points in the training data and taking a majority vote of their species — genuinely intuitive, no gradient descent or decision-tree splitting logic to explain. It's used here specifically because it's not logistic regression (ml1-5) or decision trees/random forests (ml1-7) — those get their own full, proper treatment in that course; this chapter isn't trying to preempt either one.
Step 5: Evaluating — The Single Simplest Metric
accuracy = model.score(X_test, y_test) print(f"Accuracy: {accuracy:.2%}")
.score() here reports plain accuracy — the fraction of test predictions that were correct. It's genuinely the simplest possible metric, and genuinely not the full picture: ml1-6 covers precision, recall, F1, and the confusion matrix in depth, including exactly why accuracy alone can be misleading on imbalanced data. Iris happens to have equal numbers of each species, which is part of why plain accuracy is defensible here specifically — that won't be true of every dataset.
Step 6: Seeing the Predictions
predictions = model.predict(X_test) results = X_test.copy() results["actual"] = y_test.values results["predicted"] = predictions results["correct"] = results["actual"] == results["predicted"] print(results[results["correct"] == False]) # the rows the model got wrong, if any
Looking directly at whichever rows the model got wrong (if any) is worth more than the single accuracy number alone — on this dataset, mistakes almost always happen between two of the three species that sit closest together in the Step 2 scatter plot, not randomly across all three, a small, concrete confirmation that the model is making genuinely sensible errors rather than arbitrary ones.
The Complete Classifier
from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.neighbors import KNeighborsClassifier import pandas as pd iris = load_iris() df = pd.DataFrame(iris.data, columns=iris.feature_names) df["species"] = [iris.target_names[i] for i in iris.target] X = df[iris.feature_names] y = df["species"] X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) model = KNeighborsClassifier(n_neighbors=5) model.fit(X_train, y_train) accuracy = model.score(X_test, y_test) print(f"Accuracy: {accuracy:.2%}")
Built-in datasets
load_iris() ships with scikit-learn — no download, no cleaning needed.
train_test_split()
Used correctly here; ml1-2 covers the full reasoning behind it.
K-Nearest Neighbors
Classify by majority vote among the closest training points — no gradient descent.
Accuracy's own honest limits
The simplest metric, not the full picture — ml1-6 covers precision/recall/F1.
Try these on your own:
- Change
n_neighborsto 1 and to 15 — how does accuracy change, and why might too few or too many neighbors both cause problems? - Once you've taken ml1, come back and fit a
LogisticRegressionorDecisionTreeClassifieron this exact same data and compare its accuracy to KNN's own. - Use only two features (say, petal length and width) instead of all four, and see how much accuracy actually drops.
- Try the same pipeline on scikit-learn's other built-in dataset,
load_wine(), with no other changes — does it still work this cleanly?
What's Next
Chapter 8: Capstone — Your Own Dataset, Start to Finish — no fixed dataset this time: your own choice, taken through the complete collect-clean-explore-visualize-model pipeline this course has built one piece at a time.
Chapter 7 Quick Reference
- This course's own single, deliberately light touch of ml1 — a taste, not a substitute for that course's own full depth
- Iris: built into scikit-learn, needs no cleaning, chosen specifically to keep the focus on the modeling pipeline itself
- K-Nearest Neighbors used deliberately instead of logistic regression (ml1-5) or decision trees (ml1-7), so as not to preempt either
train_test_split()used correctly, not re-derived — ml1-2 covers the full reasoning- Plain accuracy reported honestly as the simplest metric, not the full evaluation picture ml1-6 covers