Python Tutorial
K-nearest Neighbors (KNN)
Classify or predict a point by looking at the k closest examples in the training data.
A Lazy, Instance-Based Learner
KNN does no real "training" — it just stores the data. To predict, it finds the k nearest training points to the query and takes a majority vote (classification) or an average (regression). Because it defers all work to prediction time, it is called a lazy learner.
It is intuitive and makes no assumptions about the data's shape, but is sensitive to feature scale and slows down as the dataset grows.
KNN Classifier
import numpy as np
from sklearn.neighbors import KNeighborsClassifier
x = [4, 5, 10, 4, 3, 11, 14, 8, 10, 12]
y = [21, 19, 24, 17, 16, 25, 24, 22, 21, 21]
classes = [0, 0, 1, 0, 0, 1, 1, 0, 1, 1]
X = np.column_stack([x, y])
knn = KNeighborsClassifier(n_neighbors=3)
knn.fit(X, classes)
print(knn.predict([[8, 21]])) # predicted class
print(knn.predict_proba([[8, 21]])) # vote proportionsScaling Is Essential
KNN measures distance, so a feature with a large range dominates the calculation. Always standardize features first — inside a pipeline to avoid leakage.
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
knn = make_pipeline(StandardScaler(), KNeighborsClassifier(n_neighbors=5))
knn.fit(X, classes)Skipping scaling is the most common KNN mistake. Unscaled, a feature measured in thousands will drown out one measured in single digits.
Choosing k
Small k (like 1) fits noise and overfits; large k oversmooths and underfits. Tune k with cross-validation, and prefer an odd k for binary problems to avoid tie votes.
from sklearn.model_selection import cross_val_score
for k in range(1, 10, 2):
pipe = make_pipeline(StandardScaler(), KNeighborsClassifier(n_neighbors=k))
score = cross_val_score(pipe, X, classes, cv=3).mean()
print(f"k={k}: {score:.3f}")Distance Weighting and Metrics
By default all k neighbors vote equally. weights="distance" gives closer neighbors more influence. The distance metric can also change (p=2 is Euclidean, p=1 is Manhattan).
knn = KNeighborsClassifier(n_neighbors=5, weights="distance", p=2)KNN for Regression
from sklearn.neighbors import KNeighborsRegressor
reg = KNeighborsRegressor(n_neighbors=3)
reg.fit(X, [21, 19, 24, 17, 16, 25, 24, 22, 21, 21])
print(reg.predict([[8, 21]])) # average of the 3 nearest targetsStrengths, Weaknesses, Best Practices
- Strengths: simple, no training phase, naturally multiclass, adapts to any decision boundary.
- Weaknesses: slow at prediction on large data, memory-heavy, hurt by irrelevant features and high dimensionality (curse of dimensionality).
- Always scale features and tune k with cross-validation.
- Reduce dimensionality (PCA / feature selection) before KNN on wide datasets.
Try It Yourself
Exercise 1: Train a 3-NN classifier on iris (scaled) and print test accuracy.
Show solution
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
X, y = load_iris(return_X_y=True)
Xtr, Xte, ytr, yte = train_test_split(X, y, random_state=0)
knn = make_pipeline(StandardScaler(), KNeighborsClassifier(3)).fit(Xtr, ytr)
print(round(knn.score(Xte, yte), 3))Exercise 2: Why should k usually be odd for binary classification?
Show solution
An odd k avoids tie votes between the two classes.
📘 Real-World Deep Dive
Knowing <strong>ML Knn (scikit-learn)</strong> well is what turns scikit-learn from a curiosity into a daily tool — you'll reach for it in nearly every real project.
Real-Life Scenario
An end-to-end usage of ML Knn that you'd actually see in a data pipeline or analytics notebook.
Real-Life Example
import numpy as np
from sklearn.neighbors import KNeighborsClassifier
from sklearn.datasets import load_iris
X, y = load_iris(return_X_y=True)
clf = KNeighborsClassifier(n_neighbors=5).fit(X, y)
print("score:", round(clf.score(X, y), 3))Expected Output
(see source)Common mistakes
fitexpects numeric arrays;OneHotEncoder/LabelEncoderare easy to forget for categorical features.- Calling
predicton a model trained on unscaled data and then scaling inputs at inference time silently degrades accuracy. train_test_split(X, y)requires both arrays;train_test_split(X)for unsupervised learning slips past static checkers.- Treating ML Knn as a black box without reading the docs — the API has subtle defaults that bite when you scale.
🚀 Performance & Best Practices
- Wrap preprocessing + estimator in a
Pipelinesofit/predictstay reproducible. - Use
joblib/picklefor serialising models, not the entire Python state. - For > 100 k rows, switch to
HistGradientBoostingClassifierorcuML. - When working with scikit-learn, prefer vectorised / batched operations over Python loops.
🧪 Try It Yourself
- Reproduce the snippet on a representative slice of your own data.
- Profile the snippet with
cProfileortimeitand find the single biggest improvement. - Generalise the snippet into a small, reusable function you can drop into future projects.