Chatomics Field GuideWhat They Don't Teach You

Glossary · Single-Cell and Spatial

Single-cell integration

Integration erases the exact signal it was designed to erase, the trick is knowing when that's the goal and when it's the bug.

By Ming "Tommy" Tang, Director of Bioinformatics in Big Pharma · Reviewed September 2026 · 2 min read

Also: batch integration, anchors, CCA, RPCA

Definition

Single-cell integration is the process of aligning single-cell datasets from different samples, donors, batches, or technologies into a shared low-dimensional space so that cells of the same type cluster together regardless of origin. Methods such as Harmony, CCA and RPCA (in Seurat's IntegrateLayers), fastMNN, and scVI operate on a reduced representation, typically PCA coordinates or a learned latent embedding, rather than on raw or normalized expression values. The result is an embedding used for clustering, UMAP visualization, and cross-sample cell-type annotation; the underlying counts matrix is generally left untouched for downstream statistical testing.

You hit this decision the moment you merge more than one 10x run into a single Seurat or AnnData object. Run PCA and UMAP without integration and cells split by donor or batch first, cell type second, naive T cells from donor A sit far from naive T cells from donor B on the plot. Integration methods (Harmony, CCA/RPCA in Seurat, fastMNN, scVI) pull the embedding so cells align by cell type instead, and every tutorial makes this look like a mandatory step between RunPCA() and FindNeighbors().

It isn't mandatory. Whether you integrate is a scientific call, not a technical default, and it decides what questions your downstream clustering and differential expression can still answer once you're done.

Why it matters

Integrate when you're building a reference map across donors or need every sample's naive B cells to land in the same cluster before annotation, that's the case integration is built for, and skipping it leaves you annotating the same cell type five different times under five different donor-driven clusters. Skip it when the batch axis might be the biology, as in comparing COVID versus healthy PBMCs to find an activated B cell state that shouldn't exist in the healthy samples. Integrate that comparison with Harmony or CCA and the activated state gets folded back into the general B cell cluster, because the algorithm's explicit job is to remove the kind of shift you're trying to detect. The failure is silent: you get a clean, well-mixed UMAP and no warning that the disease signal is gone.

Where people get it wrong

People use "integration" and "batch correction" as if they're one operation applied to one matrix. They usually aren't. Harmony and CCA/RPCA correct the PCA embedding used for clustering and visualization; they don't rewrite the counts matrix. Run differential expression directly on "integrated" assay values or a Harmony-corrected embedding, and the p-values reflect an artifact of the alignment algorithm rather than biology. The other common trap: treating uneven sequencing depth across batches as a batch effect integration should fix, when it's really a technical covariate that most integration methods can't distinguish from real signal, it needs to be handled in normalization or the DE model, not papered over by the integration step.

A concrete example

A four-donor PBMC dataset in Seurat v5, split by donor as separate layers, integrated with CCA so cell types align across donors, followed by DE on the original (non-integrated) counts within one cell type.

r
obj <- NormalizeData(obj) |> FindVariableFeatures() |> ScaleData() |> RunPCA()

obj <- IntegrateLayers(
  object = obj,
  method = CCAIntegration,
  orig.reduction = "pca",
  new.reduction = "integrated.cca",
  k.anchor = 5
)

obj <- FindNeighbors(obj, reduction = "integrated.cca", dims = 1:30) |>
  FindClusters() 
obj <- RunUMAP(obj, reduction = "integrated.cca", dims = 1:30)

# DE between conditions within one annotated cell type: use RAW counts, not the integrated embedding
JoinLayers(obj)
b_cells <- subset(obj, cell_type == "B")
markers <- FindMarkers(b_cells, ident.1 = "COVID", ident.2 = "healthy",
                        group.by = "condition", assay = "RNA", slot = "counts")

Related terms

Questions people ask

What is the difference between integration and batch correction?

In practice on single-cell data they're used to mean the same thing: aligning cells from different samples or batches so they cluster by cell type. Technically, most integration tools (Harmony, CCA, RPCA) correct only the PCA embedding used for clustering and visualization, they leave the raw or normalized counts matrix untouched, which is why you still run differential expression on the original counts, not the integrated values.

When should I not integrate my single-cell data?

Skip integration when the difference between samples might be the biology you're testing for, for example comparing COVID versus healthy PBMCs to find an activated B cell state. Integrating that comparison folds the activated cells back into the healthy cluster because the algorithm's job is to remove exactly the shift you're hunting for.

CCA vs RPCA vs Harmony, which should I use in Seurat?

CCA (the default in IntegrateLayers) finds correlated structure between datasets and aligns well when cell types overlap strongly across samples, but it's slower and can over-correct with weak biological overlap. RPCA is faster and more conservative, better when datasets are large or only partially overlapping. Harmony corrects the PCA embedding iteratively and tends to be faster still on many samples; it's a common choice for atlas-scale integration.

Does integration change my raw gene expression values?

No, for embedding-based methods like Harmony, CCA, and fastMNN. They correct the low-dimensional space (PCA coordinates or a learned latent space), not the counts matrix, so raw or normalized counts remain valid for differential expression, ideally after collapsing to pseudobulk per sample.

How do I know if integration over-corrected my data?

Check whether known biological states you expect to differ between conditions (a disease-specific cell state, a treatment response) still show up as separate clusters after integration. If a state you had strong prior evidence for disappears into a larger cluster once you integrate, re-run the analysis without integration and compare, or annotate cell type first and only integrate within cell type.

Related pages

Related reading on the blog

Sources

  1. Seurat Integration Introduction — Seurat v5 IntegrateLayers workflow and default parameters
  2. Chapter 13: Integrating Datasets - OSCA (Bioconductor) — fastMNN mechanism and assumptions behind linear-regression batch correction
  3. How CCA alignment and cell label transfer work in Seurat — CCA mechanism, canonical variate and anchor parameters
  4. Harmony - GitHub Repository — Harmony corrects PCA embeddings, not raw expression
  5. Common mistakes when analyzing single-cell RNAseq data — Integration can erase biologically meaningful sample differences
  6. How to create pseudobulk from single-cell RNAseq data — DE should run on raw counts / pseudobulk, not integrated embeddings