k-Nearest Neighbors › Example

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

Section 6 of 10 · 4 min read

k-Nearest Neighbors › ExampleSection view · Sections in this chapter6 / 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

Before trusting code, it is worth carrying out one prediction entirely by hand. A single worked example with k=3k=3 confirms that every operation (distance, ranking, tie-breaking, voting, averaging) is exactly what we think it is, and it exposes the one place where the rules leave us a choice.

Suppose a two-dimensional binary classification training set contains five points:

(x1,y1)=((1,1),0),(x2,y2)=((2,3),0),(x3,y3)=((3,3),1),(\mathbf{x}_1, y_1) = ((1, 1), 0), \quad (\mathbf{x}_2, y_2) = ((2, 3), 0), \quad (\mathbf{x}_3, y_3) = ((3, 3), 1), (x4,y4)=((4,4),1),(x5,y5)=((5,5),1).(\mathbf{x}_4, y_4) = ((4, 4), 1), \quad (\mathbf{x}_5, y_5) = ((5, 5), 1).

The query is xq=(2.5,2.5)\mathbf{x}_q = (2.5, 2.5). Class-0 points sit near the origin, and class-1 points form a diagonal ridge going out toward (5,5)(5, 5), so the query lies between the two groups and the class votes are close. That is deliberate: an ambiguous query is exactly where the mechanics become visible.

Step 1: compute Euclidean distances to every training point.

For x1\mathbf{x}_1:

d(xq,x1)=(2.5−1)2+(2.5−1)2=2.25+2.25=4.5≈2.121.d(\mathbf{x}_q, \mathbf{x}_1) = \sqrt{(2.5 - 1)^2 + (2.5 - 1)^2} = \sqrt{2.25 + 2.25} = \sqrt{4.5} \approx 2.121.

For x2\mathbf{x}_2:

d(xq,x2)=(2.5−2)2+(2.5−3)2=0.25+0.25=0.5≈0.707.d(\mathbf{x}_q, \mathbf{x}_2) = \sqrt{(2.5 - 2)^2 + (2.5 - 3)^2} = \sqrt{0.25 + 0.25} = \sqrt{0.5} \approx 0.707.

For x3\mathbf{x}_3:

d(xq,x3)=(2.5−3)2+(2.5−3)2=0.25+0.25=0.5≈0.707.d(\mathbf{x}_q, \mathbf{x}_3) = \sqrt{(2.5 - 3)^2 + (2.5 - 3)^2} = \sqrt{0.25 + 0.25} = \sqrt{0.5} \approx 0.707.

For x4\mathbf{x}_4:

d(xq,x4)=(2.5−4)2+(2.5−4)2=2.25+2.25=4.5≈2.121.d(\mathbf{x}_q, \mathbf{x}_4) = \sqrt{(2.5 - 4)^2 + (2.5 - 4)^2} = \sqrt{2.25 + 2.25} = \sqrt{4.5} \approx 2.121.

For x5\mathbf{x}_5:

d(xq,x5)=(2.5−5)2+(2.5−5)2=6.25+6.25=12.5≈3.536.d(\mathbf{x}_q, \mathbf{x}_5) = \sqrt{(2.5 - 5)^2 + (2.5 - 5)^2} = \sqrt{6.25 + 6.25} = \sqrt{12.5} \approx 3.536.

The distances already show the ranking: x2\mathbf{x}_2 and x3\mathbf{x}_3 are closest and exactly tied, x1\mathbf{x}_1 and x4\mathbf{x}_4 form the next tier, and x5\mathbf{x}_5 is farthest. How we resolve the ties will decide which points enter the neighborhood, so it is worth handling them explicitly.

Step 2: sort the distances in increasing order:

0.707  (x2),0.707  (x3),2.121  (x1),2.121  (x4),3.536  (x5).0.707\;(\mathbf{x}_2), \quad 0.707\;(\mathbf{x}_3), \quad 2.121\;(\mathbf{x}_1), \quad 2.121\;(\mathbf{x}_4), \quad 3.536\;(\mathbf{x}_5).

Notice that x2\mathbf{x}_2 and x3\mathbf{x}_3 are exactly tied at 0.5\sqrt{0.5}, and x1\mathbf{x}_1 and x4\mathbf{x}_4 are tied at 4.5\sqrt{4.5}. Duplicate inputs or rounded and discrete features can produce exact ties in any dimension. This hand-designed coordinate set makes their effect visible. When they occur, we need a deterministic rule. We break equal-distance ties by training-row order, using np.argsort(..., kind="stable") in the implementation. If a class vote is tied, our classifiers choose the smallest class label. A different distance-tie rule can change the selected neighbor at the cutoff and therefore change the prediction.

Step 3: select the k=3k=3 nearest neighbors. The first three sorted indices are {2,3,1}\{2, 3, 1\}. Their labels are y2=0y_2 = 0, y3=1y_3 = 1, and y1=0y_1 = 0.

Step 4: count votes. Class 0 receives two votes and class 1 receives one vote. The majority class is therefore class 0, and our kNN prediction is y^q=0\hat{y}_q = 0.

We can now check what happens with weighted voting. Suppose we use wi=1/di2w_i = 1/d_i^2. The weights for the three neighbors are:

w2=10.5=2,w3=10.5=2,w1=14.5≈0.222.w_2 = \frac{1}{0.5} = 2, \quad w_3 = \frac{1}{0.5} = 2, \quad w_1 = \frac{1}{4.5} \approx 0.222.

where:

  • wiw_i: weight assigned to neighbor ii, equal to the reciprocal of its squared distance
  • did_i: Euclidean distance from the query to neighbor ii

Class 0 weight is w2+w1≈2.222w_2 + w_1 \approx 2.222; class 1 weight is w3≈2w_3 \approx 2. Weighted voting still predicts class 0, but the margin is now narrow. Uniform voting predicted class 0 by a 2-to-1 vote; weighted voting predicts it by 2.22 versus 2.00. This illustrates how sensitive predictions near the decision boundary can be: the same neighbor set can carry very different levels of confidence depending on how much each neighbor is allowed to count.

Step 5 (regression analogue). If the same five training points had numeric labels y=(0,0,1,1,1)y = (0, 0, 1, 1, 1) as targets for a regression task, uniform kNN with k=3k=3 would produce the local average

y^q=13(y2+y3+y1)=13(0+1+0)≈0.333.\hat{y}_q = \frac{1}{3}(y_2 + y_3 + y_1) = \frac{1}{3}(0 + 1 + 0) \approx 0.333.

This is the same set of neighbors, but now the answer is a continuous value rather than a class, and each observed label is contributing equally to the prediction. The result sits between the class-0 and class-1 values, which is what a local average should do. The uniform average is, in effect, the least-squares minimizer over the local neighborhood, the same justification used in the OLS chapter, restricted here to three nearby examples.

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