k-Nearest Neighbors › Formula

Michael BrenndoerferOctober 26, 202617 min read
Machine Learning from ScratchAll chapters
Part 7 of 15Supervised Learning Beyond Linear Models
Chapter 53k-Nearest Neighbors › Formula
Reading section 4 of 10 · Formula · 17 minSection 4 of 10 · 17 min

Section 4 of 10 · 17 min read

k-Nearest Neighbors › FormulaSection view · Sections in this chapter4 / 10 ↓
Reading tools Definitions, links & notes

Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.

Article links

Make inline references clickable

Notes & comments

This section defines the distance and neighborhood rules used by kNN, then examines neighborhood size, weighting, scaling, and computational cost.

Distances and local prediction rules

The whole of kNN is built from two questions. First, how similar is the query to each stored example? Second, which examples are similar enough to count? The first question needs a number, a distance. The second needs a rule, a neighborhood. Everything else, including voting and averaging, follows from how we answer those two questions. It makes sense to build them in that order, because the prediction rule only becomes meaningful once we know which points are allowed to speak.

Suppose our training set is a collection of nn labeled observations

D={(x1,y1),(x2,y2),…,(xn,yn)},\mathcal{D} = \{(\mathbf{x}_1, y_1), (\mathbf{x}_2, y_2), \dots, (\mathbf{x}_n, y_n)\},

where each xi∈Rp\mathbf{x}_i \in \mathbb{R}^p is a vector of pp features, and yiy_i is its label or target. The query point is a feature vector xq\mathbf{x}_q with unknown label. Here x\mathbf{x} is bold because it is a vector, and the subscript indexes the training example. In words: D\mathcal{D} is the stored dataset, xi\mathbf{x}_i is the ii-th training input, and yiy_i is the corresponding output.

Why start with a distance rather than a probability or a parameter? Because kNN is non-parametric: it does not restrict the regression function or decision boundary to a fixed parametric family. Its predictions compare examples directly, and their usefulness depends on a distance that places inputs with similar targets near one another. A distance is the minimal machinery that turns "these two inputs look alike" into something a computer can sort. Everything we add later is built on this single measurable notion of resemblance.

The Euclidean distance between the query and the ii-th training point is

d(xq,xi)=∑j=1p(xqj−xij)2d(\mathbf{x}_q, \mathbf{x}_i) = \sqrt{\sum_{j=1}^{p} (x_{qj} - x_{ij})^2}

where:

  • xqjx_{qj}: the jj-th feature of the query point
  • xijx_{ij}: the jj-th feature of the ii-th training point
  • pp: number of features
  • d(⋅,⋅)d(\cdot, \cdot): Euclidean distance between two feature vectors

Read it in four moves: take the difference between the query and the training point in each feature jj; square it; sum over the pp features; then take the square root. The squaring does real work: it lets a positive and a negative deviation of the same size count identically, and it lets a few large disagreements outweigh many small ones. The square root returns the total to the original units, so features measured in the same physical unit yield a distance in that unit. With mixed or transformed feature units, the distance is a modeling quantity whose interpretation depends on those scales.

We can also write the same expression compactly using vector notation. Let d=xq−xi\mathbf{d} = \mathbf{x}_q - \mathbf{x}_i be the elementwise difference vector. Then

d(xq,xi)=∥xq−xi∥2=d⊤dd(\mathbf{x}_q, \mathbf{x}_i) = \|\mathbf{x}_q - \mathbf{x}_i\|_2 = \sqrt{\mathbf{d}^\top \mathbf{d}}

where:

  • xq\mathbf{x}_q: the query feature vector
  • xi\mathbf{x}_i: the ii-th training feature vector
  • d\mathbf{d}: the elementwise difference vector d=xq−xi\mathbf{d} = \mathbf{x}_q - \mathbf{x}_i
  • ∥⋅∥2\|\cdot\|_2: Euclidean (L2) norm

