Using cosine distance with scikit learn KNeighborsClassifier
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
By default, scikit-learn's KNeighborsClassifier uses Euclidean distance (Minkowski with p=2) to find nearest neighbors. For text classification, recommendation systems, and other high-dimensional sparse data problems, cosine distance often produces better results because it measures the angle between vectors rather than their magnitude. This article explains how to configure KNeighborsClassifier to use cosine distance.
Cosine Similarity vs Cosine Distance
Cosine similarity measures how similar two vectors are by computing the cosine of the angle between them:
The result ranges from -1 (opposite) to 1 (identical direction). For non-negative vectors (like TF-IDF), it ranges from 0 to 1.
Cosine distance is simply:
This converts similarity to a distance metric where 0 means identical and values closer to 1 (or 2) mean more different.
Using Cosine Distance in KNeighborsClassifier
Method 1: Using the metric Parameter
Pass metric='cosine' directly to the classifier:
This uses scikit-learn's built-in cosine distance implementation.
Method 2: Using a Custom Distance Function
For more control, define a custom distance function:
Method 3: Pre-normalize and Use Euclidean Distance
A computationally efficient alternative is to L2-normalize your vectors first. When vectors are unit length, Euclidean distance and cosine distance produce the same ranking:
This approach is faster because the ball_tree and kd_tree algorithms (not available for cosine) can be used with Euclidean distance.
Complete Example: Text Classification
Algorithm Compatibility
Not all KNN algorithms support cosine distance:
| Algorithm | Cosine Support | Notes |
brute | Yes | Computes all pairwise distances; works with any metric |
ball_tree | No | Only supports metrics in BallTree.valid_metrics |
kd_tree | No | Only supports metrics in KDTree.valid_metrics |
auto | Depends | Falls back to brute for cosine |
When using metric='cosine', set algorithm='brute' explicitly to avoid warnings:
Common Pitfalls
- Sparse matrix support:
metric='cosine'withalgorithm='brute'works with sparse matrices (like TF-IDF output). Custom distance functions may not handle sparse input — convert to dense first or handle explicitly. - Zero vectors: Cosine distance is undefined for zero vectors. Scikit-learn handles this gracefully, but custom functions should check for zero norms.
- Performance with large datasets: The
brutealgorithm has O(n*d) time complexity per query, which is slow for large datasets. For better performance, use the normalization trick withkd_treeor approximate nearest neighbor libraries like FAISS or Annoy. - Choosing k: With cosine distance in high-dimensional spaces, the optimal k is often smaller than with Euclidean distance. Use cross-validation to tune it.
- Negative values: Cosine similarity can range from -1 to 1, making cosine distance range from 0 to 2. Some implementations assume non-negative features. Verify behavior with your data.
Summary
- Use
metric='cosine'inKNeighborsClassifierfor angle-based similarity - Set
algorithm='brute'since tree-based algorithms do not support cosine - For better performance, L2-normalize features and use Euclidean distance instead
- Cosine distance is especially effective for text (TF-IDF), recommendation, and high-dimensional sparse data

