Math for AI course · Lesson 43 of 180

Distance depends on the chosen geometry

Derive distance from a norm, compare a tiny nearest-neighbor search under L1, L2, and L-infinity, and debug metric choices in AI systems.

Share this article

When a support-ticket router, recommender, or retrieval system looks for the “closest” stored example, it is making a geometric choice. A point that is closest when every coordinate contributes separately can lose to a different point when the coordinates combine by squared length. The algorithm did not become inconsistent; the meaning of “close” changed.

By the end of this lesson, you will be able to derive a distance from a norm, calculate a small nearest-neighbor ranking under several metrics, and identify the preprocessing and tie-handling decisions that make that ranking meaningful.

Learning outcome: Derive distance from a norm and compare nearest neighbors.

This lesson builds on Lesson 040: L1, L2, and L-infinity See Size Differently, which introduced several ways to measure the size of one vector. Section 3.3 of the official Mathematics for Machine Learning book supplies the mathematical reference. The dataset, calculations, code, visual, and assessments here are original.

The closest point starts with a displacement

Let x=(x1,…,xn)x=(x_1,\ldots,x_n) and y=(y1,…,yn)y=(y_1,\ldots,y_n) be two points in Rn\mathbb{R}^n. Each coordinate is a feature position; nn is the number of features. To ask how far xx is from yy, first subtract coordinate by coordinate:

x−y=(x1−y1,…,xn−yn).x-y=(x_1-y_1,\ldots,x_n-y_n).

This difference is a displacement vector. It says how much one point must move in each coordinate to land on the other. The order changes the sign of the displacement, but a norm gives the same size either way because ∥v∥=∥−v∥\lVert v\rVert=\lVert -v\rVert.

The names describe different movement rules. L1, often called Manhattan distance, adds the amount moved in every coordinate. L2 measures the straight line length of the displacement. L-infinity only asks for the largest single coordinate movement. A model that uses one of these measures is declaring which kinds of movement count as expensive.

The Section 3.3 reference also connects an inner product to a norm through ∥v∥=⟨v,v⟩\lVert v\rVert=\sqrt{\langle v,v\rangle}. With the usual dot product, this is the Euclidean norm, so d2(x,y)=⟨x−y,x−y⟩d_2(x,y)=\sqrt{\langle x-y,x-y\rangle}. A useful subtlety is that the distance construction works for any norm: L1 is a valid norm and induces a valid metric even though it is not induced by an inner product in the usual real-vector-space sense.

Why subtraction must happen first

The distance compares two locations through one displacement. It is not the difference between their separate sizes. For example, if x=(1,1)x=(1,1) and y=(1,−1)y=(1,-1), both points have L2 norm 2\sqrt{2}, but

x−y=(0,2),d2(x,y)=02+22=2.x-y=(0,2),\qquad d_2(x,y)=\sqrt{0^2+2^2}=2.

Computing ∥x∥2−∥y∥2=0\lVert x\rVert_2-\lVert y\rVert_2=0 would erase the fact that the points sit two units apart vertically. The safe mental recipe is subtract, then apply the chosen norm.

The same points can have different nearest neighbors

Consider a query point q=(0,0)q=(0,0) and three candidate records in a two-feature representation. The coordinates are deliberately small and unitless: they stand for values after some chosen feature representation, not for a universal notion of similarity.

PointDescriptionCoordinates
AAaxis-heavy case(6,0)(6,0)
BBbalanced case(4,4)(4,4)
CCmixed case(5,2)(5,2)

The ranking changes because the norms reward different shapes of displacement. L1 favors the axis-heavy point AA here because it has the smallest total coordinate movement. L2 picks CC: its two coordinates combine to a shorter straight-line segment than BB‘s diagonal and AA‘s long horizontal segment. L-infinity picks BB because no individual coordinate moves by more than 44.

One query, three neighbor maps

