k-Nearest Neighbors

Suppose a hospital wants to predict whether a new patient's tumor is malignant. It has thousands of past patients with measured features and known outcomes. However, it has no formula relating them. One natural idea: find the past patients most similar to the new one. Then predict whatever the majority of them turned out to have. This is the entire idea behind k-Nearest Neighbors (k-NN). It is one of the oldest and conceptually simplest algorithms in machine learning. It dates to work in the early 1950s on nonparametric discrimination. k-NN matters because it makes almost no assumptions about the shape of the relationship between inputs and outputs. Instead of fitting a global equation, it stores the training data. It defers all computation to prediction time, a strategy called instance-based or lazy learning. This makes it a natural first algorithm for understanding the Bias–Variance Tradeoff concretely. The single knob kk visibly slides the model between overfitting and underfitting. It also introduces the idea that a learning algorithm's behavior is governed by a notion of distance or similarity. This theme reappears in clustering, recommender systems, and later in kernel methods. The big picture is this: k-NN replaces "learn a function" with "remember examples and vote among neighbors." Prediction requires no training phase in the usual sense. But it requires searching through data at query time. This creates real engineering tradeoffs in large-scale systems. Despite its simplicity, k-NN remains a strong baseline in image retrieval, recommendation, and anomaly detection. It also serves as a diagnostic tool for understanding a dataset's local structure before reaching for heavier models. This article builds k-NN from first principles. It covers the precise algorithm for classification and regression. It examines the effect of the neighborhood size kk and the choice of distance metric. It explains the complexity of brute-force versus tree-based search. It includes a full worked numerical example. Finally, it addresses the classical pitfalls that trip up nearly every newcomer, most notably the curse of dimensionality.

Key takeaways
  • k-NN predicts a query point's label by looking at the kk closest training points and taking a majority vote (classification) or average (regression).
  • There is no training phase beyond storing data; all work happens at prediction time — "lazy learning."
  • Small kk gives low bias, high variance (jagged, overfit boundaries); large kk gives high bias, low variance (smooth, underfit boundaries).
  • The distance metric (Euclidean, Manhattan, Minkowski, cosine) defines what "similar" means, and feature scaling is essential before computing it.
  • Brute-force prediction costs O(nd)O(nd) per query; k-d trees and ball trees can reduce this in low dimensions but degrade toward brute force in high dimensions.
  • k-NN is the conceptual bridge to Kernels and the Kernel Trick, since a vote among neighbors is a special case of a weighted average with a hard cutoff kernel.
The core insight

k-NN predicts by finding the kk most similar stored examples and letting them vote or average, turning the choice of kk and distance metric into a direct, tunable knob on the bias–variance tradeoff.

The idea

Formally, k-NN is a non-parametric, instance-based learning method. Given a training set

D={(x1,y1),…,(xn,yn)},\mathcal{D} = \{(x_1, y_1), \dots, (x_n, y_n)\},

where xi∈Rdx_i \in \mathbb{R}^d are feature vectors and yiy_i are labels (discrete for classification, real-valued for regression), k-NN stores D\mathcal{D} verbatim. There is no parameter vector θ\theta estimated from data — the "model" is the dataset itself, together with a chosen distance function dist(⋅,⋅)\text{dist}(\cdot,\cdot) and an integer k≥1k \geq 1. To predict the label of a new query point xqx_q, the algorithm finds the set Nk(xq)⊆DN_k(x_q) \subseteq \mathcal{D} of the kk training points closest to xqx_q under the chosen distance. For classification, the prediction is the plurality (most frequent) class among the neighbors' labels:

y^(xq)=argmax⁡c∑i∈Nk(xq)1[yi=c].\hat{y}(x_q) = \operatorname*{argmax}_{c} \sum_{i \in N_k(x_q)} \mathbb{1}[y_i = c].

For regression, the prediction is the mean (or a distance-weighted mean) of the neighbors' target values:

y^(xq)=1k∑i∈Nk(xq)yi.\hat{y}(x_q) = \frac{1}{k} \sum_{i \in N_k(x_q)} y_i.

Because prediction depends entirely on which points happen to be nearby, k-NN implicitly assumes that similar inputs have similar outputs — a smoothness assumption on the underlying function, not a specific parametric form. This is what makes it non-parametric: the effective complexity of the decision boundary grows with the amount of data, unlike a linear model whose functional form is fixed in advance regardless of nn.

How it works

The algorithm has two conceptual phases, though the first is nearly vacuous: 1. "Training": store the dataset D\mathcal{D}, optionally organized in a spatial index (a k-d tree or ball tree) to speed up future queries. No parameters are fit. 2. Prediction: for each query point, compute distances to (a relevant subset of) training points, select the kk nearest, and aggregate their labels by vote or average. Preprocessing is not optional in practice: features must be put on comparable scales (standardization or min-max scaling), because distance metrics like Euclidean distance treat all coordinates as commensurable, and a feature measured in the thousands will dominate one measured in fractions unless rescaled. ``` ALGORITHM: k-Nearest-Neighbors (classification variant) INPUT: D = {(x_1, y_1), ..., (x_n, y_n)} # training set x_q # query point k # number of neighbors, 1 <= k <= n dist(a, b) # distance function TRAIN(D): store D # optionally build a spatial index (k-d tree, ball tree) return D PREDICT(D, x_q, k, dist): distances <- empty list for each (x_i, y_i) in D: d_i <- dist(x_q, x_i) append (d_i, y_i) to distances sort distances by d_i ascending neighbors <- first k entries of sorted distances

# classification: majority vote (break ties by nearest neighbor or smallest class index) counts <- empty map from label to integer for (d_i, y_i) in neighbors: counts[y_i] <- counts[y_i] + 1 y_hat <- label with maximum count in counts (tie-break: prefer label of the single nearest neighbor) return y_hat PREDICT_REGRESSION(D, x_q, k, dist):

# same neighbor search as above, then: neighbors <- k nearest (d_i, y_i) pairs y_hat <- (1 / k) * sum(y_i for (d_i, y_i) in neighbors) return y_hat PREDICT_WEIGHTED(D, x_q, k, dist):

# distance-weighted variant, reduces sensitivity to k neighbors <- k nearest (d_i, y_i) pairs weights <- [1 / (d_i + epsilon) for each neighbor] # epsilon avoids divide-by-zero y_hat <- sum(w_i * y_i) / sum(w_i) # regression

# for classification, sum w_i within each class and argmax return y_hat ```

Choosing kk: kk is a hyperparameter selected by holding out validation data or by cross-validation, never by the training data's own error (which is minimized, misleadingly, at k=1k=1). Odd values of kk are conventional in binary classification to avoid tied votes. Choosing the distance metric: common choices are Euclidean (ℓ2\ell_2), Manhattan (ℓ1\ell_1), the general Minkowski ℓp\ell_p family, and cosine distance for high-dimensional sparse data such as text; the right choice depends on the geometry and scale of the features.

Figure
Effect of k on the decision boundary: k=1 memorizes noise, larger k smooths it
k = 1 (jagged, high variance) boundary bends around every noisy point k = 15 (smooth, high bias) boundary ignores the one noisy green point

Complexity & correctness