Here d⊤d\mathbf{d}^\top \mathbf{d} is the dot product of d\mathbf{d} with itself, which equals ∑jdj2\sum_j d_j^2. This is the same quantity as before, only now expressed as a matrix multiplication. The vector form is also computationally useful. Stack all xi\mathbf{x}_i into an n×pn \times p matrix X\mathbf{X}, and the array of distances becomes ∑j(xqj−X:,j)2\sqrt{\sum_{j} (\mathbf{x}_{qj} - \mathbf{X}_{:,j})^2}, where xqj\mathbf{x}_{qj} is the jj-th feature of the query and X:,j\mathbf{X}_{:,j} is the jj-th column of X\mathbf{X}. That single vectorized expression is what makes brute-force kNN feasible even for moderately large nn, and it is exactly the computation the code later performs.

Euclidean distance is only one choice. Minkowski distance generalizes it as

d(xq,xi)=(∑j=1p∣xqj−xij∣r)1/rd(\mathbf{x}_q, \mathbf{x}_i) = \left(\sum_{j=1}^p |x_{qj} - x_{ij}|^r\right)^{1/r}

where:

  • r≥1r \ge 1: the Minkowski order parameter controlling how large feature differences are weighted
  • xqjx_{qj}: the jj-th feature of the query point
  • xijx_{ij}: the jj-th feature of the ii-th training point
  • pp: number of features

Here r=2r=2 recovers Euclidean distance and r=1r=1 gives Manhattan distance (sum of absolute differences). Any choice of distance defines a slightly different notion of "closeness", and therefore different neighbors. This is the first hint that the distance is a modeling decision, not a neutral measurement. Euclidean distance is a common starting point for comparably scaled continuous features. Manhattan distance adds absolute coordinate differences, while Euclidean distance combines their squares before taking a square root, so a large coordinate discrepancy can have greater relative influence in Euclidean neighborhoods. Both distances remain sensitive to extreme observations, and standardization does not make their neighbor rankings agree. Compare the metric choices under the same validation protocol.

From distances to a neighborhood

With a distance in hand, every training point carries a single number: how far it sits from the query. The natural next move is to keep the smallest kk of those numbers, and that is exactly what defines the neighborhood. Formally, the set of the kk nearest training indices is

Nk(xq)=arg min⁡S⊂{1,…,n}, ∣S∣=k∑i∈Sd(xq,xi),\mathcal{N}_k(\mathbf{x}_q) = \operatorname*{arg\,min}_{S \subset \{1,\dots,n\}, \, |S|=k} \sum_{i \in S} d(\mathbf{x}_q, \mathbf{x}_i),

where:

  • Nk(xq)\mathcal{N}_k(\mathbf{x}_q): the set of indices of the kk nearest training points to the query
  • SS: a subset of training indices with ∣S∣=k|S|=k
  • d(xq,xi)d(\mathbf{x}_q, \mathbf{x}_i): distance from the query to the ii-th training point
  • nn: number of training points

which reads: "the subset SS of the nn training indices of size kk that minimizes the total distance from the query". In practice we never solve this as an optimization; we simply sort the distances and take the first kk. The arg min⁡\operatorname{arg\,min} notation is a compact description of the sorted order, not a computational instruction.

Two design choices are hidden inside this definition, and both matter:

  • Why fix the count kk instead of a radius? Because a fixed count lets the neighborhood radius adapt when local density varies. In a dense region, the kk nearest points sit within a small ball; in a sparse region, the same kk points spread out over a much larger ball. Fixing kk therefore lets the neighborhood adapt its radius to local density automatically. A fixed radius would instead yield many neighbors where data is dense and too few, or none, where it is sparse.
  • Why rely on locality at all? The locality heuristic is that nearby inputs have similar conditional means or class probabilities in the chosen representation. It motivates local averaging or voting; noise still permits different labels at identical inputs.

