Chatomics Field GuideWhat They Don't Teach You

Comparison · single cell toolkit

Seurat vs Scanpy: Which One Should You Use?

Feature parity is high, so the real decision comes down to which language the rest of your team already lives in and how many cells you're about to throw at it.

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

The verdict

Default to Seurat if you're the typical reader of this site: a wet-lab biologist or early-career analyst working mostly in R, whose collaborators and downstream stats (DESeq2, edgeR, limma) already live in Bioconductor. Seurat's v5 multimodal and spatial support (Visium, SLIDE-seq, MERFISH, Xenium) is native, its documentation and tutorials are aimed at exactly this audience, and its BPCells backing store plus sketch-based sampling now handle the memory problems that used to make it fall over on large datasets. The Wilcoxon-based FindAllMarkers output is also what most published single-cell papers still report, which matters when you're trying to compare your results to the literature.

Switch to Scanpy when your pipeline needs to live in Python: you're feeding results into scVI, squidpy, or other scverse tools, you're building automated pipelines in Snakemake or Nextflow where your team already scripts in Python, or you expect to push past a few million cells and want to build on AnnData's sparse, backed-mode design and experimental Dask support from day one rather than retrofitting it later. If your dataset is already an .h5ad from a public repository or a collaborator, staying in Scanpy avoids a conversion step that can silently introduce the gene x cell / cell x gene transpose bug. Neither tool wins on raw clustering accuracy against purpose-built alternatives like OSCA or scrapper, so if accuracy on a specific dataset is the deciding factor, benchmark against those before committing to either.

Seurat and Scanpy solve the same problem with different plumbing. Seurat is an R package built around an S4 object that stores your expression matrix genes-by-cells. Scanpy is a Python library built around AnnData, which stores the same matrix cells-by-genes. That flip sounds cosmetic until you load an .h5ad file into R, or a Seurat .rds into Python, and everything comes out transposed.

The deeper difference is buried in the defaults. Both tools run the same conceptual pipeline: normalize, pick highly variable genes, run PCA, build a KNN/SNN graph, cluster, run UMAP, call marker genes. But the algorithms behind each step diverge: Seurat's FindVariableFeatures and Scanpy's highly_variable_genes use different variance models, their PCA scaling isn't identical, and their clustering implementations aren't the same code. Feed both tools what looks like matching parameters and you can get 6,000 highly variable genes out of one and 5,132 out of the other, with no error or warning telling you they disagreed.

At scale, the difference shows up as memory and wall-clock time. Seurat v5 added sketch-based sampling and BPCells-backed matrices specifically because dense-matrix operations blew up on datasets in the hundreds of thousands to millions of cells. Scanpy was built around AnnData's sparse, on-disk-friendly layout from the start, and it has experimental Dask support for pushing past a million cells. Neither tool is a clear winner on accuracy: a 1.3-million-cell mouse brain benchmark put both in the middle of the pack for speed, and both landed well behind purpose-built tools like OSCA and scrapper on clustering accuracy for at least one dataset.

Head to head

