Chapter 2 — Dimensionality Reduction & Clustering

Author

Single Cell RNA-seq Workshop

Note

Where this chapter sits. Companion to Lecture 02 and Tutorial 02. Builds on Chapter 1 (you start with a scaled HVG matrix). Prerequisites: Appendix B §§ 4–9 (linear models, eigendecomposition), P2 (sparse matrices, factors).

2.1 The mental model: project, partition, paint

After preprocessing, every cell is a point in a ~2,000-dimensional gene-expression space (one dimension per HVG). That space has too many dimensions to do anything useful with directly: most pairs of cells are nearly equidistant under Euclidean distance — the curse of dimensionality — and “neighbors” stop carrying meaning.

Three steps fix this:

  1. Project to a lower-dimensional space that keeps the structured variance (PCA → 20–50 PCs)
  2. Partition the cells into communities of mutually-similar neighbors (k-NN graph + Louvain/Leiden)
  3. Paint the result onto a 2-D picture for inspection (UMAP or t-SNE)

Each step is a separate idea with its own theory and failure modes. This chapter walks them in order.

Note🔑 Key concept — the three-step pipeline is a strict dependency chain

Projection, partitioning, and painting are conceptually independent but computationally sequential: PCA produces the coordinate space that the neighbor graph requires; the neighbor graph produces the community structure that clustering formalizes; and UMAP or t-SNE produces the 2-D picture of the clustering, not a replacement for it. A common mistake is to draw conclusions from the UMAP shape without realizing that the clusters shown on it were computed in PC space — the UMAP layout can differ substantially between runs with different random seeds, but the cluster assignments are determined by the SNN graph and do not change with the UMAP. Read the clusters first; consult the UMAP only as a visual summary.

2.2 PCA — what it does and why it works first

Principal Component Analysis finds orthogonal directions of maximum variance in the data. Concretely, given the centered, scaled HVG matrix \(\tilde X\) (cells × genes), PCA computes the singular value decomposition

\[\tilde X = U \Sigma V^\top\]

where:

  • \(V\) (genes × \(k\)) holds the loadings — each column is a direction in gene space
  • \(U\Sigma\) (cells × \(k\)) holds the scores — each cell’s coordinates in PC space
  • \(\sigma_i^2 / \sum \sigma_j^2\) is the fraction of variance explained by PC \(i\)

In scRNA-seq the first ~10 PCs usually separate major cell-type axes (T vs B vs myeloid; epithelial vs stromal vs immune); PCs 10–30 resolve subtypes (CD4 vs CD8, naive vs memory); beyond ~50, you’re almost entirely in noise.

Why PCA first? Three reasons work together. First, compression: 2,000 genes collapse to 30 PCs, making every downstream step faster. Second, denoising: the discarded high-PC directions are dominated by per-cell sampling noise rather than biological signal, so dropping them actually improves the quality of neighbor graphs and cluster solutions. Third, decorrelation: PCs are orthogonal by construction, so Euclidean distance in PC space behaves the way intuition expects — which it does not in raw HVG space, where correlated genes inflate distances along shared axes.

UMAP and clustering operate on PCs, not raw expression, exactly because of (3). Skip PCA and try to UMAP the scaled HVG matrix and you’ll get visibly worse embeddings, slower runtime, and unstable cluster solutions. In practice the SVD is computed by a randomized / truncated solver (irlba in Seurat, scanpy’s arpack/randomized backends), which returns only the top ~50 components and scales to atlas-sized matrices where a full eigendecomposition would be intractable1. The choice of PCA as the linear backbone of the single-cell workflow is shared across Seurat, Scanpy, and Bioconductor13.

Note🔑 Key concept — what a principal component captures

Each PC is a linear combination of all HVGs — a weighted sum of gene expression values — chosen so that cell scores along that axis have maximum variance, subject to the constraint of being orthogonal to all previous PCs. In biological terms, PC1 captures the largest source of transcriptional variation in the dataset (often a cell-type axis, or a batch/condition axis on unintegrated data); PC2 captures the next largest source, uncorrelated with PC1; and so on. Because PCA finds axes of maximum variance in a linear sense, it is interpretable: the loadings tell you exactly which genes drive cells apart along each PC, making it the only major dimensionality reduction in the workflow where you can inspect a component and understand its biology. UMAP and t-SNE lack this interpretability entirely.

