We will integrate two PBMC datasets from different 10x Chromium chemistry versions to observe and correct batch effects.
sc.concat([a, b], join='inner') does this automatically; in Seurat, merge() keeps the union of genes, so subset to the shared genes (e.g. via intersect() of the two feature sets) before or after merging.sc.tl.leiden(adata, flavor="igraph", n_iterations=2), matching the Day 2 workflow)CD3D (T cells), CD14 (Monocytes), CD79A (B cells), NKG7 (NK cells)In Seurat v5, the two chemistry versions live as separate counts layers inside the RNA assay (this happens automatically when you merge() the two objects; you can also enforce it with obj[["RNA"]] <- split(obj[["RNA"]], f = obj$batch)). Integration then operates directly on this single, layered object - there is no longer a separate IntegrateData() step as in Seurat v4.
NormalizeData, FindVariableFeatures, ScaleData, RunPCA) on the merged, layered objectIntegrateLayers(), using the PCA you already computed as input:
obj <- IntegrateLayers(
object = obj,
method = HarmonyIntegration, # or CCAIntegration / RPCAIntegration
orig.reduction = "pca",
new.reduction = "harmony"
)
# after integration, rejoin the split layers for all downstream steps
obj <- JoinLayers(obj)
HarmonyIntegration requires the harmony package (install.packages("harmony")). Using it here keeps the R and python workflows comparable, since the python side below also uses Harmony. CCAIntegration is the classic Seurat default if you prefer.FindNeighbors() and RunUMAP() at the integrated embedding via reduction = "harmony" (or whatever you passed to new.reduction) instead of "pca". import scanpy.external as sce
sce.pp.harmony_integrate(adata, key='batch') # 'batch' = your batch column in adata.obs
adata.obsm['X_pca'] and writes the corrected embedding to adata.obsm['X_pca_harmony']. It does not rebuild your neighbor graph automatically. sc.pp.neighbors(adata, use_rep='X_pca_harmony')
sc.tl.umap(adata)
sc.tl.leiden(adata, flavor='igraph', n_iterations=2)
harmony_integrate needs the harmonypy package. If it is not already in your environment, install it once with pip install harmonypy (or add harmonypy to your mamba environment).harmonypy). Start with the defaults and only tune if the UMAP shows a problem:
max_iter_harmony (default 10): maximum number of Harmony iterations. If the run reports that it did not converge, raise this (e.g. 20-30).theta (default 2): how strongly batches are pushed to mix. Higher values force stronger mixing but can begin to blend genuinely distinct cell types; lower values are gentler. This is the main knob to reach for if you see over- or under-correction.np.random.seed(0) before the call.Discuss with a neighbor:
theta in scanpy)NKG7) is preserved rather than washed out by the correction