omics.co.in

Chapter 6: Graph-Based Clustering (Louvain & Leiden) and Marker Annotation

Chapter 6: Graph-Based Clustering (Louvain & Leiden) and Marker Annotation

Once single-cell data is projected into reduced dimensional space, unsupervised clustering groups cells with similar transcriptomic profiles into discrete cell types and states. Graph-based clustering is the gold standard for single-cell data, outperforming older k-means or hierarchical clustering algorithms.

The Graph-Based Clustering Pipeline
  1. k-Nearest Neighbor (k-NN) Graph: Connect each cell to its \(k\) closest neighbors in Principal Component space.
  2. Shared Nearest Neighbor (SNN) Graph: Weight edges between cells by the Jaccard similarity of their shared neighbor sets.
  3. Community Detection: Partition the graph into densely connected subgraphs (communities) using modularity optimization algorithms.

1. Community Detection: Louvain vs. Leiden Algorithms

While the Louvain algorithm has been widely used, Traag et al. demonstrated that Louvain has a mathematical flaw: it can produce disconnected or poorly connected sub-communities. The modern Leiden algorithm resolves this by introducing a refinement step that guarantees connected communities and converges faster.

# Graph construction and Leiden clustering in Seurat v5
# Step 1: Compute SNN graph on top 30 PCs
pbmc <- FindNeighbors(pbmc, dims = 1:30, k.param = 25)

# Step 2: Cluster using Leiden algorithm (algorithm = 4)
# resolution parameter: higher values (>1.0) produce more granular clusters; lower (<0.5) produce broad lineages
pbmc <- FindClusters(pbmc, resolution = 0.8, algorithm = 4)

# Visualize clusters on UMAP
DimPlot(pbmc, reduction = "umap", label = TRUE, pt.size = 0.5)

2. Canonical Marker Genes for PBMC Cell Types

# Canonical Human Immune Cell Marker Catalog:
# -------------------------------------------------------------------------------------
# Cell Type                  Primary Positive Markers           Negative / Excluded
# -------------------------------------------------------------------------------------
# CD4+ T Cells               CD3D, CD3E, CD4, IL7R              CD8A, MS4A1
# CD8+ Cytotoxic T Cells     CD3D, CD8A, CD8B, GZMB, PRF1       CD4
# B Cells                    MS4A1 (CD20), CD19, CD79A          CD3D
# Natural Killer (NK) Cells  GNLY, NKG7, NCAM1 (CD56), PRF1     CD3D
# CD14+ Monocytes            CD14, LYZ, S100A9, VCAN            FCGR3A (low)
# FCGR3A+ Monocytes          FCGR3A (CD16), MS4A7               CD14 (low)
# Dendritic Cells (mDC/pDC)  HLA-DRA, CST3, CLEC4C, LILRA4      CD3D, CD19
# Platelets / Megakaryocytes PPBP, PF4                          PTPRC (CD45)
# -------------------------------------------------------------------------------------

3. Automated Cell Type Annotation (SingleR & Azimuth)

Manual marker annotation can be subjective. Automated annotation compares single-cell profiles against curated reference atlases (e.g. Human Primary Cell Atlas or Blueprint/ENCODE).

library(SingleR)
library(celldex)

# Load reference expression atlas
hpca_ref <- celldex::HumanPrimaryCellAtlasData()

# Run SingleR annotation
predictions <- SingleR(
    test = as.SingleCellExperiment(pbmc), 
    ref = hpca_ref, 
    labels = hpca_ref$label.main
)

# Assign annotations back to Seurat object
pbmc$SingleR_label <- predictions$labels
DimPlot(pbmc, group.by = "SingleR_label", label = TRUE)
# CellTypist automated annotation in Python (Scanpy)
import celltypist
from celltypist import models

# Download Immune_All_Low model
models.download_models(force_update=False, model=['Immune_All_Low.pkl'])
model = models.Model.load(model='Immune_All_Low.pkl')

# Predict cell types
predictions = celltypist.annotate(adata, model='Immune_All_Low.pkl', majority_voting=True)
adata = predictions.to_adata()
sc.pl.umap(adata, color='majority_voting')
← Return to Home Hub Scroll to Top ↑