Note⚙️ Key parameter — RunPCA npcs

RunPCA(seu, npcs = 50). Default: 50. Sets the maximum number of principal components computed. More PCs cost more computation but ensure you don’t miss a low-variance biological axis. For ElbowPlot to be informative you need at least as many PCs as you expect to use (typically 30–50); requesting 50 upfront is a safe standard. Note that npcs is the maximum computed — you then choose a subset (e.g. dims = 1:20) for FindNeighbors and RunUMAP. Increasing npcs beyond 50 rarely helps for standard 10x runs of < 200k cells.

2.3 Choosing how many PCs to keep

The standard tool is the scree plot (sometimes called an elbow plot): for each PC, plot the variance it explains, in descending order. The shape is a steep initial drop, an elbow, and a long noise floor. Table 1 summarizes the main PC-selection strategies, their cost, and when to reach for each.

Table 1: PC-selection strategies and when to use each. Leaning high is the safer error: an extra few noise PCs slow runtime minimally but a discarded PC carrying a rare subtype cannot be recovered.
Strategy How it works Typical result Notes
Scree / elbow plot (visual) Identify the “elbow” in variance-explained curve PC 15–30 for 5–50k-cell runs Fast; subjective but usually fine
JackStraw permutation test Compare each PC’s variance to a null by random permutation Statistically principled cutoff Very slow for >50k cells
Fixed high bound (e.g. 50 PCs) Accept more noise dimensions in exchange for not missing low-variance subtypes Typical for atlas-scale (>100k cells) Safest default when in doubt
Marker-based sanity check Inspect gene loadings; confirm biology not QC drives the retained PCs Flags mito/ribo-dominated PCs Should always accompany any other method

Rules of thumb:

  • The elbow is usually between PC15 and PC30 for 10x runs of 5–50k cells
  • Lean high rather than low: going from 20 to 30 PCs rarely hurts; going from 30 to 50 mostly costs runtime
  • A more principled alternative is the JackStraw permutation test (Seurat::JackStraw), which compares each PC’s variance to a null distribution; slow on large datasets
  • For atlas-scale (>100k cells), 50 PCs is typical

The number of PCs is one of two knobs (the other is clustering resolution) that jointly determine how many cell types you recover, and the choice is genuinely consequential — benchmarks of clustering pipelines show that the number of estimated cell types is sensitive to both4,5. Leaning slightly high on PCs is the safer error, since dropping a real low-variance subtype is harder to detect than carrying a few noise dimensions.

A PC dominated by mitochondrial or ribosomal genes is a red flag: it’s tracking cell quality, not biology. If PC1 looks like that, your QC was too lax — go back to filtering.

2.4 The shared-nearest-neighbor (SNN) graph

After PCA, we want to identify groups of similar cells. Two-decade-old methods like K-means are inappropriate for scRNA-seq because:

  • K-means assumes clusters are spherical and equally sized (false — rare cell types coexist with abundant ones)
  • K-means requires you to know k in advance (the whole point is often to discover how many populations there are)

The modern alternative is graph-based clustering, which has two stages:

2.4.1 Build the k-nearest-neighbor (kNN) graph

For each cell, find its \(k\) nearest neighbors in PC space (typically \(k = 20\)). Connect each cell to its neighbors with edges. The result is a graph where every cell has degree \(k\). The k here is the knob that controls how local or global the neighborhood concept is: small k (5–10) makes each cell’s community very tight and local, while large k (30–50) creates broader, fuzzier communities that survive noise better but can blend nearby populations.

2.4.2 Promote to a shared-nearest-neighbor (SNN) graph

For every edge \((i, j)\) in the kNN graph, weight it by how many of \(i\)’s neighbors are also \(j\)’s neighbors — the Jaccard similarity of their neighborhoods. Edges between cells in the same dense region get high weight; edges between cells on the boundary of a cluster get low weight. The SNN graph is what clustering actually runs on. The Jaccard re-weighting is important: it makes the graph robust to “hub” cells — cells with unusually high connectivity that would otherwise dominate naive kNN clustering — by requiring that shared-neighborhood structure, not just raw proximity, defines community membership.

Seurat’s FindNeighbors(seu, dims = 1:30) builds both. The SNN graph stored in seu@graphs$RNA_snn is then handed to FindClusters.

Note⚙️ Key parameter — FindNeighbors k.param