The table is the complete semantic fallback: smaller distances rank closer to the query q = (0, 0).
CandidateCoordinatesL1sum of absolute movesL2straight-line lengthL-infinitylargest coordinate move
A axis-heavy(6, 0)6.000rank 16.000rank 36.000rank 3
B balanced(4, 4)8.000rank 35.657rank 24.000rank 1
C mixed(5, 2)7.000rank 25.385rank 15.000rank 2
Two nearest candidatesA → CC → BB → C

Numbers inside the circles give the rank for that metric. The dashed segment points to the nearest candidate; the first two ranks are outlined so the map does not rely on color alone.

One query, three neighbor mapsFor L1, the order is A then C then B. For L2, it is C then B then A. For L-infinity, it is B then C then A. The query is at the origin in every panel.L10246024feature 1feature 2query qA1B3C2L20246024feature 1feature 2query qA3B2C1L-infinity0246024feature 1feature 2query qA3B1C2

For L1, the order is A then C then B. For L2, it is C then B then A. For L-infinity, it is B then C then A. The query is at the origin in every panel.

The geometry changes the neighbor map even though the query and candidate coordinates stay fixed.

The table and rank labels make the conclusion available without reading the SVG. The visual adds one spatial cue: the dashed segment changes endpoint when the metric changes, while the coordinates themselves remain fixed.

The NumPy numpy.linalg.norm documentation exposes the two controls needed here: ord chooses the norm order and axis chooses which coordinate axis to reduce. A batch of three candidate rows is therefore handled by subtracting the query from every row and reducing each row separately.

The following code mirrors the derivation rather than hiding it in a nearest neighbor object. It was executed with NumPy; the printed ordering is the same as the worked example.

import numpy as np

query = np.array([0.0, 0.0])
points = np.array([[6.0, 0.0], [4.0, 4.0], [5.0, 2.0]])
labels = np.array(["A", "B", "C"])

difference = points - query
for name, order in [("L1", 1), ("L2", 2), ("L-infinity", np.inf)]:
    distances = np.linalg.norm(difference, ord=order, axis=1)
    ranked = np.argsort(distances, kind="stable")
    print(
        f"{name}: "
        + ", ".join(f"{labels[i]}={distances[i]:.3f}" for i in ranked)
    )
L1: A=6.000, C=7.000, B=8.000
L2: C=5.385, B=5.657, A=6.000
L-infinity: B=4.000, C=5.000, A=6.000

There are three separate operations worth noticing:

  1. points - query performs the subtraction before any norm is applied.
  2. axis=1 computes one norm per row, so each candidate gets one distance.
  3. argsort turns distances into a ranking; the norm itself does not decide how to handle equal values.

For a larger data set, scikit-learn’s NearestNeighbors class packages the same idea. Its metric="minkowski" setting uses the parameter p, with p=1p=1 corresponding to L1 and p=2p=2 to L2; its kneighbors method returns both distances and indices. The estimator is useful when the search should be integrated into a model pipeline, but the NumPy version is the better debugging tool because every intermediate array is visible.

For this exact example, the equivalent search configuration is:

from sklearn.neighbors import NearestNeighbors

search = NearestNeighbors(n_neighbors=2, metric="minkowski", p=2)
search.fit(points)
distances, indices = search.kneighbors(query.reshape(1, -1))
nearest_labels = labels[indices[0]]

Changing p to 1 or np.inf changes the geometry while keeping the data and the value of k=2k=2 fixed. The API does not make the metric choice disappear; it makes that choice explicit in a parameter.

Boundaries that change the answer

Distance is precise only after the feature space, metric, and search policy are specified. These are the cases most likely to make a small nearest-neighbor demo misleading.

Ties are information, not noise

Let q=(0,0)q=(0,0), u=(1,1)u=(1,1), and v=(2,0)v=(2,0). Both candidates have

d1(q,u)=∣1∣+∣1∣=2,d1(q,v)=∣2∣+∣0∣=2.d_1(q,u)=|1|+|1|=2, \qquad d_1(q,v)=|2|+|0|=2.

If k=1k=1, the mathematics supplies a tie, not a unique winner. A production system should state a deterministic secondary key, return all tied candidates, or use a policy appropriate to its application. The stable sort in the NumPy code makes repeated runs reproducible, but it does not prove that the first tied row is more similar.

