UMAP

UMAP is the modern default for nonlinear dimensionality reduction: fast, scalable, and able to embed new points. This page covers the graph-based algorithm, its main parameters, practical code including the UMAP + HDBSCAN clustering pattern, and how it compares to t-SNE and PCA.


1. What UMAP Is

UMAP in Two Phases k-NN graph n_neighbors approx. (NN-descent) Fuzzy graph local radius per point union of memberships Init layout spectral embedding Optimize cross-entropy, SGD neg. sampling Embedding 2-50 dims min_dist spacing Phase 1: build graph in high-dim space Phase 2: lay out graph in low-dim space Edges near each point get weight ~1, decaying with distance beyond that point's nearest neighbor.

UMAP (Uniform Manifold Approximation and Projection, McInnes, Healy and Melville 2018) is a nonlinear dimensionality-reduction method grounded in manifold learning and topological data analysis. Like t-SNE, it keeps local neighborhoods, but it is typically much faster, scales to millions of points, tends to preserve more of the global arrangement of clusters, can embed into any number of dimensions, and supports transform on new data. That makes it useful both for 2-D visualization and as a preprocessing step for clustering.

2. How It Works

Graph construction. UMAP finds each point's k = n_neighbors nearest neighbors (approximately, with NN-descent). For point i, let ρi be the distance to its nearest neighbor and pick σi so the memberships sum to log2(k). Edge weights are wij = exp(-(d(xi, xj) - ρi) / σi), making every point connected to at least one neighbor with weight 1 — a local, adaptive notion of distance. Directed weights are combined with a fuzzy union: w = a + b - a·b.

Layout optimization. In the low-dimensional space, similarity is (1 + a·d2b)-1, with a, b fit from min_dist. UMAP minimizes the fuzzy-set cross-entropy between the two graphs with stochastic gradient descent: attractive forces along sampled edges, repulsive forces from negative samples. Unlike t-SNE's KL, the cross-entropy also penalizes placing dissimilar points close together, which is one reason more global structure survives. Initialization from a spectral embedding of the graph adds stability.

3. Key Parameters

Effect of n_neighbors and min_dist n_neighbors=5 local, fragmented n_neighbors=50 more global layout min_dist=0.0 tight clumps min_dist=0.8 spread, even
ParameterEffectTypical
n_neighborsLocal vs global trade-off, like perplexity15 (5–100)
min_distHow tightly points pack in the output; cosmetic for plots0.1 for plots, 0.0 before clustering
n_componentsOutput dimension2 for plots, 5–15 for clustering
metricInput distancecosine for text embeddings
random_stateReproducibility; disables parallelismSet for reports

4. Example with umap-learn

# pip install umap-learn
import umap
from sklearn.datasets import load_digits
from sklearn.cluster import HDBSCAN
from sklearn.model_selection import train_test_split

X, y = load_digits(return_X_y=True)
X_tr, X_new = train_test_split(X, test_size=0.2, random_state=0)

# 2-D map for plotting
reducer = umap.UMAP(n_neighbors=15, min_dist=0.1, n_components=2,
                    metric="euclidean", random_state=42)
Z_tr = reducer.fit_transform(X_tr)
Z_new = reducer.transform(X_new)          # embed unseen points into the same map

# 10-D embedding for density clustering
Z10 = umap.UMAP(n_neighbors=30, min_dist=0.0, n_components=10,
                random_state=42).fit_transform(X)
labels = HDBSCAN(min_cluster_size=30).fit_predict(Z10)
print("clusters:", labels.max() + 1, "noise points:", (labels == -1).sum())

# Supervised / semi-supervised UMAP: pass y (use -1 for unlabeled)
Z_sup = umap.UMAP(random_state=42).fit_transform(X, y=y)

5. UMAP vs t-SNE vs PCA, and Pitfalls

Typical Embedding-Analysis Pipeline Embeddings e.g. 768-d text UMAP to 5-15 dims HDBSCAN clusters + noise UMAP 2-D for plotting Label topics inspect samples Cluster in the 5-15-d UMAP space, plot in a separate 2-D UMAP. This is the BERTopic recipe.

6. A Worked Example of the Graph Weights

Suppose n_neighbors = 4 and point i has neighbours at distances 1.0, 1.5, 2.0 and 3.0. Then ρi = 1.0, the nearest-neighbour distance. UMAP searches for σi so that the memberships sum to log2(4) = 2. Solving 1 + e−0.5/σ + e−1.0/σ + e−2.0/σ = 2 numerically gives σ ≈ 0.89 and memberships of about 1.00, 0.57, 0.33 and 0.11.