FindNeighbors(seu, dims = 1:30, k.param = 20). Default: k.param = 20. Sets the number of nearest neighbors per cell in PC space. Increasing k.param (e.g. to 30–50) makes clusters larger and more robust to noise but can merge genuinely distinct subtypes; decreasing it (e.g. to 10) emphasizes fine-grained local structure and tends to produce more, smaller clusters. For most 10x datasets in the 5k–100k cell range, the default of 20 is appropriate; rare populations benefit from a modest increase (25–30) to ensure they are not isolated as disconnected singleton nodes. dims should match the number of PCs you selected in §2.3.

2.5 Graph-based community detection (Louvain & Leiden)

Both algorithms optimize modularity — a measure of how much denser within-community edges are than the random expectation. Louvain6 is Seurat’s default and is what the workshop tutorials run (FindClusters(seu, resolution = 0.5), i.e. algorithm = 1), because it has no extra dependencies. Leiden7 is the modern preferred refinement — it fixes a real defect in Louvain, which can produce internally disconnected communities, by guaranteeing well-connected communities — but it needs the leidenalg Python package via reticulate, so we present it as the one-line upgrade rather than the default.

Leiden iterates (Louvain follows the same first and third steps, without the refinement guarantee):

  1. Local moves: each cell joins the neighbor’s community if doing so increases modularity
  2. Refinement (Leiden only): split sub-communities that have low internal connectivity
  3. Aggregation: collapse each community into a node and run again

The final partition is a labeling of every cell into a numbered cluster. To switch from the Louvain default to Leiden, add algorithm = 4: FindClusters(seu, resolution = 0.5, algorithm = 4) (algorithm = 4 selects Leiden; 1 is Louvain) — once leidenalg is installed.

The resolution parameter controls granularity:

  • Low resolution (0.1–0.3) → few, broad clusters (immune cell families)
  • Medium (0.5–0.7) → typical PBMC-scale dataset, 8–15 clusters
  • High (1.0+) → many small clusters; subdivides cell states

There is no single “right” resolution. The right one is the one that matches your biological question. Run several and inspect with clustree to see how clusters split as resolution increases. Resolutions where the clustree topology is stable (clusters don’t shuffle membership across nearby resolutions) are defensible. Modularity optimization has a known resolution limit — it can fail to detect communities smaller than a scale set by the total graph size — which is part of why a single global resolution rarely resolves both abundant and rare populations well, and why iterative subclustering of a coarse partition is often more effective than pushing one global resolution very high. Unsupervised clustering remains one of the harder, less-settled steps of the pipeline; systematic comparisons find that no method dominates across datasets and that the recovered cluster count is method- and parameter-dependent4,5,8.

Note⚙️ Key parameter — FindClusters resolution

FindClusters(seu, resolution = 0.8). Default: 0.8. Controls the granularity of community detection: higher values yield more, smaller clusters; lower values yield fewer, larger clusters. There is no universally correct resolution — the right value depends on your biological question. A practical workflow is to run several resolutions (e.g. c(0.1, 0.3, 0.5, 0.8, 1.0)) simultaneously, visualize the hierarchy with clustree, and choose the resolution where each cluster can be given a defensible biological identity. For the workshop ifnb PBMC dataset, resolution = 0.5 is the tutorial default; on unintegrated data this typically yields 15–20 clusters (more than the ~13 biologically distinct types, because condition-split duplicates appear). Post-integration, the same resolution usually collapses to ~13 clean clusters.

2.6 t-SNE and UMAP: visualization, not analysis

Once you have clusters defined on the SNN graph, you want a 2-D picture of them. Two non-linear methods dominate:

Table 2 summarizes both methods side by side before the subsections walk the details.

Table 2: Comparison of t-SNE and UMAP embedding methods for scRNA-seq visualization. Both algorithms are designed for visualization only — do not measure distances, densities, or trajectory direction from either plot. Global-structure preservation in UMAP is largely an artifact of PCA-based initialization rather than the algorithm itself9.
Property t-SNE UMAP
Objective Minimize KL divergence between Gaussian (high-D) and Student-\(t\) (low-D) neighborhood distributions Minimize cross-entropy between fuzzy topological graph representations
Loss symmetry Asymmetric — penalizes wrong-far neighbors heavily; barely penalizes wrong-close Symmetric — penalizes both directions
Local neighborhood preservation Excellent Good
Global structure preservation Poor (inter-cluster distances not meaningful) Moderate (positions roughly interpretable)
Key tuning parameter Perplexity (default 30): lower → more local; higher → smoother n.neighbors (default 30) + min.dist (default 0.3)
Stability across parameter sweep Sensitive to perplexity More stable across n.neighbors
Workshop default? No (available via RunTSNE) Yes (RunUMAP)

