| name | bio-single-cell-batch-integration |
| description | Integrate multiple scRNA-seq samples/batches using Harmony, scVI, Seurat anchors, and fastMNN. Remove technical variation while preserving biological differences. Use when integrating multiple scRNA-seq batches or datasets. |
| tool_type | mixed |
| primary_tool | Harmony |
Version Compatibility
Reference examples tested with: anndata 0.10+, scanpy 1.10+, scikit-learn 1.4+, scvi-tools 1.1+
Before using code patterns, verify installed versions match. If versions differ:
- Python:
pip show <package> then help(module.function) to check signatures
- R:
packageVersion('<pkg>') then ?function_name to verify parameters
If code throws ImportError, AttributeError, or TypeError, introspect the installed
package and adapt the example to match the actual API rather than retrying.
Batch Integration
Integrate multiple scRNA-seq datasets to remove batch effects while preserving biological variation.
Tool Comparison
| Tool | Speed | Scalability | Best For |
|---|
| Harmony | Fast | Good | Quick integration, most use cases |
| scVI | Moderate | Excellent | Large datasets, deep learning |
| Seurat CCA/RPCA | Moderate | Good | Conserved biology across batches |
| fastMNN | Fast | Good | MNN-based correction |
Harmony (R/Python)
Goal: Remove batch effects from merged scRNA-seq datasets using Harmony's iterative correction of PCA embeddings.
Approach: Run PCA on merged data, iteratively adjust embeddings to mix batches while preserving biological variation, and use corrected embeddings for downstream analysis.
"Integrate my batches" → Merge samples, preprocess jointly, correct technical variation in the embedding space, and cluster on corrected coordinates.
R with Seurat
library(Seurat)
library(harmony)
merged <- merge(sample1, y = list(sample2, sample3), add.cell.ids = c('S1', 'S2', 'S3'))
merged <- NormalizeData(merged)
merged <- FindVariableFeatures(merged)
merged <- ScaleData(merged)
merged <- RunPCA(merged)
merged <- RunHarmony(merged, group.by.vars = 'orig.ident', dims.use = 1:30)
merged <- RunUMAP(merged, reduction = dims
merged FindNeighborsmerged reduction dims
merged FindClustersmerged resolution
Multiple Batch Variables
merged <- RunHarmony(merged, group.by.vars = c('sample', 'technology'),
dims.use = 1:30, max.iter.harmony = 20)
Python with Scanpy
import scanpy as sc
import scanpy.external as sce
adata = sc.read_h5ad('merged.h5ad')
sc.pp.normalize_total(adata, target_sum=1e4)
sc.pp.log1p(adata)
sc.pp.highly_variable_genes(adata, batch_key='batch')
adata = adata[:, adata.var.highly_variable]
sc.pp.scale(adata)
sc.tl.pca(adata)
sce.pp.harmony_integrate(adata, key='batch')
sc.pp.neighbors(adata, use_rep='X_pca_harmony')
sc.tl.umap(adata)
sc.tl.leiden(adata)
scVI (Python)
Goal: Integrate batches using a deep generative model that learns a shared latent space.
Approach: Train a variational autoencoder (scVI) conditioned on batch to learn batch-invariant latent representations, then use the latent space for clustering and visualization.
import scvi
import scanpy as sc
adata = sc.read_h5ad('merged.h5ad')
scvi.model.SCVI.setup_anndata(adata, batch_key='batch')
model = scvi.model.SCVI(adata, n_latent=30, n_layers=2)
model.train(max_epochs=100, early_stopping=True)
adata.obsm['X_scVI'] = model.get_latent_representation()
sc.pp.neighbors(adata, use_rep='X_scVI')
sc.tl.umap(adata)
sc.tl.leiden(adata)
scVI with Covariates
scvi.model.SCVI.setup_anndata(adata, batch_key='batch',
continuous_covariate_keys=['percent_mito'])
model = scvi.model.SCVI(adata, n_latent=30)
model.train()
scANVI (with cell type labels)
scvi.model.SCANVI.setup_anndata(adata, batch_key='batch', labels_key='cell_type',
unlabeled_category='Unknown')
model = scvi.model.SCANVI(adata, n_latent=30)
model.train(max_epochs=100)
adata.obs['predicted_type'] = model.predict()
Seurat Integration (R)
Goal: Integrate batches using Seurat's anchor-based framework (CCA or RPCA).
Approach: Find shared biological anchors between datasets via canonical correlation analysis, then use anchors to correct expression values into a unified space.
CCA-based Integration
library(Seurat)
obj_list <- SplitObject(merged, split.by = 'batch')
obj_list <- lapply(obj_list, function(x) {
x <- NormalizeData(x)
x <- FindVariableFeatures(x, selection.method = 'vst', nfeatures = 2000)
return(x)
})
anchors <- FindIntegrationAnchors(object.list = obj_list, dims = 1:30)
integrated <- IntegrateData(anchorset = anchors, dims = 1:30)
DefaultAssayintegrated
integrated ScaleDataintegrated
integrated RunPCAintegrated
integrated RunUMAPintegrated dims
RPCA (Faster for Large Datasets)
anchors <- FindIntegrationAnchors(object.list = obj_list, dims = 1:30,
reduction = 'rpca')
integrated <- IntegrateData(anchorset = anchors, dims = 1:30)
Seurat v5 Integration
merged[['RNA']] <- split(merged[['RNA']], f = merged$batch)
merged <- IntegrateLayers(merged, method = CCAIntegration, orig.reduction = 'pca',
new.reduction = 'integrated.cca')
merged <- JoinLayers(merged)
fastMNN (R)
library(batchelor)
library(SingleCellExperiment)
sce <- as.SingleCellExperiment(merged)
corrected <- fastMNN(sce, batch = sce$batch, d = 30, k = 20)
reducedDim(sce, 'MNN') <- reducedDim(corrected, 'corrected')
Evaluate Integration
Goal: Assess whether integration successfully removed batch effects while preserving biological variation.
Approach: Compute mixing metrics (LISI, silhouette scores) and visualize batch versus cell-type separation before and after integration.
Mixing Metrics (R)
library(lisi)
lisi_scores <- compute_lisi(Embeddings(merged, 'harmony'),
merged@meta.data, c('batch', 'cell_type'))
mean(lisi_scores$batch)
mean(lisi_scores$cell_type)
Visual Assessment
DimPlot(merged, reduction = 'pca', group.by = 'batch')
DimPlot(merged, reduction = 'pca', group.by = 'cell_type')
DimPlot(merged, reduction = 'harmony', group.by = 'batch')
DimPlot(merged, reduction = 'harmony', group.by = 'cell_type')
Silhouette Score (Python)
from sklearn.metrics import silhouette_score
batch_sil = silhouette_score(adata.obsm['X_scVI'], adata.obs['batch'])
celltype_sil = silhouette_score(adata.obsm['X_scVI'], adata.obs['cell_type'])
Complete Workflow
Goal: Run end-to-end multi-sample integration from raw 10X files to clustered, integrated UMAP.
Approach: Load and merge samples, preprocess jointly, integrate with Harmony, and perform downstream clustering on corrected embeddings.
library(Seurat)
library(harmony)
samples <- list.files('data/', pattern = '*.h5', full.names = TRUE)
obj_list <- lapply(samples, Read10X_h5)
names(obj_list) <- gsub('.h5', '', basename(samples))
merged <- merge(CreateSeuratObject(obj_list[[1]], project = names(obj_list)[1]),
y = lapply(2:length(obj_list), functioni
CreateSeuratObjectobj_listi project obj_listi
merged PercentageFeatureSetmerged pattern
merged subsetmerged nFeature_RNA nFeature_RNA percent.mt
merged NormalizeDatamerged
merged FindVariableFeaturesmerged nfeatures
merged ScaleDatamerged vars.to.regress
merged RunPCAmerged npcs
merged RunHarmonymerged group.by.vars
merged RunUMAPmerged reduction dims
merged FindNeighborsmerged reduction dims
merged FindClustersmerged resolution
DimPlotmerged group.by ncol
When to Use Each Method
| Scenario | Recommended |
|---|
| Quick integration, most cases | Harmony |
| Large datasets (>500k cells) | scVI or Harmony |
| Strong batch effects | scVI |
| Reference mapping | Seurat anchors or scANVI |
| Preserving rare populations | fastMNN |
Related Skills
- single-cell/preprocessing - QC before integration
- single-cell/clustering - Clustering after integration
- single-cell/cell-annotation - Annotation after integration
- single-cell/multimodal-integration - Multi-omic integration (different from batch)