Reference: Distance Metrics
Distance metrics quantify how “far apart” two data points are. They are the backbone of KNN classification, k-means clustering, hierarchical clustering, anomaly detection, information retrieval, and most similarity-based recommender systems. The same pair of points can yield wildly different distance values depending on the metric you choose, and the “right” choice depends on the geometry of your data — not on habit.
Roster
| Metric | Formula (1-line) | Range | When to use | Used in corpus article |
|---|---|---|---|---|
| Euclidean (L2) | Continuous features of similar scale, isotropic geometry, the default for physical coordinates | Building your first RAG pipeline), SHAP values | ||
| Manhattan (L1, cityblock) | High-dim sparse data, grid-aligned motion, features with outliers (robust to large single-axis spikes) | KNN in high dimensions) | ||
| Minkowski (general Lp) | A family — is Euclidean, is Manhattan; tunable when you want something between | KNN in high dimensions) | ||
| Chebyshev (L∞, max) | Chessboard distance, worst-case per-axis deviation, log-normal or heavy-tailed per-axis errors | KNN in high dimensions) | ||
| Cosine | (often for non-negative) | Embeddings, text vectors, TF-IDF — direction matters more than magnitude | Building your first RAG pipeline), Vector databases compared) | |
| Jaccard | Sets, token n-grams, binary bags, deduplication | Building your first RAG pipeline) | ||
| Hamming | Categorical features, binary strings, error-correcting codes, edit-distance proxies | Building your first RAG pipeline) | ||
| Mahalanobis | Correlated, differently-scaled continuous features; multivariate outlier detection | Vector databases compared), SHAP values |
All metrics above except cosine, Jaccard, Hamming and Mahalanobis reduce to a “straight-line” or “axis-aligned” geometry. Cosine measures the angle between vectors (a similarity converted to a distance by 1 - cos). Jaccard and Hamming operate on sets and symbols respectively. Mahalanobis is the only one in the roster that accounts for feature covariance — it is Euclidean distance measured in a whitened, decorrelated space.
Decision tree: which distance for which data
Use this as a first-pass selector. When two leaves apply, prefer the one higher on the list.
- Is your data a set or bag of tokens?
- Yes → Jaccard. (Add a count weighting if you need it; that’s the generalized Jaccard / Tanimoto.)
- Is your data a fixed-length binary string or categorical one-hot vector?
- Yes → Hamming. (For strings of different lengths, use Levenshtein instead — Hamming requires equal-length inputs.)
- Is your data a high-dim embedding from a neural model (BERT, OpenAI, SBERT, …) or a TF-IDF vector?
- Yes, and the vectors are normalized (unit norm) → cosine or Euclidean — they give the same ranking under unit-norm normalization, but cosine is the convention.
- Yes, and you want to ignore magnitude (only direction matters) → cosine.
- Yes, and magnitude carries signal (e.g., tf-idf weighting reflects term importance) → Euclidean, or cosine on L2-normalized vectors which collapses to the same ranking.
- Are your features correlated and on different scales?
- Yes, and you have enough data to estimate a covariance matrix (≥ ~10× the dimension) → Mahalanobis.
- Yes, but you cannot estimate covariance reliably → standardize features, then use Euclidean (a poor-man’s Mahalanobis).
- Are your features independent, continuous, and roughly the same scale?
- Yes → Euclidean (the default; do not default to Manhattan out of habit).
- Are you in very high dimensions (> ~50 dense features, > ~5000 sparse)?
- Yes → see the curse-of-dimensionality sidebar below. Prefer cosine for sparse text, Manhattan for moderate-dim dense data, or fractional Lp with p < 1 if you must stay in the Minkowski family.
- Do you care only about the single worst-aligned axis (chessboard / Chebyshev metric)?
- Yes → Chebyshev.
- Do you want a tunable compromise between Manhattan and Euclidean?
- Yes → Minkowski with .
A worked Python example: one pair, eight measurements
Below, the same pair of points is measured with each of the eight core metrics. The point is not to compare the numbers head-to-head (they are not commensurable across metrics) — it is to show that a “distance” between two fixed points is a function of the metric, and that the choice has real downstream consequences for which neighbours KNN picks, which cluster k-means forms, and which docs a vector DB returns.
import numpy as np
from scipy.spatial.distance import (
euclidean, cityblock, minkowski, chebyshev, cosine,
jaccard, hamming, mahalanobis
)
# Two points in a 4-dim continuous space
x = np.array([1.0, 2.0, 0.0, 5.0])
y = np.array([3.0, 0.0, 0.0, 1.0])
# A toy covariance matrix (for Mahalanobis) — diagonal-ish, slightly correlated
S = np.array([
[2.0, 0.5, 0.0, 0.0],
[0.5, 1.0, 0.0, 0.0],
[0.0, 0.0, 0.5, 0.0],
[0.0, 0.0, 0.0, 3.0],
])
S_inv = np.linalg.inv(S)
print("Euclidean :", round(euclidean(x, y), 4)) # 4.899
print("Manhattan :", round(cityblock(x, y), 4)) # 8.0
print("Minkowski p=1.5:", round(minkowski(x, y, p=1.5), 4)) # 5.7135
print("Minkowski p=3 :", round(minkowski(x, y, p=3), 4)) # 4.3089
print("Chebyshev :", round(chebyshev(x, y), 4)) # 4.0
print("Cosine :", round(cosine(x, y), 4)) # 0.5381
print("Mahalanobis:", round(mahalanobis(x, y, S_inv), 4)) # 3.8048
# scipy's jaccard() expects boolean *vectors*, not Python sets directly —
# passing sets raises `ValueError: Input vector should be 1-D.` because
# np.asarray(some_set) wraps the whole set in a single 0-D object array.
# Encode each set as a boolean membership vector over the shared vocabulary:
A = set(['apple', 'pear', 'banana'])
B = set(['apple', 'kiwi', 'banana'])
vocab = sorted(A | B)
vec_a = np.array([w in A for w in vocab])
vec_b = np.array([w in B for w in vocab])
print("Jaccard (sets) :", round(jaccard(vec_a, vec_b), 4)) # 0.5
print("Jaccard (1 - IoU):", round(1 - len(A & B) / len(A | B), 4)) # 0.5
bin_x = np.array([1, 0, 1, 1, 0, 1])
bin_y = np.array([1, 1, 0, 1, 0, 1])
print("Hamming :", round(hamming(bin_x, bin_y), 4)) # 0.3333
The code block above does the same thing eight times: it picks two specific points x and y and asks each metric to report a distance. The eight outputs are not directly comparable to each other — the only fair comparison is the same metric applied to different pairs — but the exercise shows what each metric is sensitive to.
- Euclidean sums squared per-axis differences and takes the square root. Axis 0 contributes 4, axis 1 contributes 4, axis 2 contributes 0, axis 3 contributes 16; the squared sum is 24, and
sqrt(24) ≈ 4.899. Note how axis 3 dominates the squared budget — Euclidean is sensitive to the largest single-axis displacement. - Manhattan sums absolute per-axis differences directly:
|1-3| + |2-0| + |0-0| + |5-1| = 2 + 2 + 0 + 4 = 8. No squaring, so no axis dominates — the largest axis contributes its raw value, not its square. This is what makes Manhattan more robust to outliers and to high-dimensional sparsity. - Minkowski with p=1.5 is a tunable compromise: more axis-weighted than Manhattan, less than Euclidean (
5.7135, sitting between Manhattan’s8.0and Euclidean’s4.899). Minkowski with p=3 is more axis-dominant than Euclidean — it gives more weight to the largest axis, pushing the result toward Chebyshev (4.3089, between Euclidean’s4.899and Chebyshev’s4.0). - Chebyshev ignores everything except the single largest per-axis difference:
max(2, 2, 0, 4) = 4. It is the limit of Minkowski as . - Cosine ignores magnitude entirely. It computes
1 - (x · y) / (|x| |y|). Withx · y = 1·3 + 2·0 + 0·0 + 5·1 = 8,|x| = sqrt(30) ≈ 5.477,|y| = sqrt(10) ≈ 3.162, the cosine of the angle is8 / 17.318 ≈ 0.462, so the cosine distance is1 - 0.462 ≈ 0.538— matching the printed0.5381. - Mahalanobis plugs the difference vector
(x - y) = [-2, 2, 0, 4]into the inverse covariance.Σ⁻¹couples axes 0 and 1 (because the off-diagonal0.5entries inΣdo), so those two axes contribute jointly:Δ[:2] @ Σ⁻¹[:2,:2] @ Δ[:2] ≈ 9.143. Axis 2 contributes0² × 2.0 = 0(its displacement is zero, so its large inverse-variance weight doesn’t matter here). Axis 3 contributes4² × (1/3) ≈ 5.333— less than its raw squared value of 16 would suggest, becauseΣgives axis 3 a large variance (3.0) and Mahalanobis downweights high-variance axes. Summing:9.143 + 0 + 5.333 ≈ 14.476, andsqrt(14.476) ≈ 3.805— matching the printed3.8048. The full element-wise expansion is in the Math toggle below; the key intuition is that Mahalanobis re-scales each axis by its own typical spread and removes correlations between axes. - Jaccard is computed on the set view of the data, not the numeric view — scipy’s
jaccard()needs boolean vectors, so the code above first encodes each set as a boolean membership vector over the shared vocabulary{apple, banana, kiwi, pear}. WithA = {apple, pear, banana}andB = {apple, kiwi, banana}, the intersection is{apple, banana}(size 2) and the union is{apple, banana, kiwi, pear}(size 4), soJaccard = 1 - 2/4 = 0.5— matching both printed lines. - Hamming counts positions where two equal-length binary (or symbol) strings disagree, divided by the length. With
bin_x = [1,0,1,1,0,1]andbin_y = [1,1,0,1,0,1], the disagreements are at indices 1 and 2, so2 / 6 ≈ 0.3333.
The pattern to take away: the same two points look close under some metrics and far apart under others. A KNN classifier built with cosine will return a different set of neighbours than one built with Euclidean, on the same data.
Each metric above can be written as a function satisfying (for true metrics) the four axioms:
- Non-negativity:
- Identity:
- Symmetry:
- Triangle inequality:
Most of the roster above satisfies all four; cosine distance () satisfies (1), (2), (3) but violates the triangle inequality in general — it is a pseudometric on the unit sphere and a divergence off it. Jaccard distance is a true metric on sets. Hamming is a true metric on equal-length binary strings.
Minkowski family
| Plain English | Symbol | Python (scipy.spatial.distance) |
|---|---|---|
| The p-th power of per-axis differences | (x - y) ** p (element-wise) | |
| Sum across axes | np.sum(...) | |
| The p-th root of that sum | np.power(..., 1/p) or np.sqrt(...) for |
Special cases:
- : Manhattan (
cityblock) - : Euclidean (the default
euclidean) - : Chebyshev (
chebyshev = np.max(np.abs(x - y)))
For the Minkowski formula is not a metric (triangle inequality fails) — but the fractional Lp distances with are sometimes used in high-dimensional settings to counteract the curse of dimensionality (see Aggarwal, Hinneburg & Keim 2001).
Cosine
| Plain English | Symbol | Python equivalent |
|---|---|---|
| Dot product of and | np.dot(x, y) | |
| L2 norm of | np.linalg.norm(x) | |
| Cosine of the angle | np.dot(x, y) / (np.linalg.norm(x) * np.linalg.norm(y)) | |
| Cosine distance | 1 - np.dot(x, y) / (... norms ...) |
When and are both unit-norm, , and cosine distance and Euclidean distance give the same ranking because for unit vectors.
Jaccard
| Plain English | Symbol | Python equivalent |
|---|---|---|
| Set intersection | set(A) & set(B) | |
| Set union | set(A) | set(B) | |
| Jaccard similarity | len(A & B) / len(A | B) | |
| Jaccard distance | 1 - len(A & B) / len(A | B) |
For multiset / count vectors, the generalized Jaccard (Tanimoto coefficient) replaces the set cardinalities with .
Mahalanobis
| Plain English | Symbol | Python equivalent |
|---|---|---|
| Difference vector | delta = x - y | |
| Covariance matrix of the feature distribution | np.cov(X.T) for data matrix X | |
| Inverse covariance (a.k.a. precision matrix) | np.linalg.inv(Sigma) or np.linalg.pinv if singular | |
| Mahalanobis distance | np.sqrt(delta @ S_inv @ delta) |
Geometrically, Mahalanobis is Euclidean distance applied after whitening the data: if is the eigendecomposition, then , and the transform rescales each principal axis by . The result is a vector whose components are uncorrelated and unit-variance, and the Euclidean norm of equals .
A concrete worked value for the toy example in the code block: with and
we have
The full expansion splits into the coupled axis-0/axis-1 block ( — the off-diagonal entries in mean the inversion mixes those two axes, so the contribution of axis 0 is not simply ), plus the independent axis-2 term (), plus the independent axis-3 term ().
Curse of dimensionality sidebar
As the number of dimensions grows, all of the Minkowski-family distances converge on the same value for any pair of points drawn from the same distribution. The intuition:
- The expected squared Euclidean distance between two i.i.d. points in dimensions grows as .
- The variance of that distance also grows as .
- But the relative spread shrinks as .
So in very high dimensions, every pair of points is (in a normalized sense) about equally far from every other pair — the nearest neighbour and the farthest neighbour become nearly indistinguishable. KNN, hierarchical clustering, and most density-based methods become unreliable.
Mitigations:
- Reduce dimensionality first: PCA, UMAP, or autoencoders down to a few dozen dims before computing distances.
- Use cosine for sparse high-dimensional text: it normalizes out the magnitude that grows with .
- Use fractional Lp with (Aggarwal, Hinneburg & Keim 2001): the smaller , the more weight is given to close axes rather than far axes, partially counteracting the concentration effect.
- Use feature selection to drop irrelevant dimensions — a noisy feature in high-dim data is worse than no feature at all, because it adds distance without adding signal.
- Accept it: some methods (random forests, gradient-boosted trees, linear models with regularization) do not depend on distances at all and bypass the issue entirely.
For a deeper treatment, see the KNN/curse-of-dimensionality article in the corpus.
Edge cases + common mistakes
Scale, magnitude, and covariance issues
1. Euclidean on un-scaled features. If feature A ranges over and feature B over , Euclidean distance is dominated by B. Standardize (z-score) or min-max scale before computing distances. Or use Mahalanobis, which scales each axis by its variance automatically.
2. Cosine on zero vectors. Cosine is undefined when either vector has zero norm — the denominator is zero. Libraries return nan or raise; in pipelines, filter zero rows first or add a tiny epsilon.
3. Mahalanobis with a singular covariance. If you have more features than samples, or perfectly correlated features, is singular and does not exist. Use the pseudo-inverse (np.linalg.pinv) or regularize: for small .
4. Mahalanobis with a stale covariance. must be estimated from a representative sample of your data. If you estimate it on a training set that has different scaling or correlation structure than the points you later measure, the distances are meaningless. Re-estimate when the data distribution drifts.
Set, string, and exponent edge cases
5. Jaccard on multisets. Vanilla Jaccard treats inputs as sets — duplicates are lost. If you have count vectors (e.g., a bag of words with weights), use the generalized Tanimoto coefficient instead.
6. Hamming on unequal-length strings. Hamming requires inputs of equal length. For variable-length strings, use Levenshtein (edit distance), which is the minimum number of single-character edits (insertions, deletions, substitutions) needed to turn one string into another.
7. Minkowski with . It is not a true metric (triangle inequality fails). It is sometimes useful in high-dim settings per Aggarwal et al., but do not pass it to a KNN implementation that assumes a true metric — the result may not even satisfy the triangle inequality, breaking neighbour-search optimizations like ball trees.
Conventions and comparability
8. Cosine distance vs. cosine similarity. Many libraries (scipy, sklearn) return 1 - cos as “cosine distance” — that is a distance-like quantity (smaller = more similar). Other libraries (pytorch, some transformers) return the raw cos as “cosine similarity” (larger = more similar). Mixing the two conventions silently flips your ranking. Always check the sign and the range before plugging a cosine into a downstream metric.
9. Treating cosine as a true metric. Cosine distance does not satisfy the triangle inequality in general. It is fine for similarity search and for KNN-style retrieval (where you only care about top-k ranking, not metric properties), but it is not safe for methods that assume a metric — e.g., metric multidimensional scaling, ball-tree indexing with a metric assumption, or any algorithm that relies on the triangle inequality to prune candidates.
10. Using the metric the library defaults to. sklearn’s KNeighborsClassifier defaults to p=2 (Euclidean). That is a reasonable default for low-dim isotropic data, but it is not a universal best choice. Always pick the metric to match the data geometry, not the library default.
11. Comparing distances across metrics. A Euclidean distance of 4.899 and a Manhattan distance of 8.0 between the same pair are not “the same distance” in different units — they are different functions that happen to share the word “distance” in their name. Only compare rankings produced by the same metric across different candidate points, never the raw values of different metrics to each other.
Cross-references
- Why your KNN model fails in high dimensions) — the curse-of-dimensionality article this reference cross-links into; explains in depth why Minkowski distances concentrate as and what to do about it.
- Vector databases compared) — covers cosine vs. Euclidean for ANN (approximate nearest neighbour) indexing, including the HNSW / IVF / PQ trade-offs that depend on the distance metric.
- Building your first RAG pipeline) — uses cosine similarity over OpenAI or SBERT embeddings as the retrieval step; the choice of cosine (over Euclidean) is justified by the unit-norm normalization that most embedding providers apply.
- SHAP values — uses a distance-weighted kernel (a variant of Mahalanobis-like weighting) to approximate local model behaviour around a single prediction; the choice of kernel bandwidth is essentially a choice of distance scale.
Further reading
- Deza, M.M. & Deza, E. (2009). Encyclopedia of Distances. Springer. The comprehensive reference: every named distance metric in the literature, organized by family. Use as a dictionary, not as a textbook.
- Aggarwal, C.C., Hinneburg, A. & Keim, D.A. (2001). On the Surprising Behavior of Distance Metrics in High Dimensional Space. ICDT 2001. The foundational paper on the curse of dimensionality for distance metrics; motivates the use of fractional norms with in high-dim data.
- Cosine similarity and the triangle inequality — NIST/AMS technical discussions on the metric properties of cosine; useful when deciding whether cosine is safe for your specific algorithm.
- scipy.spatial.distance — the canonical Python reference for the metrics above; docs include the exact formulas and edge-case behaviour.
- sklearn.neighbors — KNN implementations supporting Minkowski with arbitrary ; useful for experimenting with the impact of on neighbour selection.
For a Kaggle competition where the choice of distance metric materially affects results, see the Spotify Million Playlist Dataset recommender-style exercises: cosine over track-count vectors consistently outperforms Euclidean there because the data is sparse and high-dimensional.
Related articles
- Machine Learning Under review
Reference: Feature Engineering
A standalone lookup for feature engineering: pick the right encoder, scaler, derived feature, time-series construct, and distance metric for any model family.
- Machine Learning Under review
Reference: Regularization
A comprehensive reference covering L1, L2, dropout, BatchNorm, early stopping, and data augmentation — when to use each, with worked Python code.
- Machine Learning Under review
What Is Cross-Validation, and How Do You Avoid Doing It Wrong?
Learn cross-validation the right way: stop overfitting, prevent data leakage with pipelines, read the standard deviation, and handle time-series correctly.
- Machine Learning Under review
Reference: Evaluation Metrics
A one-stop reference of ML evaluation metrics — classification, regression, calibration, and information theory — with formulas, use cases, and blind spots.
Looking for something else?
Search every article by title, summary or topic.