2.6.1 t-SNE

Models pairwise distances in high-D as a probability distribution (Gaussian, with bandwidth set per-cell to match a target perplexity), and tries to find a 2-D arrangement whose distance distribution (Student’s \(t\) in low-D, hence “t” in t-SNE) has minimal Kullback–Leibler divergence to the original10. The heavy-tailed Student’s \(t\) in the low-D space is what solves the “crowding problem” of the original SNE — it gives moderately distant points room to spread out in 2-D.

The KL loss is asymmetric: it heavily penalizes putting true neighbors far apart but only weakly penalizes putting unrelated cells nearby. This is why t-SNE preserves local neighborhoods beautifully and why cluster-to-cluster distances on a t-SNE plot are not meaningful.

The perplexity parameter (default 30) is roughly “how many neighbors does each cell consider”. Low perplexity (5–15) emphasizes very local structure; high (50+) smooths over fine subtypes. Worth running at multiple perplexities and looking for stable structure across them; Kobak & Berens give practical, single-cell-specific guidance on perplexity, learning rate, and initialization11.

2.6.2 UMAP

Builds a fuzzy weighted graph of local neighborhoods (per-cell normalization makes points in dense and sparse regions both still see their own local neighbors) and finds a 2-D embedding by stochastic gradient descent on a cross-entropy objective.

The cross-entropy is symmetric in its penalty structure (both “wrong-far” and “wrong-close” errors are penalized), so UMAP preserves more global topology than t-SNE — not perfectly, but enough that cluster positions are roughly meaningful (still don’t measure pseudotime in UMAP coordinates)12,13. An important caveat from the methods literature: much of the global structure that t-SNE and UMAP appear to preserve actually comes from the initialization (PCA-based initialization preserves global layout; random initialization does not), rather than from the algorithms themselves — so the common claim that “UMAP preserves global structure better than t-SNE” is largely an artifact of their default initializations9.

UMAP is the workshop default. Two parameters:

  • n.neighbors (default 30): smaller = more local; larger = more global
  • min.dist (default 0.3): smaller = tighter clusters with clear gaps; larger = spread-out, good for continua

UMAP is noticeably more stable across n.neighbors than t-SNE is across perplexity. Usually one well-chosen UMAP is enough.

Note🔑 Key concept — what UMAP does and does not preserve

UMAP optimizes a cross-entropy objective over a fuzzy weighted neighborhood graph: it penalizes both placing true neighbors too far apart (attraction) and placing unrelated cells too close together (repulsion). This symmetric penalty preserves more global topology than t-SNE, which only penalizes the first error. However, “more global” does not mean “faithful” — inter-cluster distances on a UMAP are not a reliable measure of biological dissimilarity, and apparent density differences are artifacts of the min.dist parameter, not cell-type frequencies. UMAP is correctly read as a visualization of the cluster solution (computed on the SNN graph in PC space), not as a geometric analysis in its own right. It is valid for: showing cluster assignments, overlaying gene expression as a continuous gradient (FeaturePlot), and spotting dramatic batch effects as parallel sample islands. It is not valid for: distance-based statistics, trajectory direction, or density quantification.

Note⚙️ Key parameter — RunUMAP n.neighbors and min.dist

RunUMAP(seu, dims = 1:30, n.neighbors = 30, min.dist = 0.3). Defaults: n.neighbors = 30, min.dist = 0.3. n.neighbors sets how many nearest neighbors each cell considers when building UMAP’s local graph — smaller values (5–10) emphasize very local detail and produce more fragmented embeddings; larger values (50+) emphasize global topology and can blur subtypes together. min.dist sets the minimum separation between points in the 2-D embedding — smaller values (0.05–0.1) produce tight, compact clusters with clear gaps between them (best for discrete cell types); larger values (0.5–0.8) spread cells across continuous gradients (best for developmental trajectories or smooth state transitions). For the workshop ifnb PBMC dataset, the defaults produce a clean, interpretable embedding.

2.7 What UMAPs do not tell you