CriterionSeuratScanpyEdge
Native object formatSeurat objects store the expression matrix genes-by-cells inside an S4 object, with slots for metadata, PCA, and UMAP coordinates.AnnData stores the same matrix cells-by-genes, with obs/var/obsm slots for metadata and embeddings.Tie
Neither orientation is 'correct'; it only has to match whatever tool reads the file next.
Highly variable gene selection defaultsFindVariableFeatures(nfeatures=6000, mean.cutoff=c(0.0125,3), dispersion.cutoff=c(0.3,Inf)) returned about 6,000 genes in a documented comparison.sc.pp.highly_variable_genes(min_mean=0.0125, max_mean=3, min_disp=0.3) with the matching cutoffs returned about 5,132 genes on the same dataset.Tie
'Equivalent' parameters are not equivalent outputs; re-derive HVGs independently in each tool rather than assuming parity.
Speed on a 1.3-million-cell datasetSeurat finished in roughly 2.5 to 3 hours in a published mouse brain benchmark, on par with OSCA.Scanpy landed at an intermediate speed in the same benchmark, slower than Seurat/OSCA and much slower than RAPIDS' roughly 20 minutes.Seurat
Single benchmark, single dataset; don't extrapolate to every hardware setup.
Memory behavior at scaleSeurat has documented memory blowups during integration, SCTransform, and dense-matrix clustering on hundreds-of-thousands-of-cells datasets; v5's sketch-based sampling, BPCells backing store, and igraph-based clustering are the mitigations.AnnData's sparse, on-disk 'backed' representation is scanpy's default answer to the same problem, plus experimental Dask integration for going past a million cells.Scanpy
Seurat v5 has closed much of this gap but still requires you to opt into the memory-safe path (igraph clustering, BPCells) rather than getting it by default.
Clustering accuracy vs. purpose-built toolsOn the BE1 lung cancer benchmark dataset, Seurat's default pipeline reached an Adjusted Rand Index of 0.68 to 0.70.Scanpy's default pipeline landed in the same 0.68 to 0.70 ARI range on the same dataset.Tie
Both trail OSCA and scrapper's 0.95 to 0.97 ARI on this dataset; the gap traces mainly to HVG selection method, not R vs. Python.
Multimodal and spatial supportSeurat v5 natively handles multimodal integration (e.g., paired scRNA-seq + scATAC-seq) and spatial platforms including Visium, SLIDE-seq, MERFISH, and Xenium.Scanpy handles the equivalent spatial and multimodal work mainly through companion scverse packages like squidpy and muon rather than inside scanpy itself.Seurat
Functionality exists on both sides; Seurat's is more consolidated in one package.
Differential expression defaultsFindAllMarkers defaults to a Wilcoxon rank-sum test and reports avg_log2FC computed with Seurat's own formula.rank_genes_groups computes fold change and significance with scanpy's own formula, which is not numerically identical to Seurat's avg_log2FC even on the same input data.Tie
Never merge or directly compare marker tables produced by the two tools without re-deriving one from the other's normalized data.
Marker gene visualizationSeurat has no native stacked violin plot function; users build one with cowplot or patchwork.Scanpy has a native stacked violin function for the same plot.Scanpy
A small but frequently-requested gap that the Seurat developers have not closed.
Cross-ecosystem interoperabilityMoving a Seurat object into Python requires an explicit conversion step (e.g., via anndataR or sceasy), including transposing the matrix orientation.AnnData's .h5ad is the native scverse format; anndataR lets R read/write it directly, but converting the other way into a full Seurat object still requires an extra step.Tie
Interoperability exists in both directions but is never automatic; validate normalization and HVG selection survived the round trip.

Use Seurat when

  • Your collaborators, statistics team, and downstream tools (DESeq2, edgeR, limma) already live in R and Bioconductor, and format conversion would slow down every handoff.
  • You need native multimodal or spatial support (Visium, SLIDE-seq, MERFISH, Xenium, CITE-seq) without stitching together separate packages.
  • Your dataset is in the hundreds of thousands to low millions of cells and you're willing to opt into v5's sketch-based sampling and BPCells-backed matrices to keep memory in check.
  • You want your marker gene output (Wilcoxon test, avg_log2FC) to line up with the format most published single-cell papers already report.
  • Your lab's existing analysis scripts, lab notebook, and training materials are already built around Seurat objects.

Use Scanpy when

  • Your stack is already Python (scikit-learn, PyTorch, scVI, squidpy) and you want AnnData objects flowing straight into those tools without conversion.
  • You're building automated, reproducible pipelines in Snakemake or Nextflow where your team scripts in Python by default.
  • You expect to scale past a few million cells and want to build on AnnData's sparse, backed-mode design and experimental Dask support from the start.
  • You need scanpy's native stacked violin plot or other scverse-specific visualizations without extra plotting workarounds.
  • Your project touches other scverse tools like squidpy for spatial data or scvi-tools for integration, where AnnData is the native currency.

Switching between them

Moving between Seurat and Scanpy is never a drop-in swap. The matrix orientation flips: Seurat stores genes-by-cells, AnnData stores cells-by-genes, so a silent transpose bug will corrupt every normalization and clustering step downstream without throwing an error. Metadata access syntax also differs (seurat_obj@meta.data vs. adata.obs), and neither tool's HVG thresholds transfer directly: matched-looking parameters (nfeatures=6000 vs. min_disp=0.3-style cutoffs) produced 6,000 genes in Seurat versus 5,132 in Scanpy in a documented comparison, so re-derive and check gene overlap rather than assuming the same gene set. Clustering resolution values are not portable either, since the two tools use different KNN/SNN graph construction and different clustering implementations; a resolution of 0.5 in one tool does not produce the same cluster count or composition in the other, so re-validate against known marker genes after any migration. If you're merging marker gene tables from both tools, don't treat avg_log2FC as the same statistic across them: both packages compute it differently from a naive log-fold-change definition, and differently from each other. For object conversion itself, anndataR lets R read and write native .h5ad files directly and convert to/from Seurat or SingleCellExperiment objects; sceasy and zellkonverter are the other common bridges, but always spot-check that normalization and HVG selection survived the round trip before trusting downstream results.