The neighborhood depends on the query, the stored training inputs, the metric, and the tie rule. Neighbor lookup is performed for each new query; this is the sense in which kNN is instance-based. There is no fitted global parametric curve to inspect; each prediction requires a local comparison against the stored examples. Ties are the one wrinkle: when two training points are exactly equidistant, the sorted order must break the tie somehow. We return to that in the worked example.

One more design choice deserves a note:

  • Distance metric: Euclidean distance suits comparably scaled, roughly symmetric continuous features; Manhattan distance gives large coordinate discrepancies a different relative influence; cosine distance can suit nonzero sparse or text-derived vectors when direction is more relevant than magnitude. The metric defines the neighborhood, so record and reuse it consistently.

Classification votes and regression averages

Once the neighborhood Nk\mathcal{N}_k exists, the prediction is a reduction over it. The reduction depends on whether the target is categorical or numeric, and the distinction deserves its own heading because it changes the interpretation even though the machinery stays the same.

For classification with CC classes, each neighbor casts a vote for its own label. The predicted class is the one receiving the most votes:

y^q=arg max⁡c∈{1,…,C}∑i∈Nk(xq)1[yi=c]\hat{y}_q = \operatorname*{arg\,max}_{c \in \{1,\dots,C\}} \sum_{i \in \mathcal{N}_k(\mathbf{x}_q)} \mathbb{1}[y_i = c]

where:

  • y^q\hat{y}_q: the predicted class for the query
  • cc: a class label in {1,…,C}\{1, \dots, C\}
  • yiy_i: the label of the ii-th neighbor
  • 1[⋅]\mathbb{1}[\cdot]: indicator function (1 when the condition is true, 0 otherwise)

We can also divide the count by kk to obtain the local class probability p^(c∣xq)\hat{p}(c \mid \mathbf{x}_q):

p^(c∣xq)=1k∑i∈Nk(xq)1[yi=c].\hat{p}(c \mid \mathbf{x}_q) = \frac{1}{k} \sum_{i \in \mathcal{N}_k(\mathbf{x}_q)} \mathbb{1}[y_i = c].

The indicator function 1[yi=c]\mathbb{1}[y_i = c] takes value 11 when the ii-th neighbor's label equals class cc, and 00 otherwise. The inner sum therefore counts how many of the kk neighbors are of class cc, and arg max⁡c\operatorname*{arg\,max}_c selects the class with the largest count. If we also divide by kk, the inner expression becomes the local class probability p^(c∣xq)\hat{p}(c \mid \mathbf{x}_q). That probability is not required for the hard label, but it is useful for ranking predictions or for calibration.

For regression, each neighbor contributes its numeric label, and the prediction is the local average:

y^q=1k∑i∈Nk(xq)yi\hat{y}_q = \frac{1}{k} \sum_{i \in \mathcal{N}_k(\mathbf{x}_q)} y_i

where:

  • y^q\hat{y}_q: the predicted numeric value for the query
  • yiy_i: the numeric target of the ii-th neighbor
  • kk: number of neighbors in the neighborhood

The average is the continuous analogue of the majority vote: instead of selecting a discrete class with the most votes, we select the value that minimizes the squared error among the neighbors. That connection is worth remembering: the sum of squared errors to the local neighbors is minimized by their mean. To see why, minimize

SSE(c)=∑i∈Nk(xq)(yi−c)2\mathrm{SSE}(c) = \sum_{i \in \mathcal{N}_k(\mathbf{x}_q)} (y_i - c)^2

with respect to the constant cc. Taking the derivative and setting it to zero,

ddc SSE(c)=∑i∈Nk(xq)−2(yi−c)=−2∑i∈Nk(xq)yi+2kc=0,\begin{aligned} \frac{d}{dc}\,\mathrm{SSE}(c) &= \sum_{i \in \mathcal{N}_k(\mathbf{x}_q)} -2(y_i - c) \\ &= -2 \sum_{i \in \mathcal{N}_k(\mathbf{x}_q)} y_i + 2kc \\ &= 0, \end{aligned}

