Single-Cell RNA-Seq Analysis: Complete Workflow with Seurat
A concise Seurat v5 workflow for droplet single-cell RNA-Seq — QC, normalization, integration, clustering, and marker discovery — with the flags that matter.
Single-cell RNA sequencing turns a bulk transcriptome into thousands of individual per-cell profiles — enabling cell-type discovery, trajectory inference, and comparative analyses at cellular resolution. Seurat is the most widely used R toolkit for droplet-based (10x Genomics, Chromium) scRNA-Seq data, and Seurat v5 is the current stable release as of 2025.
This is a short reference workflow for a typical experiment: Cell Ranger output → clustered UMAP → annotated markers.
Load and QC
library(Seurat)
library(tidyverse)
s <- Read10X("filtered_feature_bc_matrix/") |>
CreateSeuratObject(project = "PBMC", min.cells = 3, min.features = 200)
s[["percent.mt"]] <- PercentageFeatureSet(s, pattern = "^MT-")
VlnPlot(s, features = c("nFeature_RNA", "nCount_RNA", "percent.mt"), ncol = 3)
s <- subset(s, subset = nFeature_RNA > 500 &
nFeature_RNA < 6000 &
percent.mt < 15)
Inspect three metrics per cell:
nFeature_RNA— number of unique genes detected. Very low = empty droplet; very high = doublet.nCount_RNA— total UMI count.percent.mt— mitochondrial UMI fraction. High values (> 15-20 %) usually indicate dying cells.
Normalize, HVGs, PCA
s <- NormalizeData(s) |>
FindVariableFeatures(nfeatures = 2000) |>
ScaleData() |>
RunPCA(npcs = 50)
ElbowPlot(s, ndims = 50) # decide how many PCs to keep
Cluster and embed
s <- FindNeighbors(s, dims = 1:20) |>
FindClusters(resolution = 0.5) |>
RunUMAP(dims = 1:20)
DimPlot(s, label = TRUE)
Play with resolution (0.3-1.5) — lower gives fewer, coarser clusters; higher over-splits. There is no “right” resolution; pick the one that resolves the biology you care about.
Integration across samples
If you have multiple 10x runs and expect batch effects:
s.list <- SplitObject(s, split.by = "sample")
s.list <- lapply(s.list, function(x) SCTransform(x))
features <- SelectIntegrationFeatures(s.list, nfeatures = 3000)
s.list <- PrepSCTIntegration(s.list, anchor.features = features)
anchors <- FindIntegrationAnchors(s.list, normalization.method = "SCT",
anchor.features = features)
s.int <- IntegrateData(anchors, normalization.method = "SCT")
For faster, memory-lighter integration, swap in harmony::RunHarmony() on the PCA embedding — it produces excellent results and scales to millions of cells.
Markers and annotation
DefaultAssay(s.int) <- "SCT"
markers <- FindAllMarkers(s.int, only.pos = TRUE,
min.pct = 0.25, logfc.threshold = 0.25)
top10 <- markers |> group_by(cluster) |> slice_max(avg_log2FC, n = 10)
DoHeatmap(s.int, features = top10$gene)
Annotate clusters against a canonical marker table (Azimuth, CellTypist, or manual against literature). For automated annotation, SingleR with a matched reference (Human Primary Cell Atlas, MonacoImmuneData) works well as a starting point.
What to save
Persist the final Seurat object as an .rds and, for cross-language reuse, also as an .h5ad (AnnData):
SaveH5Seurat(s.int, "results/pbmc_integrated.h5Seurat")
Convert("results/pbmc_integrated.h5Seurat", dest = "h5ad")
Where to go next
- Trajectory inference with
slingshotormonocle3. - Cell-cell communication with
CellChat. - Differential state analysis across conditions with
muscatorMAST. - If you’re a Python user, port this pipeline to
scanpy— the concepts translate 1:1.
For the aligned quantification step that produces the filtered_feature_bc_matrix/, see our RNA-Seq pipeline guide (bulk) — Cell Ranger and STARsolo handle the single-cell equivalent using the same STAR core.
This walkthrough is deliberately compact. Every step above has 20 more dials worth exploring; use it as a scaffold and dive deeper where the biology needs it.
FAQ
Q. Should I use Seurat or scanpy for single-cell analysis?
A. Both are excellent and share the same underlying methods (PCA + kNN graph + Leiden/Louvain + UMAP). Seurat has a slight edge in visualization polish and integration methods (SCTransform, harmony) already wired in; scanpy has better ties to deep-learning tooling. Learn whichever your lab's downstream analysts already use.
Related in RNA-Seq
Kallisto vs Salmon: Pseudo-Alignment Tools Compared
Two flagship pseudo-alignment quantifiers for RNA-Seq compared on speed, accuracy, and the small but meaningful differences that matter downstream.
STAR vs HISAT2: RNA-Seq Aligner Comparison
A practical, benchmark-informed comparison of STAR and HISAT2 for short-read RNA-Seq alignment — accuracy, memory, speed, splice discovery, and when to prefer each.
How to Use DESeq2 for Differential Expression Analysis
A rigorous walkthrough of the DESeq2 workflow: count matrix prep, dispersion estimation, LFC shrinkage, batch correction, and pitfalls that get past most tutorials.