| Differential Expression (Lec 06) | Differential Abundance (Lec 08) | |
|---|---|---|
| Question | For cells of the same type, which genes change between conditions? | Do cell-type proportions themselves change between conditions? |
| Tool | DESeq2 on pseudobulk |
miloR on kNN neighbourhoods |
| Unit of test | Per gene, per cell type | Per neighbourhood (a small group of similar cells) |
| Statistical test | Negative binomial GLM on counts | Negative binomial GLM on cell counts per neighbourhood |
| Output | per-gene log2FC + padj | per-neighbourhood log2FC + SpatialFDR |
Note
They are complementary, not redundant.
This works for big, well-separated clusters but breaks in three ways:
Warning
The naive cluster-level Fisher test is the most common mistake in scRNA-seq DA.
~ condition, optionally + donor) — gets a log2FC per neighbourhoodReading rules:
Read the beeswarm vertically.
- A cell type with most points above zero is enriched in STIM; below zero, depleted. - A cell type with points scattered above AND below zero contains both expanding and contracting subpopulations. - This is the case where neighbourhood-level testing recovers what cluster-level testing would miss.
prop = 0.1 (sample 10% of cells as neighbourhood centres) is the default and works for most datasets in the 5k–50k rangeplotNhoodSizeHist(milo) is the go-to diagnostic — peak between 50 and 200 means you’re setk (graph k-nearest-neighbours) from 30 → 50 or 75Warning
No replication, no inference.
| Design | Notes |
|---|---|
~ condition |
Two-condition contrast, no other covariates |
~ donor + condition |
Paired design (donors balanced across conditions) — preferred when you have it |
~ batch + condition |
Block on batch effects |
~ severity |
Continuous covariate (e.g. clinical severity score) |
~ donor + group + condition + group:condition |
Interaction tests |
The full DESeq2-style design framework is available — including interactions, continuous covariates, and random-effect-style blocking.
Running on the unintegrated kNN graph. If batch effect dominates the embedding, neighbourhoods cluster by sample, and DA falsely “detects” composition shifts that are really batch artifacts. Always run miloR on the integrated reduction. In Tutorial 08, the Harmony reduction from Tutorial 05 is stored under the name "HARMONY" so the call is:
Synthetic donor splits passed as biological replicates. Splitting one large library into “pseudo-donors” gives miloR fake degrees of freedom — the test is anti-conservative. The workshop tutorial flags this with a set.seed(2026) synthetic split for illustration only; never report those p-values as real findings.
Imbalanced sample sizes. 8 STIM vs 1 CTRL gives almost no information about the CTRL distribution. Aim for balanced designs.
Ignoring seurat_annotations mapping. Without annotateNhoods(), you’ll get neighbourhood-level results but no easy way to summarise by cell type. Always run annotateNhoods after testNhoods.
Reading the UMAP without the SpatialFDR filter. A red cluster of dots that all have SpatialFDR > 0.5 is just visual noise. Filter da_results$SpatialFDR < 0.1 before drawing conclusions.
miloR works at the neighbourhood level on the integrated kNN graph, with NB GLM + SpatialFDRSingle Cell RNA-seq Workshop · Lecture 08