| name | bio-single-cell-multimodal-integration |
| description | Analyze multi-modal single-cell data (CITE-seq, Multiome, spatial). Use when working with data that measures multiple modalities per cell like RNA + protein or RNA + ATAC. Use when analyzing CITE-seq, Multiome, or other multi-modal single-cell data. |
| tool_type | mixed |
| primary_tool | Seurat |
Multimodal Integration
Analyze multi-modal single-cell data where multiple measurements are made per cell.
Common Modalities
| Technology | Modalities | Package |
|---|
| CITE-seq | RNA + surface proteins (ADT) | Seurat |
| 10X Multiome | RNA + ATAC | Seurat, Signac, ArchR |
| SHARE-seq | RNA + ATAC | Seurat, Signac |
| Spatial (Visium) | RNA + spatial coordinates | Seurat, Squidpy |
CITE-seq Analysis (Seurat)
Load Data
library(Seurat)
data <- Read10X('filtered_feature_bc_matrix/')
rna_counts <- data$`Gene Expression`
adt_counts <- data$`Antibody Capture`
obj <- CreateSeuratObject(counts = rna_counts, assay = 'RNA')
obj[['ADT']] <- CreateAssayObject(counts = adt_counts)
QC and Normalization
obj <- PercentageFeatureSet(obj, pattern = '^MT-', col.name = 'percent.mt')
obj <- subset(obj, nFeature_RNA > 200 & percent.mt < 20)
obj <- NormalizeData(obj, assay = 'RNA')
obj <- FindVariableFeatures(obj, assay = 'RNA')
obj <- ScaleData(obj, assay = 'RNA')
obj <- NormalizeData(obj, assay = 'ADT', normalization.method = 'CLR', margin = 2)
obj <- ScaleData(obj, assay =
Weighted Nearest Neighbor (WNN) Clustering
obj <- RunPCA(obj, assay = 'RNA', reduction.name = 'pca')
obj <- RunPCA(obj, assay = 'ADT', reduction.name = 'apca',
features = rownames(obj[['ADT']]))
obj <- FindMultiModalNeighbors(obj,
reduction.list = list('pca', 'apca'),
dims.list = list(1:30, 1:18))
obj <- FindClusters(obj, graph.name = resolution
obj RunUMAPobj nn.name reduction.name
Visualize
DimPlot(obj, reduction = 'wnn.umap', label = TRUE)
FeaturePlot(obj, features = c('adt_CD3', 'adt_CD19', 'adt_CD14'),
reduction = 'wnn.umap')
VlnPlot(obj, features = 'RNA.weight', group.by = 'seurat_clusters')
10X Multiome (RNA + ATAC)
Load Data
library(Seurat)
library(Signac)
rna_counts <- Read10X_h5('filtered_feature_bc_matrix.h5')$`Gene Expression`
atac_counts <- Read10X_h5('filtered_feature_bc_matrix.h5')$Peaks
fragments <- CreateFragmentObject('atac_fragments.tsv.gz')
obj <- CreateSeuratObject(counts = rna_counts, assay = 'RNA')
obj[['ATAC']] <- CreateChromatinAssay(counts = atac_counts, fragments = fragments,
genome = 'hg38', min.cells = 5)
Process ATAC
obj <- NucleosomeSignal(obj)
obj <- TSSEnrichment(obj)
obj <- RunTFIDF(obj, assay = 'ATAC')
obj <- FindTopFeatures(obj, assay = 'ATAC', min.cutoff = 'q0')
obj <- RunSVD(obj, assay = 'ATAC')
Joint Analysis
DefaultAssay(obj) <- 'RNA'
obj <- NormalizeData(obj) %>% FindVariableFeatures() %>% ScaleData() %>% RunPCA()
obj <- FindMultiModalNeighbors(obj, reduction.list = list('pca', 'lsi'),
dims.list = list(1:30, 2:30))
obj <- RunUMAP(obj, nn.name = 'weighted.nn', reduction.name = 'wnn.umap')
obj <- FindClusters(obj, graph.name = 'wsnn')
Scanpy/MuData (Python)
CITE-seq with MuData
import scanpy as sc
import muon as mu
from muon import prot as pt
mdata = mu.read_10x_h5('filtered_feature_bc_matrix.h5')
rna = mdata.mod['rna']
prot = mdata.mod['prot']
sc.pp.filter_cells(rna, min_genes=200)
sc.pp.normalize_total(rna, target_sum=1e4)
sc.pp.log1p(rna)
sc.pp.highly_variable_genes(rna)
sc.tl.pca(rna)
pt.pp.clr(prot)
mu.tl.mofa(mdata, n_factors=20)
mu.tl.umap(mdata)
mu.pl.umap(mdata, color=['rna:leiden', 'prot:CD3'])
Integration Metrics
Modality Weights
weights <- obj@reductions$wnn@misc$weights
aggregate(weights, by = list(obj$seurat_clusters), mean)
Correlation Between Modalities
import numpy as np
common = set(rna.var_names) & set(prot.var_names)
for gene in common:
rna_expr = rna[:, gene].X.toarray().flatten()
prot_expr = prot[:, gene].X.toarray().flatten()
corr = np.corrcoef(rna_expr, prot_expr)[0, 1]
print(f'{gene}: r={corr:.3f}')
Marker Discovery
Multi-Modal Markers
DefaultAssay(obj) <- 'RNA'
rna_markers <- FindAllMarkers(obj, only.pos = TRUE)
DefaultAssay(obj) <- 'ADT'
adt_markers <- FindAllMarkers(obj, only.pos = TRUE)
all_markers <- rbind(
transform(rna_markers, modality = 'RNA'),
transform(adt_markers, modality = 'ADT')
)
Related Skills
- single-cell/data-io - Loading single-cell data
- single-cell/clustering - Clustering methods
- single-cell/markers-annotation - Cell type annotation
- chip-seq/peak-calling - For ATAC peak calling