From gene counts to cell groups · Workflow

How a table of gene counts for 8,569 human pancreas cells becomes clusters, checked against the published cell types. Every step can be tried with its own controls.

Some controls recompute in your browser: the gene threshold, the PCA axes and the live DBSCAN panel. The t-SNE, UMAP and HDBSCAN controls switch between runs computed in advance. The gene-selection slider shows which marker genes a different threshold would keep; the later steps always use the standard 2,000 genes.

Cells × genes in the count matrix

8,569 × 20,125

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.

A · Baron human pancreas — raw counts —
120 cells (rows, grouped by their published cell type) × 80 genes (columns). Darker means higher expression after depth normalisation and a log transform. Almost everything is empty: — of the full matrix is exactly zero, because a single cell contains too few transcripts to sample every gene. The block structure you can just about see is the signal every method below is chasing.
<
A · Pick two genes
Every dot is one cell, coloured by its lineage (the group of related cell types it belongs to). Log-normalised expression; points are jittered slightly because counts are integers and would otherwise stack into stripes. Try INS vs GCG, then try any two housekeeping genes such as ACTB vs GAPDH — the second pair separates nothing, which is the point.
B · Distance concentration 2
Pairwise distances among 600 uniformly random points in d dimensions, rescaled so the mean sits at 1.0. The spread — is (max − min) / mean: at d = 2 the nearest point is dramatically nearer than the farthest; by d = 512 they are nearly the same distance away. Real data is not uniform noise — which is exactly why dimensionality reduction can work at all.

Stage 1 hands stage 2 one matrix of counts — and the published annotation stays behind until the very last step.

Where the workflow ends: each dot is one cell, placed by UMAP so that cells with similar gene activity sit close together, and coloured by its published cell type. The sections below build this picture step by 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.

A · Before and after, six real cells
Each row is one cell, ordered by sequencing depth; each column is a marker gene. In raw counts the deep cells look uniformly louder. In normalized the same cells are on comparable footing and the pattern that survives is the cell's identity. Switch to depth to see the totals the correction divides by.

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.

A · Mean expression against variability 2,000

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.

Every gene with a finite variability score is a point: — of them. Dark points are kept at the current threshold. The workflow uses the standard 2,000; drag the slider to see which genes a different cut would keep. Names appear on hover for the strongest scores and for every marker gene.

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.

A · Principal components

Variance explained

Top loadings — PC1

The first two components separate the big endocrine populations from everything else, but squash the rare types on top of each other. PC1 and PC2 together explain — of the variance; the 50 components that feed the rest of this page explain —. Most of the remaining variance is counting noise.

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.

A · Perplexity sweep 30

Lineages

Cell types

Points morph between the five perplexities that were actually run (5, 15, 30, 50, 100); between them the points glide by interpolation, so you can follow each one. Runs are Procrustes-aligned to a common frame so the slider does not flip the whole cloud. Click a lineage or a cell type to isolate it.
B · Same data, same perplexity, three seeds
Perplexity 30, random initialisation, seeds 1–3, each shown in its own frame without alignment. Cluster membership is reproducible. Cluster position, orientation and apparent size are not. Distances between islands on a t-SNE plot carry no meaning.

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.

A · Parameter grid 15 0.1

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.

All twelve combinations of n_neighbors ∈ {5, 15, 50, 200} and min_dist ∈ {0.0, 0.1, 0.5} were computed; the sliders switch between them. Aligned to a shared frame.
B · t-SNE and UMAP, linked hover either panel
The same 8,569 cells under both methods, at their default settings. Hovering highlights the same cell's 60 nearest neighbours (computed in the 50-dimensional PCA space, not in either picture) in both panels at once. Neighbourhoods survive the trip; layouts do not.

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.

A · DBSCAN, live — 10

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
—
DBSCAN is running in your browser on the 2-D UMAP coordinates, with a uniform grid index so it stays interactive at 8,569 points. Scores are computed live against the published annotation. “Types found” counts how many of the 14 cell types have a majority of their cells inside a single cluster — a stricter test than ARI.
B · 72 runs: PCA space versus UMAP picture
72 DBSCAN runs across ε and minPts, scored four ways. Use the switch to change the space. Clustering the 50-dimensional PCA representation scores far worse than clustering the 2-D UMAP picture, because the picture has already thrown away the structure that makes the problem hard. The results in “Evaluate the clusters” come from the PCA space.

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.

A · HDBSCAN 5

Condensed cluster tree

Computed on the 50-dimensional PCA space. In membership view, opacity is the probability that a point really belongs to its cluster — HDBSCAN reports a confidence, not just a label. Grey points are noise.

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.

A · Four scores, two algorithms
Best runclustersnoise ARIAMIpurity class F1types at F1 ≥ 0.5
Best run per algorithm, chosen by ARI, in the 50-dimensional PCA space. Class F1 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 — unlike ARI, it cannot be won by lumping. Read it against the ARI column.
B · What the clusters are made of
Each column is one HDBSCAN cluster, each band a share of its cells. Switch between cell type and donor.
The clusters split cell types by donor. A 15-neighbour vote on this same space names the cell type — of the time, so the structure is unambiguously there. It also names which of the four donors a cell came from — of the time, against a — baseline. — The clusters are real. Many of them are splitting cell types by which person the pancreas came from. Batch effect is not noise; it is signal you did not ask for, and it is what these scores are spending themselves on.
StepUse it whenIt will notThe knob that matters
NormalizeAlways, before anything else. Fix a batch effect — depth and batch are different problems. The target total; log or not.
Select genesYou want contrast and speed. Keep a gene that matters in only a few rare cells. How many to keep.
PCAYou need a fast, linear, reversible denoiser, or axes you can read. Separate anything that is not linearly separable. Number of components.
t-SNEYou want local neighbourhoods rendered clearly for a figure. Preserve distances, densities or cluster sizes; two seeds give two maps. perplexity.
UMAPSame job, faster, with somewhat more global structure kept. Be a distance-preserving embedding, whatever the picture suggests. n_neighbors. Not min_dist.
DBSCANClusters are similar in density and you want outliers rejected. Handle clusters of different density with one ε. ε, at the k-distance knee.
HDBSCANDensity varies, or you do not know how many clusters there are. Rescue you from a representation that encodes the wrong thing. min_cluster_size.
Three habits this workflow is arguing for. Cluster in the representation you trust, not in the picture — the section “Cluster with one density radius” measures how much that shortcut inflates the scores. Report more than one score, because the two algorithms here swap places depending on which you quote. And when a clustering disappoints, find out what it did capture before blaming the algorithm: here it found the four donors, faithfully and unhelpfully.

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 spaceClustersARI ↑Class F1 ↑Unassigned
DBSCAN · best ARI in sweep30.3970.23426.0%
HDBSCAN · best ARI in sweep240.3560.67725.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.