so c=1k∑i∈Nk(xq)yic = \frac{1}{k}\sum_{i \in \mathcal{N}_k(\mathbf{x}_q)} y_i, which is exactly the local average above. This is the same logic that underlies ordinary least squares in the earlier OLS chapter. Local averaging is essentially OLS with an implicit neighborhood restriction: one tiny least-squares fit per query, using only the kk nearest examples.

Both rules are the same weighted sum in disguise. Stack the labels into a vector y\mathbf{y} and let W\mathbf{W} be a sparse matrix whose qq-th row is nonzero only at the indices of the neighborhood of query qq:

y^=Wy,Wqi=1k1[i∈Nk(xq)],\hat{\mathbf{y}} = \mathbf{W} \mathbf{y}, \qquad \mathbf{W}_{qi} = \frac{1}{k} \mathbb{1}[i \in \mathcal{N}_k(\mathbf{x}_q)],

where:

  • y^\hat{\mathbf{y}}: vector of predictions for all queries
  • y∈Rn\mathbf{y} \in \mathbb{R}^n: vector of training labels
  • W\mathbf{W}: sparse weight matrix whose qq-th row selects the neighborhood of query qq
  • Wqi\mathbf{W}_{qi}: weight of training point ii for query qq
  • kk: number of neighbors in the neighborhood

In regression, W\mathbf{W} multiplies the numeric labels; in classification, the same structure applied to one-hot encoded labels reproduces voting exactly. This "weight" view is a useful bridge to the next subsection, where the weights are no longer forced to be equal.

Neighborhood size, weighting, and scaling

Three knobs determine how kNN behaves, and they are easiest to understand in the order they were introduced.

The size kk. At k=1k = 1, every prediction equals the label of the single closest training point. The decision boundary can then trace every jagged detail of the training set with low bias and high variance. Small perturbations of the data, or of the query, can flip the prediction to a completely different neighbor. As kk grows, voting or averaging uses a larger neighborhood. This can reduce sensitivity to noise while blurring local changes, but boundaries and conditional bias need not change monotonically for every dataset. For independent equal-variance regression errors at fixed training inputs, the noise variance of a uniform kk-neighbor average is the common error variance divided by kk. With uniform weights, the extreme k=nk = n makes every query use the full training set: classification predicts a most common class, using the chosen tie rule if needed, and regression predicts the global mean. Distance weights can still produce query-dependent predictions at k=nk=n. At k=nk=n, the selected neighborhood contains every training example, but distance weights can still concentrate influence near the query. That limit is a useful sanity check: both neighborhood size and weighting affect how strongly nearby examples influence the prediction.

The weighting. Uniform voting treats all kk neighbors equally, even when some are much closer than others. Distance weighting lets proximity affect their influence. The weighted prediction is

y^q=∑i∈Nk(xq)wiyi∑i∈Nk(xq)wi,wi=1d(xq,xi)2\hat{y}_q = \frac{\sum_{i \in \mathcal{N}_k(\mathbf{x}_q)} w_i y_i}{\sum_{i \in \mathcal{N}_k(\mathbf{x}_q)} w_i}, \qquad w_i = \frac{1}{d(\mathbf{x}_q, \mathbf{x}_i)^2}

where:

  • y^q\hat{y}_q: the distance-weighted prediction for the query
  • yiy_i: the label or target of the ii-th neighbor
  • wiw_i: the weight assigned to the ii-th neighbor, inversely proportional to squared distance
  • d(xq,xi)d(\mathbf{x}_q, \mathbf{x}_i): distance from the query to the ii-th neighbor

The weights wiw_i are larger for close neighbors and smaller for distant ones. When all distances are equal, the formula reduces to the simple mean, so weighting is a strict generalization rather than a different algorithm. Closer training points effectively get more votes. This can reduce the influence of more distant targets near a boundary or an unevenly sampled region, but it also concentrates weight on fewer observations and can amplify noise. Whether predictions improve should be checked on held-out data. This chapter uses inverse-squared weights for positive distances; inverse distance is another common choice and is scikit-learn’s built-in weights="distance" convention. For a zero-distance query, we average only the exact matches. For weighted classification, sum the weights separately for each class and select the largest class total.