Two properties are visible. Subtracting ρ means the nearest neighbour always has weight 1, so even an isolated point is connected to something; this is the "locally uniform" assumption that each point sees its own neighbourhood as having unit density. And σ adapts per point: in a dense region where all four distances are small, the same target sum produces a small σ and a sharp falloff, so dense and sparse regions get comparable treatment. If point j assigns weight 0.6 to i while i assigns 0.3 to j, the symmetric fuzzy union gives 0.6 + 0.3 − 0.18 = 0.72. (The umap-learn implementation counts the point itself among its neighbours and has a few extra details, but the mechanics are the same.)

7. The Optimisation Step

Attraction Along Edges, Repulsion From Negative Samples Low-dimensional layout, one SGD step i j (neighbour) k (random) pulled together pushed apart What the optimiser does edge (i, j), weight w_ij: sampled in proportion to w_ij; move i and j toward each other negative samples k (default 5 per edge): move i away from random points cost: O(edges x epochs), roughly linear in n

The layout loss is a cross-entropy between the high-dimensional weights w and the low-dimensional similarities v: an attractive term w · log(w / v) rewards placing strongly connected points close, and a repulsive term (1 − w) · log((1 − w) / (1 − v)) penalises placing unconnected points close. Computing the repulsive term over all pairs would be quadratic, so UMAP samples: each epoch it visits edges with probability proportional to their weight and, for each, draws a handful of random points as negatives (negative_sample_rate, default 5). The default number of epochs is 200 for large datasets and 500 for small ones. This sampling is why UMAP scales roughly linearly after the nearest-neighbour search, and why runs are stochastic unless random_state is fixed, which also disables some parallelism.

8. Transform, Supervised and Parametric UMAP

Beyond Plain fit_transform transform embed new points into a fitted map Supervised pass y to fit; labels shape the graph Parametric neural net learns the mapping; fast inference inverse_transform approximate points back in input space

transform embeds new points by finding their neighbours among the training data and optimising only the new points' positions; it is much cheaper than refitting but assumes new data comes from the same distribution, so monitor how far new points land from training points. Supervised UMAP (fit(X, y)) mixes a label-based graph into the data graph, pulling same-class points together; target_weight controls the balance, and unlabeled points can be marked with -1 for semi-supervised use. It produces clean separation but can overstate how separable the classes really are, so do not judge class separability from a supervised embedding. Parametric UMAP trains a neural network encoder with the UMAP loss, giving fast, deterministic inference on new data. AlignedUMAP embeds related datasets, such as time slices, in a way that keeps corresponding points near each other across slices.

9. Evaluating an Embedding and Use Cases

Because the picture is persuasive, measure it. Trustworthiness (available as sklearn.manifold.trustworthiness) penalises points that appear as neighbours in the embedding but were not neighbours in the input; values close to 1 mean few false neighbours. k-NN preservation is the fraction of each point's k input-space neighbours that remain among its k embedding neighbours. If labels exist, a k-NN classifier's accuracy on the embedding compared with the raw data shows how much class structure survived. For global structure, compare the correlation of pairwise distances between cluster centroids before and after embedding.

Use caseTypical setup
Single-cell RNA sequencingPCA to ~50 components, then UMAP for the standard cell-type plots
Topic discovery on textSentence embeddings → UMAP (5 to 10 dims, cosine) → HDBSCAN, as in BERTopic
Inspecting a model's representationsPenultimate-layer activations coloured by label or error, to find confused classes
Data quality auditsEmbed images or rows and look for isolated islands of duplicates, mislabeled items or corrupt inputs

Production notes: the nearest-neighbour search dominates time on large data; pynndescent handles this approximately, and GPU implementations (RAPIDS cuML) are available for millions of points. Save the fitted reducer together with its preprocessing; with low_memory and approximate neighbours, results are not bit-identical across versions or hardware.

Further reading: McInnes, Healy & Melville, "UMAP: Uniform Manifold Approximation and Projection for Dimension Reduction" (2018, arXiv:1802.03426); Becht et al., "Dimensionality reduction for visualizing single-cell data using UMAP" (Nature Biotechnology, 2019); Sainburg, McInnes & Gentner, "Parametric UMAP" (2021); Narayan, Berger & Cho, densMAP (2021); the umap-learn documentation, especially "How UMAP Works" and the section on using UMAP for clustering.