K-Nearest Neighbors
K-Nearest Neighbors (KNN) is a non-parametric, instance-based learning algorithm that classifies or regresses a new data point by finding the K most similar examples in the training set and aggregating their labels. It matters because it requires zero training time, is trivially interpretable, and serves as the conceptual foundation for many modern similarity-based systems. The counterintuitive fact: KNN has no explicit "training" phase — the entire dataset is the model, which means prediction gets slower as you add more data, the exact opposite of most algorithms.
The fundamental assumption is beautifully simple: things that are similar to each other tend to belong to the same category. When you see a new, unknown point, find the K training points closest to it in feature space and let them "vote" on what the new point should be. There is no equation to fit, no weights to learn — the data speaks for itself at prediction time. The "aha moment" is realizing that you can build a surprisingly powerful classifier without ever explicitly learning anything, just by remembering everything.
How It Works
KNN operates in two conceptual phases: storage and lookup. During storage, you simply keep the entire training dataset in memory. During lookup (prediction), you measure the distance from the query point to every stored point, find the K closest, and aggregate their labels.
Distance Metric
The most common distance metric is Euclidean distance:
Explanation: The straight-line distance between two points A and B across all n features — the familiar ruler distance extended into many dimensions.
Symbols:
- : distance between points A and B
- , : the value of the i-th feature for points A and B respectively
- : total number of features (dimensions)
- : sum over all features
Another common choice is Manhattan distance, which adds absolute differences instead of squaring them:
Explanation: The "city block" distance — how far you'd travel if you could only move along grid lines, never diagonally.
Classification Decision
Once K neighbors are found, the predicted class is determined by majority vote:
Explanation: Pick whichever class label c appears most often among the K neighbors — literally count the votes and choose the winner.
Symbols:
- : the predicted class label
- : the class c that maximizes the expression
- : an indicator that equals 1 if neighbor i has class c, else 0
- K: the number of neighbors to consider
Python Implementation
Here's KNN from scratch, and then using scikit-learn: ```python import numpy as np from collections import Counter from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.neighbors import KNeighborsClassifier from sklearn.metrics import accuracy_score
# ── KNN from scratch ───────────────────────────────────────── class SimpleKNN: def __init__(self, k=3): self.k = k def fit(self, X, y):
# "Training" is just storing the data — that's it! self.X_train = X self.y_train = y def predict(self, X): return np.array([self._predict_one(x) for x in X]) def _predict_one(self, x):
# Step 1: compute distances to ALL training points distances = np.sqrt(np.sum((self.X_train - x) ** 2, axis=1))
# Step 2: find the K nearest indices k_indices = np.argsort(distances)[: self.k]
# Step 3: majority vote k_labels = self.y_train[k_indices] most_common = Counter(k_labels).most_common(1) return most_common[0][0]
# ── Load and split data ─────────────────────────────────────── iris = load_iris() X, y = iris.data, iris.target X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42 )
# ── IMPORTANT: scale features before KNN ───────────────────── scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) X_test_scaled = scaler.transform(X_test)
# ── From-scratch model ──────────────────────────────────────── knn_scratch = SimpleKNN(k=3) knn_scratch.fit(X_train_scaled, y_train) scratch_preds = knn_scratch.predict(X_test_scaled) print(f"Scratch accuracy: {accuracy_score(y_test, scratch_preds):.3f}")
# ── Scikit-learn model ──────────────────────────────────────── knn_sklearn = KNeighborsClassifier(n_neighbors=3, metric="euclidean") knn_sklearn.fit(X_train_scaled, y_train) sklearn_preds = knn_sklearn.predict(X_test_scaled) print(f"Sklearn accuracy: {accuracy_score(y_test, sklearn_preds):.3f}")
# ── Tune K with cross-validation ───────────────────────────── from sklearn.model_selection import cross_val_score for k in [1, 3, 5, 7, 11, 15]: scores = cross_val_score( KNeighborsClassifier(n_neighbors=k), X_train_scaled, y_train, cv=5 ) print(f"K={k:2d} CV accuracy: {scores.mean():.3f} ± {scores.std():.3f}") ``` ``` Scratch accuracy: 1.000 Sklearn accuracy: 1.000 K= 1 CV accuracy: 0.942 ± 0.026 K= 3 CV accuracy: 0.958 ± 0.033 K= 5 CV accuracy: 0.958 ± 0.033 K= 7 CV accuracy: 0.967 ± 0.021 K=11 CV accuracy: 0.950 ± 0.038 K=15 CV accuracy: 0.942 ± 0.026 ``` Notice that K=7 wins on this dataset — neither the smallest nor the largest K.
Feature Scaling Is Mandatory
KNN uses raw distances, so a feature measured in kilometres will dominate a feature measured in metres even if the metres feature is more informative. Always scale: ```python
# Without scaling — feature with large range dominates distance
# With StandardScaler — each feature has mean=0, std=1
# With MinMaxScaler — each feature is in [0, 1] from sklearn.preprocessing import MinMaxScaler
# Demonstrate the danger of not scaling X_bad = np.column_stack([ np.random.randn(100) 1000, # feature 1: huge range np.random.randn(100) 0.01, # feature 2: tiny range ])
# Distance will be almost entirely determined by feature 1!
# Feature 2 is effectively invisible to KNN. print("Always scale your features before using KNN.") ```
Evelyn Fix was an American statistician at UC Berkeley whose work focused on nonparametric methods and statistical theory. In 1951, she co-authored an unpublished technical report with Joseph Hodges introducing the discriminatory analysis method that became KNN — one of the most-cited unpublished works in all of statistics. Remarkably, because the report was never formally published in a journal, Fix never received the academic recognition commensurate with the enormous impact of her idea during her lifetime.
Intuition
Imagine you've just moved to a new city and want to know if a neighbourhood is safe. You don't have a formula — instead, you ask the five nearest residents about their experience. If four of them say "it's great", you conclude it's probably fine. That's exactly KNN: the neighbourhood of a point tells you what that point probably is. Another analogy: a doctor diagnosing a rare condition might think "this patient's symptoms most closely resemble three previous patients who all had Disease X — so probably Disease X." The doctor isn't running equations; she's doing nearest-neighbour lookup in her memory of past cases. The key insight is that this works because the world has structure — similar inputs tend to produce similar outputs, and that structure is preserved in the geometric arrangement of your data points.
Historical Development
Origins · 1 minhow this was discovered
Why It Matters
Prerequisites
What This Leads To
See Also
Decision Trees · Naive Bayes · Logistic Regression · Principal Component Analysis · DBSCAN Clustering