The scaling. Feature scaling can substantially change kNN neighborhoods when coordinate ranges differ. As described earlier in the standardization chapter, large differences in feature scale can let one coordinate dominate raw distances. If differences in one feature are much larger than those in another, their squared contributions can largely determine the neighbor ranking. Choose scaling so that feature differences have the intended influence on distance, and fit any learned scaling parameters on the training set only. This is the same data leakage discipline described in the validation chapter: computing a mean or standard deviation using the test set and then using those values to scale the training set leaks test-distribution information into preprocessing. The resulting score no longer evaluates a procedure fitted solely on the training data; the direction of the score difference is not guaranteed in every realized split. Any chosen scaling transform becomes part of the distance definition and must also be applied to future queries.

Computational cost and the curse of dimensionality

The cost of kNN has two faces: computation and geometry. They are easy to confuse.

The computational face is straightforward. Computing all brute-force distances requires O(np)O(np) operations per query. Our full-sort implementation adds O(nlog⁡n)O(n\log n) neighbor-ordering work per query, giving O(mn(p+log⁡n))O(mn(p+\log n)) work for mm queries, before vote aggregation. A partial-selection implementation can avoid sorting all nn distances. This repeated distance calculation and sorting make our implementation expensive at inference. Our brute-force implementation scans every stored training point for each query. Exact or approximate search indexes can reduce this search work. Dimensionality reduction can reduce the work per distance, but brute-force prediction still scans the transformed training set. Exact trees trade construction and storage costs for query pruning while preserving the neighbor set. Approximate search can change that set; dimensionality reduction changes the feature representation and can change its distances. Evaluate suitability from measured storage, index-building, and query costs under a representative workload.

The geometric face is the curse of dimensionality, and it is more fundamental. Under the independent centered Gaussian feature-difference model below, with fixed variance σ2>0\sigma^2>0, query-to-training distances concentrate in relative terms as pp grows. To see the effect quantitatively, consider the squared Euclidean distance between a query and a training point,

∥xq−xi∥22=∑j=1p(xqj−xij)2\|\mathbf{x}_q - \mathbf{x}_i\|_2^2 = \sum_{j=1}^{p} (x_{qj} - x_{ij})^2

where:

  • xqjx_{qj}: the jj-th feature of the query point
  • xijx_{ij}: the jj-th feature of the ii-th training point
  • pp: number of features
  • ∥⋅∥22\|\cdot\|_2^2: squared Euclidean (L2) norm

If the feature differences xqj−xijx_{qj} - x_{ij} are independent centered Gaussian variables with variance σ2\sigma^2, then the sum of their pp squares has mean pσ2p\sigma^2 and variance 2pσ42p\sigma^4. The standard deviation of the squared distance is therefore 2pσ2\sqrt{2p}\sigma^2, which grows more slowly than the mean pσ2p\sigma^2. Their ratio,

2pσ2pσ2=2p,\frac{\sqrt{2p}\sigma^2}{p\sigma^2} = \frac{\sqrt{2}}{\sqrt{p}},

shrinks as 1/p1/\sqrt{p}, so the squared distance concentrates around its mean as pp grows. Taking the square root carries this concentration over to the distances themselves. For a fixed finite number of training points under this independent Gaussian model, the nearest-to-farthest distance ratio approaches one in probability as pp grows. Correlated features, a low-dimensional representation, or a training-set size that grows with pp require separate analysis.

The concentration argument can be checked numerically. The next simulation draws independent standard normal training and query points, computes the nearest-to-farthest distance ratio for each query, and averages those ratios across queries at each dimension.