The most-violated rule in scRNA-seq figure interpretation:

Warning

A UMAP plot does not tell you:

  1. Inter-cluster distance. Two clusters drawn far apart are not necessarily more dissimilar than two drawn nearby.
  2. Cluster density. Apparent point density depends on min.dist and is not biological cell density.
  3. Trajectory direction. Apparent gradients can be optimization artifacts; use trajectory tools (Slingshot, Monocle3, scVelo) for that question.

A UMAP is good for: showing the cluster solution from §2.5, mapping a gene’s expression onto cells (FeaturePlot), and spotting batch effects (color by sample after integration). A UMAP is not good for: differential testing, distance-based statistics, or claims about lineage relationships.

Tip

Build your intuition interactively. If UMAP (or t-SNE) feels like a black box — which features of a plot are safe to read, and how the knobs reshape it — work through Google PAIR’s interactive explainer Understanding UMAP, which lets you turn n.neighbors and min.dist and watch the embedding respond in real time. For a step-by-step written walkthrough, see DataCamp’s Understanding UMAP: a guide to dimensionality reduction. Both are the best cure for over-interpreting an embedding.

2.8 Reading the PC loadings output

When you print PCA results with print(seu[["pca"]], dims = 1:5, nfeatures = 5), you see the top 5 positive and negative gene loadings for the first five PCs. These are the genes pushing cells to the high or low end of each PC axis. Table 3 shows the three canonical patterns and their interpretation. What to look for:

  • PC1 dominated by interferon-stimulated genes (ISG15, IFI6, IFIT1, MX1, etc.) signals that a condition/batch effect is the dominant source of variance — the axis is separating STIM from CTRL, not cell types. This is the expected starting state for the unintegrated ifnb data.
  • Later PCs (2–5) showing cell-type markers (LYZ, CD14 for monocytes; MS4A1, CD79A for B cells; GNLY, NKG7 for NK) are reassuring — cell-type biology is still recoverable even when a condition effect dominates PC1.
  • A PC dominated by mitochondrial or ribosomal genes is a red flag for insufficient QC: those axes are tracking cell quality, not biology.
Table 3: Canonical PC loading patterns and their interpretation. Reading the top 5 positive and negative loadings per PC is the fastest sanity check after RunPCA.
Pattern in top loadings Interpretation Action
ISGs (ISG15, IFI6, MX1) dominate PC1 Stimulation / condition effect is the largest variance axis Expected for multi-condition data; proceed to integration (Chapter 5)
Cell-type markers (CD14, MS4A1, GNLY) in PCs 2–5 Cell-type biology is recoverable despite batch in PC1 Keep these PCs; reassuring sign
Mitochondrial genes (MT-ND1, MT-CO1) dominate any PC Cell-quality axis survived filtering Re-examine QC thresholds; consider regressing percent.mt
Ribosomal genes (RPL, RPS) dominate any PC Translational-activity axis; often confounded with cell state Exclude ^RP[SL] from HVGs or regress percent.rb

DimHeatmap(seu, dims = 1, cells = 500, balanced = TRUE) provides a complementary view: rows are genes (top positive and negative loadings), columns are the 500 cells with the most extreme PC1 scores. A clean two-block structure on PC1 is the visual signature of a strong batch or condition axis.

2.9 The cross-tabulation diagnostic

After clustering, table(seu$cluster_col, seu$sample) is one of the most informative two-line diagnostics in scRNA-seq. Computing the proportion form:

prop.table(table(cluster = seu$RNA_snn_res.0.5, sample = seu$stim), margin = 1)

gives a clusters × samples table where each row sums to 1. On unintegrated data, many clusters will be >80–95% one sample — a direct measure of how completely the batch effect has split the data. After integration, the same table should show most clusters at ~50% per sample (for a balanced design). Clusters that remain heavily skewed after integration are either genuine single-condition biology (e.g. an IFN-activated state present only in STIM) or residual under-correction — biological knowledge distinguishes the two.

2.10 The single-sample workflow ends here; multi-sample needs Chapter 5

Everything above is the single-sample workflow: one biological condition, one batch, no need to align across samples. If you have only one sample, you read off cell types from the clusters (Chapter 3) and you’re done.

If you have two or more samples and the unintegrated UMAP shows them as separate islands — every cell type appearing twice or more, once per sample — you have a batch effect problem and need integration. That’s Chapter 5.