Time complexity. Let nn be the number of training points and dd the dimensionality of the features.

  • Training: O(1)O(1) with brute force (just store the array), or O(nlog⁡n)O(n \log n) if building a k-d tree or ball tree.
  • Brute-force prediction: computing distance to every point costs O(d)O(d) each, over nn points, giving O(nd)O(nd) per query; selecting the kk smallest via a partial selection (e.g., a max-heap of size kk) costs an additional O(nlog⁡k)O(n \log k), so the total is O(nd+nlog⁡k)O(nd + n\log k) per query.
  • Tree-based prediction (k-d tree, ball tree): average-case O(dlog⁡n)O(d \log n) per query when dd is small relative to nn, because the tree prunes large branches of the space that cannot contain a closer point than what's already found. This degrades toward O(nd)O(nd) in the worst case, and in practice toward brute force once dd exceeds roughly 15–20 dimensions — the geometric pruning stops helping because in high dimensions nearly every point ends up "on the boundary" of any partition.
  • Space: O(nd)O(nd) to store the raw data, plus O(n)O(n) overhead for a tree index.

There is a genuine and important asymmetry with parametric models: k-NN pushes essentially all computation from training time to prediction time. A linear or logistic regression model spends effort fitting a small parameter vector once, then predicts in O(d)O(d); k-NN "trains" instantly but pays O(nd)O(nd) or O(dlog⁡n)O(d\log n) on every single query, and must keep the entire dataset in memory indefinitely.

Correctness / why it works — the bias–variance view. Consider regression with squared-error loss. Suppose the true relationship is y=f(x)+εy = f(x) + \varepsilon with ε\varepsilon mean-zero noise of variance σ2\sigma^2, and the kk neighbors of xqx_q have approximately independent noise. The k-NN regression estimate is

f^k(xq)=1k∑i∈Nk(xq)yi=1k∑i∈Nk(xq)f(xi)+1k∑i∈Nk(xq)εi.\hat{f}_k(x_q) = \frac{1}{k}\sum_{i \in N_k(x_q)} y_i = \frac{1}{k}\sum_{i \in N_k(x_q)} f(x_i) + \frac{1}{k}\sum_{i \in N_k(x_q)} \varepsilon_i.

Taking expectation over the noise, the bias comes from averaging ff over a neighborhood instead of evaluating it exactly at xqx_q:

Bias(f^k(xq))≈1k∑i∈Nk(xq)(f(xi)−f(xq)),\text{Bias}(\hat f_k(x_q)) \approx \frac{1}{k}\sum_{i \in N_k(x_q)} \big(f(x_i) - f(x_q)\big),

