Clusterscope
The single-cell pipeline, one step at a time.
Single-cell RNA sequencing counts how many transcripts of each gene were captured from each individual cell. Turning that table into labeled cell groups takes a chain of steps, usually run in R with the Seurat package. This page runs a simplified version of that chain in your browser. Change a setting at any step and everything downstream is recomputed.
The study behind this
Real research, summarized from a 2026 abstract. It is separate from the simulation below.
Understanding progression between neoplasia and malignancy of pancreatic ductal adenocarcinoma through single-cell RNA sequencing data
Ridhi Koner, Jason Yoo, Richard Moffitt. Georgia Tech; Winship Cancer Institute, Emory.
Question. Which cell populations and genes distinguish benign PanIN from malignant PDAC?
Methods. Public scRNA-seq data from Kras-mutant genetically engineered mouse models (Burdziak et al., 2023, GEO) were merged and normalized in Seurat, clustered, and visualized with UMAP. Clusters were annotated with the markers Pyy, Sst, Amy1, Wfdc2, Epcam and Col1a1 (Hosein et al., 2019). A heatmap of the top 20 differentially expressed genes compared the PDAC-dominant epithelial cluster with the PanIN-dominant ADM cluster.
Results. Five populations were identified: epithelial, ADM, stromal, acinar, and islet & ductal. PDAC cells were concentrated in the epithelial cluster and PanIN cells in the ADM cluster. Ten genes were elevated in PDAC:
Caveat. These results come from a single GEMM study and need validation in other models and in human tumors.
Section R right below reanalyzes the real public data. The simulation after it (steps 01 to 08) reproduces the steps of this kind of pipeline, not this data. Its populations (A to E) and genes (Gene01 to Gene40) are generic and synthetic, so nothing it shows is a result of the study above.
Her data: K4 PanIN vs K5 PDAC (real)
K4 is the KC;RIK model at the PanIN stage. K5 is the KPC;RIK model (adds one p53 floxed allele) at the PDAC primary tumor stage. Both are sorted mKate2+ lineage-traced epithelial cells, so fibroblasts and immune cells were removed before sequencing.
Loading the dataset...
Her ten genes in this reanalysis
Agrees with the abstract
Differs or cannot be confirmed
Public data: Burdziak et al., Science 2023 (GEO GSE207938). Reanalysis in Python/scanpy.
Raw counts
The starting point is a count matrix with one row per gene and one column per cell. Each entry is the number of transcript molecules (UMIs) of that gene detected in that cell. Real experiments have thousands of cells and around 20,000 genes. This one has 600 cells and 40 genes so every step can run instantly.
How the generator works: each cell gets a library size from a log-normal distribution. Each population raises its own five marker genes (Gene01 to Gene05 for A, Gene06 to Gene10 for B, and so on up to Gene25 for E). Gene26 to Gene36 are shared background genes, and Gene37 to Gene40 stand in for mitochondrial genes. Counts are drawn from a negative binomial. About 5% of cells are made deliberately degraded, with tiny libraries and a high mitochondrial share, so step 2 has something to catch.
Quality control
Some captured "cells" are broken or empty. They tend to have very few counts, and a large share of their counts come from mitochondrial genes, because cytoplasmic RNA leaks out while mitochondrial transcripts stay behind. Seurat users filter on nCount_RNA and percent.mt. Move the thresholds and watch which cells are dropped.
Normalize
A cell with twice the library size shows roughly twice the counts for every gene, which says nothing about its biology. LogNormalize divides each cell's counts by its total, multiplies by 10,000 and takes log1p. The log compresses the long right tail so a few huge values do not dominate.
Highly variable genes
Genes that vary a lot between cells, more than their average level would predict, carry most of the information about cell identity. Downstream steps use only those. Seurat's default vst method fits a loess curve of log variance against log mean on raw counts and usually keeps 2,000 genes. With only 40 genes, this page fits a straight line to log variance against log mean of the normalized values and ranks genes by how far above the line they sit.
Selected genes
Scale and PCA
ScaleData shifts each selected gene to mean 0 and standard deviation 1, so a highly expressed gene does not outweigh a quiet one. PCA then finds the directions along which cells differ most. This page computes the covariance matrix of the scaled genes and finds its eigenvectors exactly (Jacobi rotations). Seurat uses a truncated SVD (irlba), which on a matrix this small gives the same components up to sign.
The elbow plot shows how much variance each component captures. Components past the elbow are mostly noise. With five populations, you should see about four strong components.
Clustering
Seurat clusters in two calls. FindNeighbors links each cell to its k nearest neighbors in PCA space, then weights each link by how many neighbors the two cells share (Jaccard overlap, a shared-nearest-neighbor or SNN graph). FindClusters runs the Louvain algorithm, which groups cells that are more densely linked to each other than to the rest. Higher resolution gives more, smaller clusters. Past eight clusters the colors repeat.
This page does the same steps with its own small implementation: brute-force k-NN in PCA space, Jaccard SNN weights with links under 1/15 pruned (Seurat's default prune.SNN), and Louvain with node moving plus graph aggregation. It is not Seurat's code, so cluster numbers and exact boundaries can differ from what R would give on the same matrix. Clusters are numbered from 0 by size, as Seurat does.
Clusters vs ground truth
t-SNE embedding
To look at the cells in two dimensions, t-SNE places points so that cells that are close in PCA space stay close on the page. It starts from random positions and improves them over many iterations, which you can watch below. Perplexity sets roughly how many neighbors each cell tries to keep near it.
This is exact t-SNE (every pair of cells, which is fine for about 600 cells), with early exaggeration of 12 for the first 250 of 1,000 iterations. Seurat's RunTSNE uses the faster Barnes-Hut approximation from Rtsne. UMAP (RunUMAP), a related nonlinear embedding, is the more common choice today and is what the study above used; it is not implemented here. Distances between separate islands in a t-SNE plot are not meaningful: the clusters come from step 6, not from this picture.
What to try
Set perplexity to 5 and the populations shatter into small fragments, because each cell only tries to keep a handful of neighbors close. At 30 to 50 the five groups come back as solid islands.
Switch the coloring to ground truth and compare it with the clusters. Where they disagree, look back at the table in step 6.
Push the step 6 resolution to 3 or more (or drop k.param to 10 first) and watch islands get split into several colors while the layout stays the same. Clustering and embedding are separate computations. On this clean synthetic data, any resolution from about 0.1 to 2 finds the same five groups; real data is rarely that forgiving.
Marker genes
To give a cluster a name, you find the genes that set it apart. FindAllMarkers compares each cluster against all other cells, gene by gene, with a Wilcoxon rank-sum test, and reports the log2 fold change and the share of cells expressing the gene in each group (pct.1 inside, pct.2 outside). P-values are Bonferroni-adjusted across all 40 genes. Here the true answer is known, so the last column shows which population each gene was planted in.
Click a gene in the table or the dot plot to color the embedding by its expression, as FeaturePlot does.