PythonIn [3]
CollapseExpand
# Distance-concentration simulation: independent standard normal features,
# 400 training points, and 50 queries per dimension. The plotted value is the
# mean over queries of nearest distance divided by farthest distance.
concentration_dims = np.array([2, 4, 8, 16, 32, 64, 128, 256, 512])
concentration_train_n = 400
concentration_query_n = 50
concentration_seed = 2024
concentration_rng = np.random.default_rng(concentration_seed)
concentration_ratios = []

for p in concentration_dims:
    X_sim_train = concentration_rng.normal(size=(concentration_train_n, int(p)))
    X_sim_query = concentration_rng.normal(size=(concentration_query_n, int(p)))
    diffs = X_sim_query[:, None, :] - X_sim_train[None, :, :]
    dists = np.sqrt((diffs**2).sum(axis=2))
    ratios = dists.min(axis=1) / dists.max(axis=1)
    concentration_ratios.append(float(ratios.mean()))

concentration_ratios = np.array(concentration_ratios)
VisualizationOut [4]
CollapseExpand
Nearest-to-farthest distance ratio versus dimension under Gaussian features.
Distance concentration under independent Gaussian features. Each point is the mean over 50 queries of the nearest-to-farthest distance ratio among 400 training points. The ratio rises toward one as dimension grows, so nearby and faraway points become harder to distinguish.

Practically, this means:

  • In high dimensions, kNN may appear to have many "close" neighbors that are far away, causing predictions to be dominated by seemingly unrelated points.
  • Feature aggregation does not rescue the situation unless the resulting representation preserves information about the target. Extra noise coordinates can weaken the relative distance contrast.
  • In high-dimensional settings, kNN is often combined with feature selection or with a learned low-dimensional representation before prediction.

Mathematical Properties

The kNN predictor is closely related to a classical non-parametric estimator. For a concrete regression result, suppose the training observations are iid, the feature domain is bounded, and Y=f(X)+ϵY=f(X)+\epsilon with ff Lipschitz with constant LL. Conditional on the training inputs, assume the errors are independent, have mean zero, and have common finite variance σ2\sigma^2. Conditional on the training inputs, the uniform kNN average has noise variance σ2/k\sigma^2/k and absolute bias bounded by LRk(x)L R_k(x), where Rk(x)R_k(x) is the distance to the kk-th neighbor. At a query in the distributional support, k→∞k\to\infty and k/n→0k/n\to0 make its mean squared estimation error tend to zero. With the additional local mass condition P(∥X−x∥≤r)≥crpP(\|X-x\|\le r)\ge c r^p for a positive constant cc and sufficiently small rr, a representative bound is O((k/n)2/p+1/k)O((k/n)^{2/p}+1/k); balancing the terms gives O(n−2/(p+2))O(n^{-2/(p+2)}). This regularity-based bound is slower than the O(n−1)O(n^{-1}) estimation-error rate of regular, correctly specified finite-dimensional parametric models; it is not a claim that every individual kNN problem converges strictly more slowly. For classification, Cover and Hart’s large-sample result bounds the error of the one-nearest-neighbor rule by twice the Bayes error under their sampling and regularity conditions. The Bayes error is a property of the underlying distribution, not of the fitted classifier. This bound does not imply that another classifier must outperform nearest neighbors.

The relationship between uniform and weighted kNN also has a precise form. Under kernel weighting, the local estimator is a Nadaraya-Watson-type kernel regression estimator, and the weights satisfy ∑iwi=1\sum_i w_i = 1 when normalized. Nonnegative weights with a positive total, normalized to sum to one, keep regression predictions within the convex hull of the neighbor targets. Dividing all class-vote totals by that same positive total leaves their ranking unchanged. These connections show that kNN is a non-parametric estimator with asymptotic results under stated sampling and regularity conditions.

Comments

Add to the conversation

A question, a correction, or an example from your own work.

No comments yet. Be the first to share your thoughts!

Newsletter

Stay up to date

Get new articles, book updates, and notes on AI and technology.

Unsubscribe at any time. Privacy policy