which grows as kk grows, because the neighborhood radius must expand to include kk points, and ff typically varies more over a larger region (assuming ff is smooth, this scales with the neighborhood's spatial extent). The variance, assuming roughly independent noise across neighbors, is

Var(f^k(xq))=σ2k,\text{Var}(\hat f_k(x_q)) = \frac{\sigma^2}{k},

which shrinks as kk grows, exactly as averaging more independent noisy quantities reduces variance. This is the bias–variance tradeoff made explicit: k=1k=1 has zero averaging bias but variance σ2\sigma^2 (as noisy as a single observation); k=nk=n (predict the global mean for every query) has minimal variance σ2/n\sigma^2/n but maximal bias, since it ignores xqx_q entirely. The optimal kk minimizes the sum

MSE(xq)=Bias2+Var+σ2=(1k∑i∈Nk(f(xi)−f(xq)))2+σ2k+σ2,\text{MSE}(x_q) = \text{Bias}^2 + \text{Var} + \sigma^2 = \left(\frac{1}{k}\sum_{i\in N_k}(f(x_i)-f(x_q))\right)^2 + \frac{\sigma^2}{k} + \sigma^2,

and is found in practice by cross-validation rather than by this formula directly, since ff is unknown.

Consistency. A classical and reassuring theoretical result (Cover & Hart, 1967, for k=1k=1; extended for general kk) states that as n→∞n \to \infty with k→∞k \to \infty and k/n→0k/n \to 0, the k-NN classifier's error rate converges to the Bayes-optimal error rate — the best possible error achievable by any classifier given the true data distribution. Intuitively: letting kk grow ensures the noise-averaging variance vanishes, while requiring k/n→0k/n \to 0 ensures the neighborhood radius shrinks to zero (so the bias from averaging over a spatial region also vanishes) as more data arrives. This is a genuine asymptotic guarantee, but it says nothing about finite-sample behavior, where the curse of dimensionality (below) can make "shrinking neighborhoods" require impractically large nn.

Worked example

Consider a tiny 2-feature dataset predicting whether a loan applicant defaults (`Yes`/`No`), with features income (in $1000s) and debt ratio (percentage of income owed in debt):

PointIncomeDebt RatioLabel
x1x_12540No
x2x_23045No
x3x_37010No
x4x_46855Yes
x5x_54060Yes
x6x_63530No

Query point: xq=(Income=32,Debt Ratio=50)x_q = (\text{Income}=32, \text{Debt Ratio}=50).

Step 1 — scale features. Income ranges roughly 25–70 (span 45), debt ratio 10–60 (span 50); they're comparable here, but in general we standardize: z=(x−μ)/σz = (x - \mu)/\sigma. For this worked example we proceed with raw values for transparency, computing Euclidean distance dist(a,b)=∑j(aj−bj)2\text{dist}(a,b) = \sqrt{\sum_j (a_j - b_j)^2}.

Step 2 — compute distances to xq=(32,50)x_q = (32, 50):

PointComputationDistance
x1x_1(32−25)2+(50−40)2=49+100\sqrt{(32-25)^2+(50-40)^2} = \sqrt{49+100}149≈12.21\sqrt{149} \approx 12.21
x2x_2(32−30)2+(50−45)2=4+25\sqrt{(32-30)^2+(50-45)^2} = \sqrt{4+25}29≈5.39\sqrt{29} \approx 5.39
x3x_3(32−70)2+(50−10)2=1444+1600\sqrt{(32-70)^2+(50-10)^2} = \sqrt{1444+1600}3044≈55.17\sqrt{3044} \approx 55.17
x4x_4(32−68)2+(50−55)2=1296+25\sqrt{(32-68)^2+(50-55)^2} = \sqrt{1296+25}1321≈36.34\sqrt{1321} \approx 36.34
x5x_5(32−40)2+(50−60)2=64+100\sqrt{(32-40)^2+(50-60)^2} = \sqrt{64+100}164≈12.81\sqrt{164} \approx 12.81
x6x_6(32−35)2+(50−30)2=9+400\sqrt{(32-35)^2+(50-30)^2} = \sqrt{9+400}409≈20.22\sqrt{409} \approx 20.22

Step 3 — sort ascending: x2(5.39)<x1(12.21)<x5(12.81)<x6(20.22)<x4(36.34)<x3(55.17)x_2 (5.39) < x_1 (12.21) < x_5 (12.81) < x_6 (20.22) < x_4 (36.34) < x_3 (55.17).

Step 4 — predict for different kk:

kkNeighbors usedLabelsVotePrediction
1x2x_2NoNo: 1No
3x2,x1,x5x_2, x_1, x_5No, No, YesNo: 2, Yes: 1No
5x2,x1,x5,x6,x4x_2, x_1, x_5, x_6, x_4No, No, Yes, No, YesNo: 3, Yes: 2No

All three choices of kk agree here — the applicant with income 32 and debt ratio 50 is predicted not to default, since the closest analogues (x2x_2, a similarly low-income, low-debt-ratio applicant) did not default. Note how k=5k=5 nearly flips: two of the five neighbors are the "Yes" applicants x4x_4 and x5x_5, illustrating how increasing kk pulls in more distant, less relevant points and can shift or destabilize the prediction — exactly the bias increase the theory predicts. For a regression flavor of the same data (say we instead predict a continuous default risk score yy with values y1=0.1,y2=0.15,y3=0.05,y4=0.8,y5=0.7,y6=0.2y_1=0.1, y_2=0.15, y_3=0.05, y_4=0.8, y_5=0.7, y_6=0.2), a k=3k=3 prediction would be

y^(xq)=13(y2+y1+y5)=13(0.15+0.1+0.7)=0.317,\hat y(x_q) = \frac{1}{3}(y_2 + y_1 + y_5) = \frac{1}{3}(0.15+0.1+0.7) = 0.317,

again dominated by the two truly close points but nudged upward by the more distant but risky x5x_5.

Common pitfalls & traps

PitfallWhy it happensFix
Forgetting to scale featuresDistance is dominated by whichever feature has the largest numeric rangeStandardize (zero mean, unit variance) or min-max scale before computing distances
Choosing kk by training accuracyk=1k=1 always achieves zero training error (each point is its own nearest neighbor) but overfits badlyChoose kk via cross-validation on held-out data
Using kk-NN naively in high dimensionsThe curse of dimensionality: in high-dd spaces, distances between all points concentrate, so "nearest" and "farthest" become nearly indistinguishableReduce dimensionality first (PCA, feature selection) or use a learned/domain-appropriate distance
Ignoring class imbalanceMajority vote is biased toward the more frequent class when kk is large, since a plurality of neighbors from the sheer background frequency, not true similarity, can dominateUse distance-weighted voting, or resample/reweight classes
Even kk in binary classificationTies in the vote must be broken arbitrarily, adding instabilityPrefer odd kk, or break ties using the single nearest neighbor
Treating brute-force search as "free"O(nd)O(nd) per query seems fine for small data but becomes a bottleneck at scale (millions of queries against millions of points)Use approximate nearest-neighbor structures (k-d tree, ball tree, or approximate methods like LSH/HNSW) in production
Leaking test data into distance computationStandardizing using statistics computed over the full dataset (train+test) leaks test informationFit scaling parameters on training data only, then apply to test/query points

Pioneers & history

Origins · 1 minEvelyn Fix and how this was discovered
EF
Evelyn Fix
1904–1965 · Statistician, University of California, Berkeley

Evelyn Fix, working with Joseph Hodges at UC Berkeley for the US Air Force School of Aviation Medicine, wrote the 1951 unpublished technical report that first formalized nonparametric discrimination by nearest neighbors — the origin of what became k-NN. Their work went largely unrecognized for years before being properly credited as foundational to nonparametric statistics.

History
1951
Fix and Hodges introduce nonparametric discriminant analysis via nearest neighbors in an Air Force technical report.
1967
Thomas Cover and Peter Hart formally prove the asymptotic error bound of the nearest-neighbor rule, showing it is at most twice the Bayes error rate in the limit.
1970
s: k-d trees (Bentley, 1975) and later ball trees are developed to accelerate nearest-neighbor search beyond brute force.
1990
s–2000s: k-NN becomes a standard baseline in pattern recognition and machine learning textbooks, alongside decision trees and linear classifiers.
2010
s–present: approximate nearest-neighbor methods (locality-sensitive hashing, HNSW graphs) scale the same core idea to billion-item retrieval systems.

Where this matters

k-NN's direct descendants are everywhere in industrial machine learning even when the name "k-NN" doesn't appear on the label. Recommender systems use nearest-neighbor lookups over user or item embeddings to suggest products, songs, or videos. Image and face-recognition retrieval systems embed images into vector spaces and then run (approximate) nearest-neighbor search to find matches — the same core operation as k-NN classification, but at a scale where brute force is replaced by structures like HNSW or product quantization. Anomaly detection systems flag points whose nearest neighbors are unusually far away. In bioinformatics, k-NN imputes missing gene-expression values from similar samples. Beyond direct use, k-NN is the pedagogical bridge to more advanced methods: understanding how it weights neighbors by distance leads naturally into Kernels and the Kernel Trick, where a smooth weighting function (a kernel) replaces the hard "in the k nearest or not" cutoff, and into kernel density estimation, local regression (LOESS), and Gaussian processes, all of which generalize the same "local similarity implies local averaging" principle that k-NN teaches in its simplest form. Understanding k-NN also builds direct intuition for the Bias–Variance Tradeoff and for probability concepts underlying Bayes-optimal classification, both essential for evaluating any supervised learning method.

Ask the tutor: How does replacing the hard k-nearest cutoff with a smooth distance-based weighting function turn k-NN into a kernel method?

Is k-NN a "trained" model in the same sense as logistic regression or a neural network?

Not really. k-NN has no parameters that are fit by minimizing a loss function during a training phase; it simply stores the training data (and perhaps builds a spatial index for faster search). All the "work" traditionally associated with training — combining information across examples to make a compact decision rule — is deferred to prediction time, when neighbors are searched and aggregated. This is why k-NN is called a lazy or instance-based learner, in contrast to eager learners like linear models or trees that compress the training set into a fixed set of parameters before ever seeing a query point.

How should I choose k in practice?

Use cross-validation: split the training data into folds, try a range of candidate kk values (commonly odd numbers from 1 up to some fraction of nn), and pick the kk that minimizes average validation error across folds. Never choose kk using training-set accuracy alone, since k=1k=1 trivially achieves zero training error by definition (each training point is its own nearest neighbor) while typically overfitting. A common heuristic starting point is k≈nk \approx \sqrt{n}, but this should be treated as a starting guess to validate, not a rule.

Which distance metric should I use?

Euclidean distance (ℓ2\ell_2) is the default for continuous, roughly isotropic features after standardization. Manhattan distance (ℓ1\ell_1) is more robust to outliers in individual coordinates and is often preferred in high-dimensional or grid-like feature spaces. Cosine distance (or cosine similarity) is standard for high-dimensional sparse vectors such as text represented as term-frequency vectors, since it measures orientation rather than magnitude. For categorical or mixed data, specialized metrics like Hamming distance (categorical) or Gower's distance (mixed types) are used instead of raw Euclidean distance.

Does k-NN work well in high dimensions?

Generally, no, without modification. As dimensionality increases, the relative contrast between the nearest and farthest neighbor distances shrinks toward zero for many common data distributions, a phenomenon known as the curse of dimensionality. This means the "neighbors" k-NN finds become less meaningfully close than they appear. Practitioners typically apply dimensionality reduction (PCA, autoencoders, or supervised feature selection) or work in a learned embedding space before applying k-NN, rather than running it directly on hundreds of raw features.

How is k-NN different from k-means clustering?

They share a name pattern but solve different problems. k-NN is a supervised algorithm: it uses labeled training data to predict labels for new points via neighbor voting or averaging. k-means is unsupervised: it partitions unlabeled data into kk clusters by iteratively assigning points to the nearest of kk cluster centroids and updating those centroids, with no labels involved at all. The "k" means something different in each: in k-NN it is the number of neighbors consulted per query; in k-means it is the number of clusters to discover.

Can k-NN be used for regression, not just classification?

Yes. Instead of a majority vote over the k nearest neighbors' class labels, k-NN regression averages (or takes a distance-weighted average of) their continuous target values. The same bias–variance logic applies: small kk gives a low-bias, high-variance, noisy-looking regression function that tracks individual training points closely, while large kk gives a smoother, higher-bias, lower-variance function that approaches the global mean as kk approaches nn.

Why is k-NN prediction expensive compared to a linear model, and what fixes this?

A linear model compresses all training information into a fixed-size weight vector during training, so prediction only costs O(d)O(d) regardless of how large the training set was. k-NN keeps the entire dataset around and must search it — at least implicitly — for every single prediction, costing O(nd)O(nd) per query in the brute-force case. Spatial index structures like k-d trees or ball trees reduce this to roughly O(dlog⁡n)O(d \log n) per query in low dimensions by pruning large regions of space, and approximate methods like locality-sensitive hashing or HNSW graphs trade a small amount of accuracy for major speedups at web scale.

Before this

Where this leads