Identical points have distance zero

If x=yx=y, then x−y=(0,…,0)x-y=(0,\ldots,0) and every norm gives d(x,y)=0d(x,y)=0. That is a valid exact match, not an error. In a training-set lookup, however, querying a row against the same fitted collection can return the row itself as the nearest neighbor. Decide whether self-matches are useful before interpreting a result; an external query that happens to duplicate a stored row should still receive distance zero.

Feature scaling is part of the geometry

Suppose one coordinate is annual income in dollars and another is a count from zero to ten. The dollar coordinate can dominate an L1 or L2 distance simply because its numeric unit is larger. Standardizing, rescaling, or assigning domain weights changes the geometry; it is not a cosmetic prelude. Fit any data-dependent scaling rule on the training data and apply the same rule to queries, or the neighbor map can leak information from the evaluation set.

Distance and cosine similarity answer different questions

Distance compares the displacement between two points and therefore includes their positions and magnitudes. Cosine similarity compares direction after normalizing lengths. For x=(1,1)x=(1,1) and y=(10,10)y=(10,10),

cos⁡(x,y)=xTy∥x∥2∥y∥2=1,\cos(x,y)=\frac{x^\mathsf{T}y}{\lVert x\rVert_2\lVert y\rVert_2}=1,

because the vectors point in exactly the same direction, while

d2(x,y)=∥x−y∥2=(−9)2+(−9)2=92≈12.728.d_2(x,y)=\lVert x-y\rVert_2 =\sqrt{(-9)^2+(-9)^2}=9\sqrt{2}\approx12.728.

An embedding-retrieval system may prefer cosine when direction is the intended notion of match. A scale-sensitive application may prefer a norm-induced distance. Neither choice is universally correct, and cosine similarity needs a separate policy for a zero vector because its direction is undefined.

High dimension calls for measurement, not a slogan

In some high-dimensional data distributions, nearest and farthest distances can become closer together, making the ranking less discriminative. Beyer, Goldstein, Ramakrishnan, and Shaft describe this distance-concentration result under specific conditions in their nearest-neighbor paper. That result is a warning about data and workload assumptions, not a law that every embedding space becomes useless. Inspect neighbor-distance gaps, validate the metric on held-out queries, and consider whether the representation contains many irrelevant coordinates before blaming the search algorithm.

The next lesson isolates the directional comparison that distance deliberately mixes with length: Lesson 044, Angles and Cosine Similarity. The Math for AI course page will show that lesson when its translation set is live.

Check your understanding

Question 1

For x = (5, −2) and y = (1, 1), compute the displacement and the L1, L2, and L-infinity distances.

Show the step-by-step solution

First subtract the points: x−y=(5−1,−2−1)=(4,−3)x-y=(5-1, -2-1)=(4,-3). Apply each norm to that one displacement. L1 is ∣4∣+∣−3∣=4+3=7|4|+|-3|=4+3=7. L2 is sqrt(4² + (−3)²) = sqrt(16 + 9) = 5. L-infinity is the largest absolute coordinate, max⁡(4,3)=4\max(4,3)=4. The three distances are 7, 5, and 4 respectively.

Question 2

Why is ||x||₂ − ||y||₂ not a general formula for the distance between x and y? Demonstrate with x = (1, 1) and y = (1, −1).

Show the step-by-step solution

The separate lengths are both ∥x∥2=12+12=2\lVert x\rVert_2=\sqrt{1^{2} + 1^{2}} =\sqrt2 and ∥y∥2=12+(−1)2=2\lVert y\rVert_2=\sqrt{1^{2} + (-1)^{2}}=\sqrt2, so their difference is zero. The actual displacement is x−y=(0,2)x-y=(0,2), whose L2 norm is 02+22=2\sqrt{0^{2} + 2^{2}}=2. Distance must subtract first because it measures the vector connecting the two locations, not the difference between their distances from the origin.

Question 3

Using q = (0, 0), A = (6, 0), B = (4, 4), and C = (5, 2), which two candidates are nearest under L-infinity? Show all three relevant distances.