The diagnostic is direct: compute the unintegrated UMAP (this chapter), color it by sample. If samples interleave within clusters, you don’t need integration. If samples form parallel structures, you do.

2.11 Common errors

Table 4 collects the errors most frequently encountered during dimensionality reduction and clustering, together with their root cause and the fix.

Table 4: Common errors in dimensionality reduction and clustering, their root cause, and the fix.
You see What’s wrong What to do
RunPCA errors with “no variable features” You skipped FindVariableFeatures Run it before ScaleData
The scree plot has no clear elbow Either your QC was insufficient or your data is genuinely high-rank Inspect the top loadings; try 30 PCs
Cluster numbers reshuffle between R sessions You didn’t set.seed() set.seed(2026) before FindClusters and RunUMAP; use random.seed = 2026 argument
UMAP shows a cluster that has no biology Sometimes batch / depth artifact, sometimes a rare cell type — inspect markers (Chapter 3) Don’t dismiss; investigate
FindAllMarkers / FindMarkers is very slow Install presto; Seurat detects it automatically and uses a much faster Wilcoxon implementation (this speeds up marker tests, not FindClusters itself) install.packages("presto")

2.12 Where this material is also discussed

2.13 Going further

For the embedding methods, go to the primary sources: the t-SNE10 and UMAP12 papers, UMAP’s single-cell debut13, and the two essential “how to use them responsibly” papers9,11. For clustering, the challenges review4 and two benchmarks — of clustering methods8 and of cell-number estimation5 — are the references to consult when choosing an algorithm and resolution. Hands-on intuition is best built with Google PAIR’s interactive Understanding UMAP and DataCamp’s written guide to dimensionality reduction. Two further tools worth knowing: densMAP, a UMAP variant that also preserves cluster densities (addressing limitation 2 in §2.7), and openTSNE, a faster, more controllable t-SNE implementation. The full curated list is on Key Papers, Reviews & Benchmarks.

References

1.
Amezquita, R. A. et al. Orchestrating single-cell analysis with Bioconductor. Nature Methods 17, 137–145 (2020).
2.
Satija, R., Farrell, J. A., Gennert, D., Schier, A. F. & Regev, A. Spatial reconstruction of single-cell gene expression data. Nature Biotechnology 33, 495–502 (2015).
3.
Wolf, F. A., Angerer, P. & Theis, F. J. SCANPY: Large-scale single-cell gene expression data analysis. Genome Biology 19, 15 (2018).
4.
Kiselev, V. Yu., Andrews, T. S. & Hemberg, M. Challenges in unsupervised clustering of single-cell RNA-seq data. Nature Reviews Genetics 20, 273–282 (2019).
5.
Yu, L., Cao, Y., Yang, J. Y. H. & Yang, P. Benchmarking clustering algorithms on estimating the number of cell types from single-cell RNA-sequencing data. Genome Biology 23, 49 (2022).
6.
Blondel, V. D., Guillaume, J.-L., Lambiotte, R. & Lefebvre, E. Fast unfolding of communities in large networks. Journal of Statistical Mechanics: Theory and Experiment 2008, P10008 (2008).
7.
Traag, V. A., Waltman, L. & Eck, N. J. van. From Louvain to Leiden: Guaranteeing well-connected communities. Scientific Reports 9, 5233 (2019).
8.
Duò, A., Robinson, M. D. & Soneson, C. A systematic performance evaluation of clustering methods for single-cell RNA-seq data. F1000Research 7, 1141 (2018).
9.
Kobak, D. & Linderman, G. C. Initialization is critical for preserving global data structure in both t-SNE and UMAP. Nature Biotechnology 39, 156–157 (2021).
10.
Maaten, L. van der & Hinton, G. Visualizing data using t-SNE. Journal of Machine Learning Research 9, 2579–2605 (2008).
11.
Kobak, D. & Berens, P. The art of using t-SNE for single-cell transcriptomics. Nature Communications 10, 5416 (2019).
12.
McInnes, L., Healy, J. & Melville, J. UMAP: Uniform manifold approximation and projection for dimension reduction. arXiv https://doi.org/10.48550/arXiv.1802.03426 (2018) doi:10.48550/arXiv.1802.03426.
13.
Becht, E. et al. Dimensionality reduction for visualizing single-cell data using UMAP. Nature Biotechnology 37, 38–44 (2019).
Back to top