Pitfalls with either

  • Assuming 'equivalent' HVG parameters give the same gene set across tools: matched mean/dispersion cutoffs produced 6,000 genes in Seurat's FindVariableFeatures versus 5,132 in Scanpy's highly_variable_genes, so check gene overlap explicitly rather than trusting the parameter match.
  • Porting a clustering resolution value from one tool to the other unchanged: different KNN/SNN graph construction and clustering algorithms mean resolution=0.5 in Seurat is not resolution=0.5 in Scanpy, so re-validate cluster count and identity against marker genes each time.
  • Casting a large Seurat matrix to dense form for clustering on datasets in the hundreds of thousands of cells: switch to the igraph-based clustering method instead, which avoids the dense-matrix memory blowup documented in Seurat's GitHub issues.
  • Merging or directly comparing avg_log2FC values from a Seurat marker table and a Scanpy marker table: both compute fold change with their own formula, and neither matches a naive log-fold-change definition, so don't treat the numbers as interchangeable.
  • Forgetting the gene x cell vs. cell x gene transpose when moving a matrix between R and Python: a silent orientation bug corrupts every downstream normalization and clustering step without raising an error, so verify dimensions immediately after any conversion.
  • Applying a blanket 5-10% mitochondrial content cutoff in either tool's default QC step without checking your cell types: some populations naturally run high in mitochondrial content, and a default cutoff can filter out real biology instead of dead cells.

Questions people ask

Is Seurat or Scanpy faster on large single-cell datasets?

In a published benchmark on 1.3 million mouse brain cells, RAPIDS was fastest at roughly 20 minutes, Seurat and OSCA finished in about 2.5 to 3 hours, and Scanpy came in at an intermediate speed between those two groups. Neither Seurat nor Scanpy is the fastest option at that scale; if raw speed on multi-million-cell data is your bottleneck, look at GPU-accelerated tools like RAPIDS first.

Can I convert a Seurat object to an AnnData object and back?

Yes, but not silently. The anndataR package lets R read and write native .h5ad files and convert between Seurat/SingleCellExperiment objects and AnnData. You still need to account for the matrix orientation flip (genes-by-cells in Seurat vs. cells-by-genes in AnnData) and re-check that normalization and HVG selections survived the round trip.

Do Seurat and Scanpy give the same clustering results on the same data?

No. Differences in HVG selection, PCA scaling, KNN/SNN graph construction, and clustering algorithm defaults are large enough that one benchmark described the gap as equivalent to downsampling your data to as little as 4% of original reads or 16% of original cells in terms of downstream biological conclusions. Don't assume a resolution or parameter value ports directly from one tool to the other.

Does Seurat handle spatial or multimodal data as well as Scanpy?

Seurat v5 has native support for multimodal integration (pairing scRNA-seq with scATAC-seq, for example) and spatial platforms including Visium, SLIDE-seq, MERFISH, and Xenium. Scanpy handles the equivalent work mainly through companion scverse packages like squidpy and muon rather than inside scanpy itself, so the functionality exists but lives in a separate package.

Which tool should a wet-lab biologist learn first?

Pick whichever language the rest of your collaborators and pipelines already use, since that determines how much translation work you'll do on every handoff. If you don't have that constraint yet, Seurat's tutorials and the surrounding R/Bioconductor documentation are generally considered gentler for someone without a programming background, but both tools require you to understand what each default step is actually doing before you trust the output.

Related pages

Related reading on the blog

Sources

  1. Benchmarking large-scale single-cell RNA-seq analysis — Speed and clustering accuracy comparison across Seurat, Scanpy, OSCA, scrapper, and RAPIDS on 1.3M-cell and BE1 lung cancer datasets
  2. Seurat - Satija Lab — Seurat v5 features: sketch-based sampling, BPCells, multimodal and spatial support
  3. Scanpy documentation — Scanpy's scalability claims and scverse/NumFOCUS governance
  4. AnnData interoperability in R (anndataR) — Round-trip conversion between Seurat/SingleCellExperiment and AnnData/scverse objects
  5. Do you really understand log2Fold change in single-cell RNAseq data? — avg_log2FC calculation discrepancies in Seurat's FindAllMarkers
  6. stacked violin plot for visualizing single-cell data in Seurat — Scanpy's native stacked violin function vs. Seurat's lack of one
  7. transpose single-cell cell x gene dataframe to gene x cell — Matrix orientation difference between Seurat (gene x cell) and Scanpy/AnnData (cell x gene)
  8. Seurat GitHub Discussion: Different HVG output between Seurat and Scanpy — 6,000 vs 5,132 highly variable genes from matched parameters
  9. Seurat GitHub Issue: Ultra-Large Matrices (>2^31 values) — Seurat memory issues on large datasets during integration and SCTransform
  10. Seurat GitHub Issue: Memory with Clustering Leiden algorithm — igraph-based clustering recommendation to avoid dense-matrix memory blowups