Show the step-by-step solution

Subtracting qq leaves the candidate coordinates. The L-infinity distance keeps the largest absolute coordinate: d∞(q,A)=max⁡(6,0)=6d_\infty(q,A)=\max(6,0)=6, d∞(q,B)=max⁡(4,4)=4d_\infty(q,B)=\max(4,4)=4, and d∞(q,C)=max⁡(5,2)=5d_\infty(q,C)=\max(5,2)=5. Sorting 4, 5, and 6 gives BB first and CC second, so the two-neighbor result is [B,C][B,C].

Question 4

Under L1, q = (0, 0), u = (1, 1), and v = (2, 0) tie. What is the tie distance, and what must a k = 1 implementation decide?

Show the step-by-step solution

Compute each displacement from the origin. For uu, L1 gives ∣1∣+∣1∣=2|1|+|1|=2; for vv, it gives ∣2∣+∣0∣=2|2|+|0|=2. The tied distance is therefore 2. A k=1k=1 implementation must choose a policy: return both and let a later step resolve the tie, use a documented secondary key, or use an application rule. An array order may make the output deterministic, but it does not make one point mathematically closer.

Question 5

If a query x and stored point y are both (2, −1), what is their distance under every norm, and why is that result meaningful?

Show the step-by-step solution

The displacement is x−y=(2−2,−1−(−1))=(0,0)x-y=(2-2,-1-(-1))=(0,0). Every norm maps the zero vector to zero, so d(x,y)=0d(x,y)=0 for L1, L2, and L-infinity. This is the identity-of-indiscernibles property: identical points have no displacement. It is meaningful as an exact match, although a search over the training set may need an explicit self-match policy.

Question 6

Why can an unscaled feature dominate distance? Compare q = (0, 0), a = (1, 100), and b = (2, 0) with L2 distance.

Show the step-by-step solution

The displacements are a−q=(1,100)a-q=(1,100) and b−q=(2,0)b-q=(2,0). Their L2 distances are 12+1002=10001≈100.005\sqrt{1^{2} + 100^{2}}=\sqrt{10001}\approx100.005 and 22+02=2\sqrt{2^{2} + 0^{2}}=2. The second coordinate makes aa look much farther even though its first coordinate is closer. Rescaling or standardizing features can make the coordinate contributions comparable, but that preprocessing rule must be fitted on training data and reused consistently.

Question 7

For x = (1, 1) and y = (10, 10), why can cosine similarity be 1 while L2 distance is not 0?

Show the step-by-step solution

The dot product is 1⋅10+1⋅10=201\cdot10+1\cdot10=20. The lengths are ∥x∥2=2\lVert x\rVert_2=\sqrt2 and ∥y∥2=102\lVert y\rVert_2=10\sqrt2, so cosine similarity is 20/(2⋅102)=120/(\sqrt2\cdot10\sqrt2)=1: the directions agree exactly. Distance keeps the magnitude difference. The displacement is (−9,−9)(-9,-9), so d2(x,y)=81+81=162=92≈12.728d_2(x,y)=\sqrt {81 + 81}=\sqrt{162}=9\sqrt2\approx12.728. The two measures answer different similarity questions.

Question 8

In np.linalg.norm(points − query, ord=2, axis=1), what does axis=1 mean when points has shape (number_of_points, number_of_features)?

Show the step-by-step solution

Each row of points − query is one candidate displacement, and each column is one feature coordinate. Setting axis=1 tells NumPy to reduce across the columns of each row. The result therefore has one L2 value per candidate, with shape (number_of_points,). Reducing along the wrong axis would mix candidates together or return one value per feature instead of a usable neighbor ranking.

Sources

  1. Mathematics for Machine Learning book PDF, Section 3.3
  2. NumPy documentation: numpy.linalg.norm
  3. scikit-learn documentation: NearestNeighbors
  4. scikit-learn documentation: pairwise metrics, affinities, and kernels
  5. Beyer, Goldstein, Ramakrishnan, and Shaft: When Is Nearest Neighbor Meaningful?