Skip to content
RNA-Seq

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.

PR Dr. Lin Wei 7 min read scRNA-Seq Seurat Single-Cell

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 slingshot or monocle3.
  • Cell-cell communication with CellChat.
  • Differential state analysis across conditions with muscat or MAST.
  • 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