Install any skill in seconds. Free to start, no credit card required.
Get Started Free →UMAP dimensionality reduction for visualization, clustering prep, and feature engineering. Fast nonlinear manifold learning preserving local and global structure. Standard UMAP (fit/transform, sklearn-compatible), supervised/semi-supervised, Parametric UMAP (NN encoder/decoder, TensorFlow), DensMAP (density), AlignedUMAP (temporal/batch). 15+ distance metrics, custom Numba metrics, precomputed distances. For linear reduction use PCA; for neighborhood graphs use sklearn NearestNeighbors.
.claude/skills/jaechang-hits-umap-learn/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 172% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 287% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 384% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 581% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 145% | 0% |
UMAP (Uniform Manifold Approximation and Projection) is a dimensionality reduction algorithm for visualization and general non-linear dimensionality reduction. It is faster than t-SNE, scales to larger datasets, preserves both local and global structure, and supports supervised learning and embedding of new data points.
bashpip install umap-learn # For Parametric UMAP (neural network variant) pip install umap-learn[parametric_umap] # requires TensorFlow 2.x
Critical: Always standardize features before applying UMAP to ensure equal weighting across dimensions.
pythonimport umap import numpy as np from sklearn.preprocessing import StandardScaler from sklearn.datasets import load_digits # Load and scale data X, y = load_digits(return_X_y=True) X_scaled = StandardScaler().fit_transform(X) # Fit and transform embedding = umap.UMAP(random_state=42).fit_transform(X_scaled) print(f"Input: {X_scaled.shape}, Output: {embedding.shape}") # Input: (1797, 64), Output: (1797, 2)
Basic dimensionality reduction following scikit-learn conventions.
pythonimport umap from sklearn.preprocessing import StandardScaler X_scaled = StandardScaler().fit_transform(data) # Method 1: fit_transform (single step) embedding = umap.UMAP( n_neighbors=15, # local neighborhood size (2-200) min_dist=0.1, # min distance between embedded points (0.0-0.99) n_components=2, # output dimensions metric='euclidean', # distance metric random_state=42, # reproducibility ).fit_transform(X_scaled) print(f"Embedding shape: {embedding.shape}") # Method 2: fit + access (for reuse) reducer = umap.UMAP(random_state=42) reducer.fit(X_scaled) embedding = reducer.embedding_ # trained embedding graph = reducer.graph_ # fuzzy simplicial set (sparse matrix)
python# Visualization import matplotlib.pyplot as plt plt.figure(figsize=(8, 6)) plt.scatter(embedding[:, 0], embedding[:, 1], c=labels, cmap='Spectral', s=5) plt.colorbar() plt.title('UMAP Embedding') plt.tight_layout() plt.savefig('umap_embedding.png', dpi=150)
Incorporate label information to guide embedding via the y parameter.
pythonimport umap # Supervised — all labels known embedding = umap.UMAP(random_state=42).fit_transform(X_scaled, y=labels) # Semi-supervised — partial labels (mark unlabeled as -1) semi_labels = labels.copy() semi_labels[unlabeled_indices] = -1 embedding = umap.UMAP(random_state=42).fit_transform(X_scaled, y=semi_labels) # Control label influence with target_weight (0.0=unsupervised, 1.0=fully supervised) reducer = umap.UMAP( target_weight=0.7, # emphasize labels target_metric='categorical', # for classification; use distance metric for regression random_state=42 ) embedding = reducer.fit_transform(X_scaled, y=labels) print(f"Supervised embedding: {embedding.shape}")
Project unseen data into the trained embedding space.
pythonimport umap from sklearn.preprocessing import StandardScaler scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) X_test_scaled = scaler.transform(X_test) # Fit on training data reducer = umap.UMAP(n_components=10, random_state=42) X_train_emb = reducer.fit_transform(X_train_scaled) # Transform test data X_test_emb = reducer.transform(X_test_scaled) print(f"Train: {X_train_emb.shape}, Test: {X_test_emb.shape}") # Works in sklearn Pipelines from sklearn.pipeline import Pipeline from sklearn.svm import SVC pipeline = Pipeline([ ('scaler', StandardScaler()), ('umap', umap.UMAP(n_components=10, random_state=42)), ('classifier', SVC()) ]) pipeline.fit(X_train, y_train) accuracy = pipeline.score(X_test, y_test) print(f"Pipeline accuracy: {accuracy:.3f}")
Neural network-based embedding via TensorFlow/Keras. Enables efficient transform, reconstruction, and custom architectures.
pythonfrom umap.parametric_umap import ParametricUMAP # Default architecture (3-layer, 100-neuron FC network) embedder = ParametricUMAP(n_components=2, random_state=42) embedding = embedder.fit_transform(X_scaled) new_emb = embedder.transform(new_data) # fast neural network inference print(f"Parametric embedding: {embedding.shape}")
pythonimport tensorflow as tf from umap.parametric_umap import ParametricUMAP # Custom encoder/decoder for autoencoder mode input_dim = X_scaled.shape[1] encoder = tf.keras.Sequential([ tf.keras.layers.InputLayer(input_shape=(input_dim,)), tf.keras.layers.Dense(128, activation='relu'), tf.keras.layers.Dense(64, activation='relu'), tf.keras.layers.Dense(2), ]) decoder = tf.keras.Sequential([ tf.keras.layers.InputLayer(input_shape=(2,)), tf.keras.layers.Dense(64, activation='relu'), tf.keras.layers.Dense(128, activation='relu'), tf.keras.layers.Dense(input_dim), ]) embedder = ParametricUMAP( encoder=encoder, decoder=decoder, dims=(input_dim,), parametric_reconstruction=True, autoencoder_loss=True, n_training_epochs=10, batch_size=128, n_neighbors=15, min_dist=0.1, random_state=42 ) embedding = embedder.fit_transform(X_scaled) reconstructed = embedder.inverse_transform(embedding) print(f"Reconstruction error: {np.mean((X_scaled - reconstructed)**2):.4f}")
Variant preserving local density information in the embedding.
pythonimport umap reducer = umap.UMAP( densmap=True, # enable DensMAP dens_lambda=2.0, # density preservation weight dens_frac=0.3, # fraction for density estimation output_dens=True, # output density estimates n_neighbors=15, min_dist=0.1, random_state=42 ) embedding = reducer.fit_transform(X_scaled) # Access density estimates original_density = reducer.rad_orig_ # density in original space embedded_density = reducer.rad_emb_ # density in embedded space print(f"DensMAP embedding: {embedding.shape}") print(f"Density correlation: {np.corrcoef(original_density, embedded_density)[0,1]:.3f}")
Align embeddings across multiple related datasets (time points, batches).
pythonfrom umap import AlignedUMAP # Multiple related datasets datasets = [day1_data, day2_data, day3_data] mapper = AlignedUMAP( n_neighbors=15, alignment_regularisation=1e-2, # alignment strength alignment_window_size=2, # align with N adjacent datasets n_components=2, random_state=42 ) mapper.fit(datasets) aligned_embeddings = mapper.embeddings_ # list of aligned embedding arrays print(f"Aligned {len(aligned_embeddings)} datasets") for i, emb in enumerate(aligned_embeddings): print(f" Dataset {i}: {emb.shape}")
| Parameter | Low | Medium (default) | High | Effect | |-----------|-----|-------------------|------|--------| | n_neighbors | 2-5 | 15 | 50-200 | Local detail vs global structure | | min_dist | 0.0 | 0.1 | 0.5-0.99 | Tight clusters vs spread out | | n_components | 2 | 2 | 5-50 | Visualization vs ML/clustering | | spread | 0.5 | 1.0 | 2.0 | Embedding scale (with min_dist) |
| Use-Case | n_neighbors | min_dist | n_components | metric | |----------|-------------|----------|-------------|--------| | Visualization | 15 | 0.1 | 2 | euclidean | | Clustering (HDBSCAN) | 30 | 0.0 | 5-10 | euclidean | | Text/document embedding | 15 | 0.1 | 2 | cosine | | Global structure | 100 | 0.5 | 2 | euclidean | | ML feature engineering | 15-30 | 0.1 | 10-50 | euclidean | | Binary/set data | 15 | 0.1 | 2 | hamming/jaccard |
Minkowski family: euclidean, manhattan, chebyshev, minkowski. Spatial: canberra, braycurtis, haversine. Correlation: cosine, correlation. Binary: hamming, jaccard, dice, russellrao, rogerstanimoto, sokalmichener, sokalsneath, yule. Special: precomputed (distance matrix), custom Numba-compiled callables.
| Feature | Standard | Parametric | |---------|----------|-----------| | Backend | Direct optimization | TensorFlow neural network | | Transform speed | Moderate | Fast (neural net inference) | | Inverse transform | Approximate, expensive | Decoder network, fast | | Custom architecture | No | Yes (CNNs, RNNs, etc.) | | Requirements | umap-learn | umap-learn + TensorFlow 2.x | | Best for | Quick exploration | Production pipelines, reconstruction |
pythonimport umap import hdbscan import numpy as np import matplotlib.pyplot as plt from sklearn.preprocessing import StandardScaler from sklearn.metrics import adjusted_rand_score # Step 1: Preprocess X_scaled = StandardScaler().fit_transform(data) print(f"Input shape: {X_scaled.shape}") # Step 2: UMAP for clustering (NOT visualization parameters) reducer = umap.UMAP( n_neighbors=30, # more global structure for clustering min_dist=0.0, # allow tight packing n_components=10, # higher dims preserve density better than 2D metric='euclidean', random_state=42 ) embedding = reducer.fit_transform(X_scaled) # Step 3: HDBSCAN clustering clusterer = hdbscan.HDBSCAN(min_cluster_size=15, min_samples=5) cluster_labels = clusterer.fit_predict(embedding) n_clusters = len(set(cluster_labels)) - (1 if -1 in cluster_labels else 0) noise = sum(cluster_labels == -1) print(f"Clusters: {n_clusters}, Noise: {noise}") # Step 4: Separate 2D embedding for visualization vis_emb = umap.UMAP(n_neighbors=15, min_dist=0.1, random_state=42).fit_transform(X_scaled) plt.scatter(vis_emb[:, 0], vis_emb[:, 1], c=cluster_labels, cmap='Spectral', s=5) plt.colorbar() plt.title(f'HDBSCAN Clusters (n={n_clusters})') plt.tight_layout() plt.savefig('umap_clusters.png', dpi=150)
pythonimport umap import numpy as np from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.svm import SVC from sklearn.metrics import classification_report # Split and scale X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) scaler = StandardScaler() X_train_s = scaler.fit_transform(X_train) X_test_s = scaler.transform(X_test) # Supervised UMAP for feature engineering reducer = umap.UMAP(n_components=10, random_state=42) X_train_emb = reducer.fit_transform(X_train_s, y=y_train) X_test_emb = reducer.transform(X_test_s) # Downstream classifier clf = SVC(kernel='rbf') clf.fit(X_train_emb, y_train) y_pred = clf.predict(X_test_emb) print(classification_report(y_test, y_pred))
Text-only — combines Core API modules 1 and 3 (inverse_transform on standard UMAP):
reducer.inverse_transform(grid_points) to reconstruct high-dimensional dataNote: inverse transform is approximate; works poorly outside the convex hull of the training embedding.
| Parameter | Module | Default | Range | Effect | |-----------|--------|---------|-------|--------| | n_neighbors | UMAP | 15 | 2-200 | Local vs global structure balance | | min_dist | UMAP | 0.1 | 0.0-0.99 | Cluster tightness | | n_components | UMAP | 2 | 2-100 | Output dimensionality | | metric | UMAP | 'euclidean' | See metrics list | Distance calculation method | | spread | UMAP | 1.0 | >0 | Embedding scale (with min_dist) | | n_epochs | UMAP | None (auto) | 50-500+ | Training iterations | | learning_rate | UMAP | 1.0 | >0 | SGD step size | | init | UMAP | 'spectral' | spectral/random/pca | Embedding initialization | | random_state | UMAP | None | int | Reproducibility seed | | target_weight | UMAP | 0.5 | 0.0-1.0 | Label influence (supervised) | | densmap | UMAP | False | bool | Enable DensMAP | | dens_lambda | UMAP | 2.0 | >0 | DensMAP density weight | | low_memory | UMAP | True | bool | Memory-efficient mode | | encoder | ParametricUMAP | None | Keras model | Custom encoder network | | decoder | ParametricUMAP | None | Keras model | Custom decoder network | | n_training_epochs | ParametricUMAP | 1 | 1-100 | Neural network training epochs | | alignment_regularisation | AlignedUMAP | 0.01 | >0 | Alignment strength | | alignment_window_size | AlignedUMAP | 3 | 1-N | Adjacent datasets to align |
StandardScaler before UMAP — unscaled features with different ranges will dominate the embedding.random_state for reproducibility: UMAP uses stochastic optimization; results vary between runs without a fixed seed.n_neighbors=30, min_dist=0.0, n_components=5-10. Visualization needs n_neighbors=15, min_dist=0.1, n_components=2.pythonfrom numba import njit import umap @njit() def weighted_euclidean(x, y): """Custom distance with feature weights.""" result = 0.0 for i in range(x.shape[0]): result += (x[i] - y[i]) ** 2 * (1.0 + i * 0.01) # increasing weight return np.sqrt(result) embedding = umap.UMAP(metric=weighted_euclidean, random_state=42).fit_transform(data)
pythonimport umap from scipy.spatial.distance import pdist, squareform # Compute custom distance matrix dist_matrix = squareform(pdist(data, metric='correlation')) # Use precomputed distances embedding = umap.UMAP( metric='precomputed', random_state=42 ).fit_transform(dist_matrix) print(f"Embedding from precomputed: {embedding.shape}")
pythonimport umap from sklearn.svm import SVC # Train supervised embedding on labeled data mapper = umap.UMAP(n_components=10, random_state=42) train_emb = mapper.fit_transform(X_train, y=y_train) # Transform unlabeled test data using learned metric test_emb = mapper.transform(X_test) # Downstream classifier clf = SVC().fit(train_emb, y_train) predictions = clf.predict(test_emb) print(f"Accuracy: {(predictions == y_test).mean():.3f}")
| Problem | Cause | Solution | |---------|-------|----------| | Disconnected/fragmented clusters | n_neighbors too low | Increase n_neighbors (try 30-50) | | Clusters too spread out | min_dist too high | Decrease min_dist (try 0.0-0.05) | | All points collapsed | Bad preprocessing or min_dist too low | Check StandardScaler; increase min_dist | | Poor clustering results | Using visualization parameters for clustering | Set n_neighbors=30, min_dist=0.0, n_components=5-10 | | Transform results differ from training | Distribution shift | Ensure test data matches training distribution; use Parametric UMAP | | Slow on large datasets (>100k) | Default settings | Set low_memory=True; preprocess with PCA to 50-100 dims | | First run very slow | Numba JIT compilation | Expected — subsequent runs are fast (compiled cache) | | ImportError: umap | Name conflict with umap package | pip install umap-learn (not pip install umap) | | Parametric UMAP import error | Missing TensorFlow | pip install umap-learn[parametric_umap] | | Non-reproducible results | Missing random_state | Always set random_state=42 (or any int) |
Complete UMAP constructor parameter reference (60+ parameters organized by category: core, training, advanced structural, supervised, transform, performance, DensMAP), all methods and attributes, ParametricUMAP class with autoencoder parameters, AlignedUMAP class, utility functions (nearest_neighbors, fuzzy_simplicial_set). Core parameter tuning guidance was relocated to SKILL.md Key Concepts and Core API modules. Usage examples duplicating SKILL.md workflows omitted.
metric='precomputed'| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 14,127 | 14,932 | +6% | 1 | 1 | 0% | 2,892 | 7,869 | +172% | 0 | 0 | — |
case-02 | pass→pass | 5,144 | 2,856 | -44% | 1 | 1 | 0% | 857 | 5,832 | +581% | 0 | 0 | — |
case-03 | pass→pass | 14,687 | 6,498 | -56% | 1 | 1 | 0% | 2,616 | 6,417 | +145% | 0 | 0 | — |
case-04 | pass→pass | 8,454 | 3,781 | -55% | 1 | 1 | 0% | 1,423 | 6,020 | +323% | 0 | 0 | — |
case-05 | fail→pass | 8,797 | 4,560 | -48% | 1 | 1 | 0% | 1,581 | 6,112 | +287% | 0 | 0 | — |
case-06 | pass→pass | 12,316 | 7,043 | -43% | 1 | 1 | 0% | 2,141 | 6,690 | +212% | 0 | 0 | — |
case-07 | pass→pass | 8,822 | 4,412 | -50% | 1 | 1 | 0% | 1,555 | 6,179 | +297% | 0 | 0 | — |
case-08 | pass→pass | 4,040 | 2,204 | -45% | 1 | 1 | 0% | 692 | 5,716 | +726% | 0 | 0 | — |
case-09 | pass→pass | 8,475 | 4,377 | -48% | 1 | 1 | 0% | 1,448 | 6,075 | +320% | 0 | 0 | — |
case-10 | pass→pass | 9,236 | 5,140 | -44% | 1 | 1 | 0% | 1,641 | 6,309 | +284% | 0 | 0 | — |
case-11 | fail→pass | 8,548 | 9,174 | +7% | 1 | 1 | 0% | 1,466 | 7,089 | +384% | 0 | 0 | — |
case-12 | pass→pass | 14,180 | 4,749 | -67% | 1 | 1 | 0% | 1,691 | 6,285 | +272% | 0 | 0 | — |
case-13 | fail→fail | 10,316 | 6,470 | -37% | 1 | 1 | 0% | 1,857 | 6,398 | +245% | 0 | 0 | — |
case-14 | pass→pass | 11,662 | 5,710 | -51% | 1 | 1 | 0% | 1,954 | 6,257 | +220% | 0 | 0 | — |
case-15 | pass→pass | 6,487 | 3,390 | -48% | 1 | 1 | 0% | 1,066 | 6,084 | +471% | 0 | 0 | — |
case-16 | pass→pass | 6,030 | 3,423 | -43% | 1 | 1 | 0% | 1,200 | 5,979 | +398% | 0 | 0 | — |
case-17 | pass→pass | 11,099 | 8,201 | -26% | 1 | 1 | 0% | 1,851 | 6,686 | +261% | 0 | 0 | — |
case-18 | pass→pass | 7,302 | 20,976 | +187% | 1 | 1 | 0% | 1,369 | 6,435 | +370% | 0 | 0 | — |
case-19 | pass→pass | 6,542 | 3,216 | -51% | 1 | 1 | 0% | 1,113 | 5,939 | +434% | 0 | 0 | — |
case-20 | pass→pass | 4,074 | 3,308 | -19% | 1 | 1 | 0% | 742 | 5,954 | +702% | 0 | 0 | — |
case-21 | pass→pass | 6,173 | 3,330 | -46% | 1 | 1 | 0% | 1,362 | 6,008 | +341% | 0 | 0 | — |
case-22 | pass→pass | 8,867 | 7,173 | -19% | 1 | 1 | 0% | 1,772 | 6,892 | +289% | 0 | 0 | — |
case-23 | pass→pass | 8,688 | 4,903 | -44% | 1 | 1 | 0% | 1,695 | 6,367 | +276% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted. The headline lift of +13 percentage points is the difference between those two pass rates over the 23 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.