Cells × genes in the count matrix
One row is a cell and one column is a gene. Each entry counts the RNA molecules of that gene captured from that cell; capture is incomplete, so the count is lower than the number present.
How a row is built
- Rows are individual human pancreas cells, with a count for each measured gene.
- 90.62% of the original count matrix is zero; a zero can mean the gene is switched off or that its molecules were not captured.
- The cell types published with the data are kept aside and used only to score the clusters at the end.
- Each cell’s donor (one of four people) is also kept aside, to check whether clusters split by person.
The workflow starts from one table of whole numbers: for every individual cell of a human pancreas, how many times each gene's message was captured. One row is a cell, one column is a gene, and the number is copies caught — not copies present.
It has — columns, so it cannot be drawn. The obvious workaround is to pick two genes and plot those, and the first panel below lets you try it on the 36 genes a biologist would reach for first. Some pairs work beautifully for one cell type. None separates all fourteen, and you can only pick the good pairs if you already know the answer.
The second panel shows why the problem gets worse rather than better as columns pile up: distances between points concentrate, and nearest-neighbour reasoning — which is what clustering is — runs out of contrast.
SeeThe raw matrix, any two marker genes plotted against each other, and what dimensionality does to distance.
LearnWhy hand-picking axes does not scale, and why a mostly empty table is the normal shape of this data.
Stage 1 hands stage 2 one matrix of counts — and the published annotation stays behind until the very last step.
Before any method runs, one nuisance has to go. Sequencing does not capture the same number of molecules from every cell: in this dataset the deepest cell yields — as many counts as the shallowest. Compare two cells on raw counts and much of what you see is how well each was sampled, not what it was doing.
The fix is deliberately blunt. Divide every cell by its own total, multiply by a common target, then take log1p. The first part removes depth; the log stops a handful of enormously expressed genes from dominating every distance, and makes a doubling count the same wherever it happens.
Nothing about the biology is added here. This step only makes the later comparisons about the right thing.
SeeSix real cells spanning the depth range, before and after the correction, on the same genes.
LearnWhy raw counts are not comparable between cells, and what the log transform is for.
Of — measured genes, most are either off in nearly every cell or on at much the same level in all of them. Neither kind can tell two cells apart, and carrying them costs both compute and contrast.
So we keep the genes that vary more than their own expression level predicts. Plot every gene by mean expression against a variability score, and the informative ones stand out above the trend. Taking the top — is the standard working choice.
This is the one step in the workflow that throws information away on purpose: a gene that matters in only a handful of rare cells can fall below the line.
SeeEvery scored gene as a point, with the selected set highlighted, and the threshold moving as you drag it.
LearnWhat “highly variable” means in practice, and how the number of genes kept changes which marker genes survive.
At this threshold
- genes kept
- —
- of scored
- —
- markers kept
- —
- score cut-off
- —
Marker genes
The canonical markers for the fourteen cell types. Drag the threshold down and watch which ones fall out — those are cell types the workflow would go on to miss.
Principal component analysis does not select genes and does not bend anything. It finds the directions along which the cloud of cells varies most, and rotates the coordinate system onto them. Each new axis is a weighted blend of all — input genes, ordered by how much variance it captures.
This is the workhorse step nobody talks about: every step after this one — t-SNE, UMAP and both clustering algorithms — runs on the first 50 principal components, not the raw genes. It is a denoiser as much as a reducer. The components you discard are mostly sampling noise from the — of the matrix that is zero.
And unlike everything that follows, PCA's axes mean something you can read. Click a component in the scree plot to see which genes load onto it.
SeeThe cells rotated onto the directions along which they vary most, and which genes define each of those directions.
LearnPCA rotates rather than selects, removes a lot of noise on the way, and gives you the only axes on this page you can actually read.
Variance explained
Top loadings — PC1
t-SNE gives up on preserving distances. It converts them into probabilities — how likely is cell j to be picked as a neighbour of cell i — and then arranges points in two dimensions so that the same probabilities come out. Neighbours stay neighbours; everything else is negotiable.
Perplexity sets how many neighbours each point is expected to have. It is the one knob that matters, and moving it does not just polish the picture — it changes what the picture claims. Drag it and watch the rare types detach and re-merge.
The three panels underneath are the part that gets forgotten. They are the same cells, the same perplexity, three random seeds. The islands are stable; their arrangement is not. So the distance between two islands does not show how closely their cell types are related.
SeeThe same cells drawn at five neighbourhood sizes, and three runs of identical settings that disagree with each other.
Learnt-SNE keeps track of who is near whom and nothing else: not distances, not cluster sizes, and not where the clusters sit on the page.
Lineages
Cell types
UMAP builds a weighted neighbour graph and then finds a two-dimensional layout whose graph looks the same. It is much faster than t-SNE and tends to keep more of the large-scale arrangement, which is why it took over single-cell biology almost completely.
n_neighbors is the real parameter: small values ask the algorithm to respect fine local structure and it will happily shatter a population into fragments; large values ask it to respect the global shape and it will merge distinct types. min_dist is cosmetic — it controls how tightly points may be packed, and nothing else. Moving one changes the analysis; moving the other changes the poster.
Then switch the colouring to donor. The pancreas cells came from four different people, and that fact is written all over the map.
SeeTwelve parameter combinations, plus the same map recoloured by which of four people each cell came from.
LearnWhich UMAP setting changes the analysis and which only changes the picture, and how an unwanted factor can hide inside a convincing map.
Legend
- kNN can name the cell type
- —
- kNN can name the donor
- —
- silhouette, by cell type
- —
A 15-neighbour vote on these coordinates recovers the published cell type almost perfectly. It also recovers which of four people the cell came from, against a — baseline. The section “Evaluate the clusters” returns to this.
DBSCAN needs no cluster count. It needs a radius ε and a population minPts: a point with at least minPts neighbours inside ε is a core point, touching core points merge into a cluster, and anything left over is labelled noise rather than forced into a group. Being allowed to say “this point belongs to nothing” is genuinely useful.
The catch is that ε is global. This dataset spans a — range in population size, from — down to types with a handful of cells. Turn ε down far enough to keep those rare types separate and the common ones shatter; turn it up enough to hold the common ones together and the rare ones dissolve into noise. No setting does both; try the whole range of the slider.
SeeDBSCAN re-clustering live as you move its radius, scored four different ways at the same time.
LearnOne global radius cannot serve clusters of very different density, and clustering the 2-D picture instead of the real data flatters your results.
k-distance plot
Every cell's distance to its 10th neighbour, sorted. The textbook advice is to put ε at the knee. The line marks where you have it.
- clusters
- —
- noise
- —
- ARI
- —
- types found
- —
HDBSCAN runs DBSCAN at every ε at once. Shrinking the radius builds a hierarchy: clusters appear, survive for a while, then split into children. Rather than choosing a radius, it measures how long each candidate cluster persists across that hierarchy and keeps the ones that last. A dense cluster and a sparse cluster can each be selected at the scale where each is stable.
The condensed cluster tree shows that hierarchy. Width is population, downward is decreasing radius, and the highlighted bands are what the excess-of-mass rule selected. min_cluster_size is the only parameter, and it says what you are willing to call a cluster at all.
The section “Evaluate the clusters” compares the two methods at their best, and the scores disagree: DBSCAN wins on ARI while HDBSCAN finds twice as many cell types (10 against 5, counting a type as found when at least half of its cells fall in one cluster; by the F1 ≥ 0.5 rule used in the results table the counts are 12 against 4). ARI rewards getting the bulk of the cells right, and the bulk of these cells are beta, alpha and delta. Lumping everything into three big blobs scores well. The metric you pick decides who wins.
SeeThe cluster hierarchy drawn as a tree, and two algorithms that swap places depending on which score you quote.
LearnHDBSCAN chooses clusters by how long they persist rather than by a radius, and a disappointing score can mean the clustering found something real that you did not want.
Condensed cluster tree
Now the published cell types, the answer key, come out. Two algorithms, four scores, and they do not agree with each other about who won — which is the most useful thing this workflow has to teach.
DBSCAN takes the higher ARI with — clusters and a class-F1 of —. HDBSCAN scores lower on ARI and reaches — — nearly three times better at actually resolving the individual cell types. ARI rewards getting the bulk of the cells right, and the bulk of these cells are beta, alpha and delta, so lumping the rest together is a winning strategy under that metric and a useless one in a lab.
Then the harder question: why is every score mediocre when the UMAP picture looks so clean?
SeeBoth algorithms at their best, scored four ways, and what the clusters are actually made of.
LearnThat the metric you report decides which method looks better, and that a low score can mean the clustering found something real that you did not ask for.
| Best run | clusters | noise | ARI | AMI | purity | class F1 | types at F1 ≥ 0.5 |
|---|
| Step | Use it when | It will not | The knob that matters |
|---|---|---|---|
| Normalize | Always, before anything else. | Fix a batch effect — depth and batch are different problems. | The target total; log or not. |
| Select genes | You want contrast and speed. | Keep a gene that matters in only a few rare cells. | How many to keep. |
| PCA | You need a fast, linear, reversible denoiser, or axes you can read. | Separate anything that is not linearly separable. | Number of components. |
| t-SNE | You want local neighbourhoods rendered clearly for a figure. | Preserve distances, densities or cluster sizes; two seeds give two maps. | perplexity. |
| UMAP | Same job, faster, with somewhat more global structure kept. | Be a distance-preserving embedding, whatever the picture suggests. | n_neighbors. Not min_dist. |
| DBSCAN | Clusters are similar in density and you want outliers rejected. | Handle clusters of different density with one ε. | ε, at the k-distance knee. |
| HDBSCAN | Density varies, or you do not know how many clusters there are. | Rescue you from a representation that encodes the wrong thing. | min_cluster_size. |
At each method’s best-ARI setting, DBSCAN finds 3 clusters and HDBSCAN finds 24. DBSCAN scores higher on ARI; HDBSCAN scores higher on class F1.
| Best-ARI run, 50-component PCA space | Clusters | ARI ↑ | Class F1 ↑ | Unassigned |
|---|---|---|---|---|
| DBSCAN · best ARI in sweep | 3 | 0.397 | 0.234 | 26.0% |
| HDBSCAN · best ARI in sweep | 24 | 0.356 | 0.677 | 25.3% |
A 15-neighbour vote in the same PCA space names which of the four donors a cell came from 93.0% of the time, against 25% by chance. The clusters partly reflect the donor as well as the cell type.
Main findings
- Which method wins depends on the score. DBSCAN takes ARI; HDBSCAN resolves roughly three times as many individual cell types.
- t-SNE and UMAP keep neighbours, not distances. Cluster membership is reproducible; cluster position, orientation and apparent size are not. Three seeds at one perplexity are in the t-SNE section.
- The clusters track donors as well as cell types. A 15-neighbour vote on the same space names which of four donors a cell came from, far above the 25% baseline. Many clusters split a cell type by person.
- scRNA-seq, and a count
- as used here: for one individual cell, how many times each gene's message was captured. It is copies caught, not copies present — which is why most of the table is zero and why absence and "not sampled" look identical.
- the design matrix
- as used here: one row is one cell, one column is one gene, and the entry is that count. The published cell-type annotation is not a column of it; it is the answer key, and it is held back until the last step.
- sparsity
- the share of the matrix that is exactly zero. Here it is most of it, and that is the normal shape of this data rather than a defect.
- depth normalisation, and log1p
- as used here: divide every cell by its own total count, multiply by a common target, then take log(1 + x). The first half removes how well each cell happened to be sampled; the log stops a few enormously expressed genes dominating every distance. It adds no biology — it only makes the later comparisons about the right thing.
- highly variable genes (HVG)
- as used here: genes that vary more than their own expression level predicts, scored by binned normalised dispersion, of which the top 2,000 are kept. This is the one step that throws information away on purpose.
- PCA, and a principal component
- a rotation, not a selection: it finds the directions along which the cells vary most and re-expresses every cell on those axes. Each component is a weighted blend of all the input genes. Here it is also the denoiser — everything downstream runs on the first 50 components rather than on genes.
- loadings
- the weights that say which genes define one principal component. They are the only axes on this page that can be read directly.
- t-SNE, and perplexity
- a map that preserves neighbourhoods and nothing else. Perplexity sets how many neighbours each point is expected to have; moving it does not polish the picture, it changes what the picture claims. Distances between islands carry no meaning.
- UMAP, n_neighbors and min_dist
- as used here: n_neighbors is the real parameter — small values shatter a population, large values merge distinct types. min_dist is cosmetic: it controls how tightly points may pack and nothing else. Moving one changes the analysis; moving the other changes the poster.
- Procrustes alignment
- as used here: rotating and scaling separately-run layouts onto a common frame so that a slider does not flip the whole cloud between settings. It is a presentation device, not part of either method.
- DBSCAN, ε and minPts
- density clustering with no cluster count: a point with at least minPts neighbours inside radius ε is a core point, touching core points merge, and the rest is labelled noise rather than forced into a group. ε is global, which is the whole problem when populations differ in density.
- the k-distance knee
- every cell's distance to its k-th neighbour, sorted. The textbook advice is to put ε at the bend.
- HDBSCAN, and min_cluster_size
- DBSCAN at every ε at once. Shrinking the radius builds a hierarchy; clusters are kept by how long they persist in it rather than by a chosen radius, so a dense and a sparse cluster can each be selected where each is stable. min_cluster_size is the only parameter, and it says what you are willing to call a cluster at all.
- ARI (adjusted Rand index)
- agreement between a clustering and the answer key, corrected for chance. As used here it is dominated by the big populations, so lumping the rare types together is a winning strategy under it — and a useless one in a lab.
- AMI, purity, class F1
- three other ways of asking the same question. Class F1 is the one that changes the ranking on this page: it takes each cell type's best-matching cluster and scores it by F1, so it punishes both missing a type and mixing others into it, and it cannot be won by lumping.
- silhouette
- how much closer a point sits to its own group than to the nearest other group. Reported here for the annotation, in both the 50-dimensional space and the 2-D picture — and the two differ a lot, which is the point.
- batch effect
- as used here: the donor. These cells came from four people, and a nearest-neighbour vote recovers which one far above chance. It is not noise; it is signal nobody asked for, and it is what the clustering scores are spending themselves on.
- live versus replayed
- as used here, and as the panels say themselves: live panels compute in the browser while you drag — the PCA projection, DBSCAN, the gene threshold and every score that moves. Replayed panels show results computed in advance because they take minutes rather than milliseconds — the t-SNE and UMAP grids and the HDBSCAN sweeps.