Siksha Sarovar

Siksha Sarovar (sikshasarovar.com) is a free educational web application that helps students in India learn programming and prepare for academic and competitive exams. The platform offers structured coding courses (C, C++, Python, Java, HTML, CSS, PHP, Power BI, AI, Machine Learning, Data Science), complete university curriculum notes for BCA/MCA students with previous year question papers, Class 10 and Class 12 CBSE/HBSE school notes, and dedicated preparation material for SSC, UPSC, Banking, Railway and other government exams. Browsing the site is completely free and requires no account. Users may optionally sign in with Google solely to save their learning progress, quiz scores and personal preferences across devices.

Privacy Policy | Terms of Service | Contact Siksha Sarovar | About Siksha Sarovar

v4.0.9 · PWA
Siksha Sarovar logo
Siksha Sarovar
Your Learning Universe

Siksha Sarovar is a free e-learning platform for coding courses, BCA university notes and competitive exam preparation. Optional Google sign-in saves your learning progress across devices.

Initializing knowledge base…
Compiling modules 0%

Unit 3 — K-Nearest Neighbour (KNN)

Lesson 28 of 46 in the free Introduction to Data Analytics notes on Siksha Sarovar, written by Rohit Jangra.

K-Nearest Neighbour (KNN)

KNN classifies a new observation by looking at the k closest training examples and taking a majority vote among their labels. It is the simplest possible "learning" algorithm — it stores the training data and does all the work at prediction time.

KNN is called a lazy learner (no model is built during training) and a non-parametric, instance-based algorithm (it makes no assumption about the data's distribution).

Distance Metrics

Euclidean distance (default, L2) — straight-line distance:

   d(p, q) = √[ (p₁−q₁)² + (p₂−q₂)² + … + (pₙ−qₙ)² ]

Manhattan distance (L1) — city-block distance:

   d(p, q) = |p₁−q₁| + |p₂−q₂| + … + |pₙ−qₙ|

Minkowski distance (generalisation):

   d(p, q) = ( Σ |pᵢ−qᵢ|^m )^(1/m)
             m = 1  ->  Manhattan
             m = 2  ->  Euclidean

Hamming distance — for categorical features: count of positions that differ

Worked Example — By Hand

Training data (height in cm, weight in kg → T-shirt size):

#HeightWeightSize
115858M
215859M
316060M
416361M
516561L
616862L
717068L

Classify a new customer: Height = 161, Weight = 61. Use k = 3.

Euclidean distance from (161, 61):

  #1 (158,58): √[(161−158)² + (61−58)²] = √[9 + 9]   = √18   = 4.243
  #2 (158,59): √[(161−158)² + (61−59)²] = √[9 + 4]   = √13   = 3.606
  #3 (160,60): √[(161−160)² + (61−60)²] = √[1 + 1]   = √2    = 1.414
  #4 (163,61): √[(161−163)² + (61−61)²] = √[4 + 0]   = √4    = 2.000
  #5 (165,61): √[(161−165)² + (61−61)²] = √[16 + 0]  = √16   = 4.000
  #6 (168,62): √[(161−168)² + (61−62)²] = √[49 + 1]  = √50   = 7.071
  #7 (170,68): √[(161−170)² + (61−68)²] = √[81 + 49] = √130  = 11.402

Sorted:  #3 (1.414, M), #4 (2.000, M), #2 (3.606, M), #5 (4.000, L), ...

k = 3 nearest:  #3 = M,  #4 = M,  #2 = M

Majority vote:  M = 3,  L = 0

PREDICTION: Size M

Choosing k

k valueEffect
k = 1Very flexible; fits every training point exactly → overfitting, highly sensitive to noise
Small kLow bias, high variance; jagged decision boundary
Large kHigh bias, low variance; smooth boundary, may underfit
k = nAlways predicts the overall majority class — useless

Rules of thumb:

  1. Try k ≈ √n as a starting point
  2. Use an odd k for binary classification to avoid ties
  3. Avoid k that is a multiple of the number of classes
  4. Choose k by cross-validation — plot error vs k and pick the elbow

Feature Scaling Is Mandatory

KNN is distance-based, so a feature with a large numeric range dominates the distance entirely:

Unscaled — income overwhelms age:
   Point A: age=25, income=50000
   Point B: age=45, income=50100

   d = √[(25−45)² + (50000−50100)²] = √[400 + 10000] = 102.0
        ^ age contributes 400              ^ income contributes 10,000

   The 20-year age gap is almost invisible next to a Rs.100 income difference.

After standardisation both features contribute comparably. ALWAYS scale before KNN.

Python

import numpy as np
import pandas as pd
from sklearn.neighbors import KNeighborsClassifier

# Reproducing the T-shirt example
X = np.array([[158,58],[158,59],[160,60],[163,61],[165,61],[168,62],[170,68]])
y = np.array(["M","M","M","M","L","L","L"])

knn = KNeighborsClassifier(n_neighbors=3, metric="euclidean")
knn.fit(X, y)

new = np.array([[161, 61]])
print("Prediction:", knn.predict(new)[0])                    # M
print("Class order:", knn.classes_)
print("Probabilities:", knn.predict_proba(new))              # [[0. 1.]] -> 100% M

distances, indices = knn.kneighbors(new)
print("Neighbour indices:", indices[0])                      # [2 3 1]
print("Distances:", distances[0].round(3))                   # [1.414 2.    3.606]
# Full workflow WITH scaling — on the Iris dataset
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import make_pipeline
from sklearn.metrics import accuracy_score, classification_report

iris = load_iris()
X_train, X_test, y_train, y_test = train_test_split(
    iris.data, iris.target, test_size=0.3, random_state=42, stratify=iris.target
)

# Pipeline guarantees the scaler is fitted on training data only
pipe = make_pipeline(StandardScaler(), KNeighborsClassifier(n_neighbors=5))
pipe.fit(X_train, y_train)
pred = pipe.predict(X_test)

print(f"Accuracy: {accuracy_score(y_test, pred):.4f}")       # 0.9556
print(classification_report(y_test, pred, target_names=iris.target_names))
# Choosing k by cross-validation — the elbow plot
import matplotlib.pyplot as plt

k_range = range(1, 31)
cv_scores = []
for k in k_range:
    pipe = make_pipeline(StandardScaler(), KNeighborsClassifier(n_neighbors=k))
    scores = cross_val_score(pipe, iris.data, iris.target, cv=10, scoring="accuracy")
    cv_scores.append(scores.mean())

best_k = k_range[int(np.argmax(cv_scores))]
print(f"Best k = {best_k} with CV accuracy {max(cv_scores):.4f}")

plt.figure(figsize=(9, 4.5))
plt.plot(k_range, cv_scores, marker="o", color="#168B99")
plt.axvline(best_k, color="#ef4444", linestyle="--", label=f"best k = {best_k}")
plt.xlabel("k (number of neighbours)")
plt.ylabel("10-fold CV accuracy")
plt.title("Choosing k for KNN")
plt.legend(); plt.grid(alpha=0.3); plt.show()
# Demonstrating WHY scaling matters
data = pd.DataFrame({
    "age":    [25, 45, 30, 50, 28, 47],
    "income": [50000, 50100, 52000, 51000, 49500, 53000],
    "buys":   ["No", "Yes", "No", "Yes", "No", "Yes"],
})
X = data[["age", "income"]].values
y = data["buys"].values
new_customer = [[26, 51500]]

unscaled = KNeighborsClassifier(n_neighbors=3).fit(X, y)
scaled = make_pipeline(StandardScaler(), KNeighborsClassifier(n_neighbors=3)).fit(X, y)

print("Without scaling:", unscaled.predict(new_customer)[0])
print("With scaling:   ", scaled.predict(new_customer)[0])
# The two often disagree — the unscaled version is effectively ignoring age.
# KNN for REGRESSION — average instead of vote
from sklearn.neighbors import KNeighborsRegressor
from sklearn.metrics import mean_squared_error, r2_score

X_r = np.array([[1],[2],[3],[4],[5],[6],[7],[8]])
y_r = np.array([2.1, 4.2, 6.1, 8.3, 9.8, 12.2, 14.1, 16.0])

knr = KNeighborsRegressor(n_neighbors=3)
knr.fit(X_r, y_r)
print("Prediction for x=4.5:", knr.predict([[4.5]])[0].round(3))
# Average of the 3 nearest y values

# Distance-weighted voting — closer neighbours count more
knr_w = KNeighborsRegressor(n_neighbors=3, weights="distance").fit(X_r, y_r)
print("Distance-weighted:", knr_w.predict([[4.5]])[0].round(3))

Weighted KNN

Instead of every neighbour getting one equal vote, weight votes by 1/distance so nearer neighbours influence the result more. Set weights="distance". This reduces sensitivity to a poorly chosen k.

The Curse of Dimensionality

As the number of features grows, all points become roughly equidistant from each other — the notion of "nearest" loses meaning, and KNN degrades badly.

MitigationHow
Feature selectionKeep only informative features
Dimensionality reductionApply PCA before KNN
Better distance metricCosine similarity for text/sparse data
More dataRequired exponentially as dimensions grow — often infeasible

Advantages and Disadvantages

AdvantagesDisadvantages
Extremely simple to understand and implementComputationally expensive at prediction time — O(n) distance computations per query
No training phase — new data can be added instantlyMust store the entire training dataset in memory
Naturally handles multi-class problemsRequires feature scaling
Makes no distributional assumptions (non-parametric)Suffers badly from the curse of dimensionality
Learns arbitrarily complex, non-linear boundariesSensitive to irrelevant features and to noise/outliers
Works for both classification and regressionChoice of k and distance metric is critical
Naturally adapts as data changesStruggles with imbalanced classes (majority dominates every vote)

KNN vs K-Means — The Classic Confusion

BasisKNNK-Means
Learning typeSupervisedUnsupervised
TaskClassification / regressionClustering
Meaning of kNumber of neighbours consultedNumber of clusters to form
Needs labels?YesNo
TrainingNone (lazy)Iterative centroid optimisation
OutputA predicted label/valueCluster assignments + centroids

They share only the letter k. Keep them strictly separate in exams.

Applications

Recommendation systems ("users similar to you"), handwriting and image recognition, credit-risk scoring, medical diagnosis from similar past cases, anomaly detection (a point with distant neighbours is anomalous), and missing-value imputation (KNNImputer, seen in Unit 1).

Next: the regression counterpart the syllabus names explicitly — linear regression.