
Bio Workflows Multiome Pipeline
- 3 installs
- 1.1k repo stars
- Updated July 25, 2026
- gptomics/bioskills
Run an end-to-end multiome workflow for joint scRNA-seq and scATAC-seq analysis with WNN integration in Seurat/Signac.
About
Orchestrates loading, separate RNA and ATAC modality processing, and weighted-nearest-neighbor integration with Seurat and Signac. A developer uses it to analyze joint scRNA+scATAC data into a shared cell-type embedding.
- Separate RNA/ATAC QC then WNN joint embedding
- Signac co-accessibility and motif deviation steps
Bio Workflows Multiome Pipeline by the numbers
- 3 all-time installs (skills.sh)
- Ranked #1,661 of 2,064 Data Science & ML skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/gptomics/bioskills --skill bio-workflows-multiome-pipelineAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 3 |
|---|---|
| repo stars | ★ 1.1k |
| Last updated | July 25, 2026 |
| Repository | gptomics/bioskills ↗ |
What it does
Run an end-to-end multiome workflow for joint scRNA-seq and scATAC-seq analysis with WNN integration in Seurat/Signac.
Files
Version Compatibility
Reference examples tested with: ggplot2 3.5+
Before using code patterns, verify installed versions match. If versions differ:
- R:
packageVersion('<pkg>')then?function_nameto verify parameters
If code throws ImportError, AttributeError, or TypeError, introspect the installed package and adapt the example to match the actual API rather than retrying.
Multiome Pipeline
"Analyze my 10X Multiome data jointly" -> Orchestrate Cell Ranger ARC processing, Seurat/Signac scRNA+scATAC integration via WNN, chromatin accessibility peak calling, motif enrichment, and gene regulatory network inference.
Complete workflow for 10X Multiome (joint scRNA + scATAC) analysis using Seurat and Signac.
Workflow Overview
10X Multiome data
|
v
[1. Load Data] ---------> Read RNA + ATAC
|
v
[2. RNA Processing] ----> Standard scRNA workflow
|
v
[3. ATAC Processing] ---> Peak calling, LSI
|
v
[4. WNN Integration] ---> Weighted nearest neighbors
|
v
[5. Joint Analysis] ----> Clustering, markers
|
v
[6. Linked Features] ---> Gene-peak links
|
v
Integrated multiome objectStep 1: Load Multiome Data
library(Seurat)
library(Signac)
library(EnsDb.Hsapiens.v86)
library(ggplot2)
# Load RNA
rna_counts <- Read10X_h5('filtered_feature_bc_matrix.h5')
# For multiome, this returns a list with 'Gene Expression' and 'Peaks'
# Create Seurat object with RNA
seurat_obj <- CreateSeuratObject(
counts = rna_counts$`Gene Expression`,
assay = 'RNA'
)
# Load ATAC
atac_counts <- rna_counts$Peaks
# Or from fragments file
frags <- CreateFragmentObject('atac_fragments.tsv.gz', cells = colnames(seurat_obj))
# Create ChromatinAssay
atac_assay <- CreateChromatinAssay(
counts = atac_counts,
sep = c(':', '-'),
fragments = frags,
annotation = GetGRangesFromEnsDb(ensdb = EnsDb.Hsapiens.v86)
)
seurat_obj[['ATAC']] <- atac_assayStep 2: RNA Quality Control and Processing
# QC metrics
seurat_obj[['percent.mt']] <- PercentageFeatureSet(seurat_obj, pattern = '^MT-')
# Filter
seurat_obj <- subset(seurat_obj,
nCount_RNA > 1000 &
nCount_RNA < 25000 &
percent.mt < 20
)
# Normalize RNA
seurat_obj <- SCTransform(seurat_obj, assay = 'RNA', verbose = FALSE)
# PCA
seurat_obj <- RunPCA(seurat_obj, assay = 'SCT', verbose = FALSE)Step 3: ATAC Quality Control and Processing
# ATAC QC metrics
DefaultAssay(seurat_obj) <- 'ATAC'
seurat_obj <- NucleosomeSignal(seurat_obj)
seurat_obj <- TSSEnrichment(seurat_obj)
# Visualize
VlnPlot(seurat_obj, features = c('nCount_ATAC', 'TSS.enrichment', 'nucleosome_signal'),
pt.size = 0, ncol = 3)
# Filter ATAC
seurat_obj <- subset(seurat_obj,
nCount_ATAC > 1000 &
nCount_ATAC < 100000 &
TSS.enrichment > 2 &
nucleosome_signal < 4
)
# Normalize ATAC (TF-IDF + SVD = LSI)
seurat_obj <- RunTFIDF(seurat_obj)
seurat_obj <- FindTopFeatures(seurat_obj, min.cutoff = 'q0')
seurat_obj <- RunSVD(seurat_obj)
# Check LSI components (first often correlates with depth)
DepthCor(seurat_obj)Step 4: Weighted Nearest Neighbors (WNN)
# Build WNN graph using both modalities
seurat_obj <- FindMultiModalNeighbors(
seurat_obj,
reduction.list = list('pca', 'lsi'),
dims.list = list(1:30, 2:30), # Skip LSI component 1 if depth-correlated
modality.weight.name = 'RNA.weight'
)
# UMAP on WNN graph
seurat_obj <- RunUMAP(seurat_obj, nn.name = 'weighted.nn',
reduction.name = 'wnn.umap', reduction.key = 'wnnUMAP_')
# Cluster on WNN
seurat_obj <- FindClusters(seurat_obj, graph.name = 'wsnn',
algorithm = 3, resolution = 0.5, verbose = FALSE)Step 5: Visualization and Markers
# Compare modality-specific and joint embeddings
p1 <- DimPlot(seurat_obj, reduction = 'pca', label = TRUE) + ggtitle('RNA PCA')
p2 <- DimPlot(seurat_obj, reduction = 'lsi', label = TRUE) + ggtitle('ATAC LSI')
p3 <- DimPlot(seurat_obj, reduction = 'wnn.umap', label = TRUE) + ggtitle('WNN UMAP')
p1 + p2 + p3
# Modality weights per cell
VlnPlot(seurat_obj, features = 'RNA.weight', group.by = 'seurat_clusters', pt.size = 0)
# Find markers (RNA)
DefaultAssay(seurat_obj) <- 'SCT'
rna_markers <- FindAllMarkers(seurat_obj, only.pos = TRUE, min.pct = 0.25)
# Find markers (ATAC - differentially accessible peaks)
DefaultAssay(seurat_obj) <- 'ATAC'
atac_markers <- FindAllMarkers(seurat_obj, only.pos = TRUE, min.pct = 0.05,
test.use = 'LR', latent.vars = 'nCount_ATAC')Step 6: Gene-Peak Linkage
# Link peaks to genes
DefaultAssay(seurat_obj) <- 'ATAC'
seurat_obj <- RegionStats(seurat_obj, genome = BSgenome.Hsapiens.UCSC.hg38)
seurat_obj <- LinkPeaks(
seurat_obj,
peak.assay = 'ATAC',
expression.assay = 'SCT',
genes.use = c('CD8A', 'CD4', 'MS4A1', 'CD14') # Example genes
)
# Visualize links
CoveragePlot(seurat_obj, region = 'CD8A', features = 'CD8A',
expression.assay = 'SCT', extend.upstream = 10000, extend.downstream = 10000)Complete Workflow Script
library(Seurat)
library(Signac)
library(EnsDb.Hsapiens.v86)
library(BSgenome.Hsapiens.UCSC.hg38)
library(ggplot2)
# Configuration
data_dir <- 'multiome_output'
output_dir <- 'multiome_results'
dir.create(output_dir, showWarnings = FALSE)
# === Load Data ===
cat('Loading data...\n')
counts <- Read10X_h5(file.path(data_dir, 'filtered_feature_bc_matrix.h5'))
frags <- file.path(data_dir, 'atac_fragments.tsv.gz')
seurat_obj <- CreateSeuratObject(counts = counts$`Gene Expression`, assay = 'RNA')
seurat_obj[['ATAC']] <- CreateChromatinAssay(
counts = counts$Peaks,
sep = c(':', '-'),
fragments = frags,
annotation = GetGRangesFromEnsDb(ensdb = EnsDb.Hsapiens.v86)
)
cat('Cells:', ncol(seurat_obj), '\n')
# === RNA QC ===
cat('RNA QC...\n')
seurat_obj[['percent.mt']] <- PercentageFeatureSet(seurat_obj, pattern = '^MT-')
seurat_obj <- subset(seurat_obj, nCount_RNA > 1000 & nCount_RNA < 25000 & percent.mt < 20)
# === ATAC QC ===
cat('ATAC QC...\n')
DefaultAssay(seurat_obj) <- 'ATAC'
seurat_obj <- NucleosomeSignal(seurat_obj)
seurat_obj <- TSSEnrichment(seurat_obj)
seurat_obj <- subset(seurat_obj, nCount_ATAC > 1000 & TSS.enrichment > 2 & nucleosome_signal < 4)
cat('After QC:', ncol(seurat_obj), 'cells\n')
# === Process RNA ===
cat('Processing RNA...\n')
DefaultAssay(seurat_obj) <- 'RNA'
seurat_obj <- SCTransform(seurat_obj, verbose = FALSE)
seurat_obj <- RunPCA(seurat_obj, verbose = FALSE)
# === Process ATAC ===
cat('Processing ATAC...\n')
DefaultAssay(seurat_obj) <- 'ATAC'
seurat_obj <- RunTFIDF(seurat_obj)
seurat_obj <- FindTopFeatures(seurat_obj, min.cutoff = 'q0')
seurat_obj <- RunSVD(seurat_obj)
# === WNN Integration ===
cat('WNN integration...\n')
seurat_obj <- FindMultiModalNeighbors(seurat_obj,
reduction.list = list('pca', 'lsi'),
dims.list = list(1:30, 2:30),
modality.weight.name = 'RNA.weight'
)
seurat_obj <- RunUMAP(seurat_obj, nn.name = 'weighted.nn',
reduction.name = 'wnn.umap', reduction.key = 'wnnUMAP_')
seurat_obj <- FindClusters(seurat_obj, graph.name = 'wsnn', resolution = 0.5, verbose = FALSE)
# === Save ===
saveRDS(seurat_obj, file.path(output_dir, 'multiome_analyzed.rds'))
# === Plots ===
pdf(file.path(output_dir, 'wnn_umap.pdf'), width = 10, height = 8)
DimPlot(seurat_obj, reduction = 'wnn.umap', label = TRUE)
dev.off()
cat('Results saved to:', output_dir, '\n')
cat('Clusters:', length(unique(seurat_obj$seurat_clusters)), '\n')Related Skills
- single-cell/data-io - Loading 10X data
- single-cell/preprocessing - QC and normalization
- single-cell/multimodal-integration - WNN details
- single-cell/scatac-analysis - ATAC-specific processing
- atac-seq/single-cell-atac - Signac / ArchR / SnapATAC2 ecosystem decision; AMULET; cellranger-arc
- atac-seq/co-accessibility - Cicero / ArchR getCoAccessibility for cis-regulatory inference
- atac-seq/enhancer-gene-linking - ABC / ENCODE-rE2G for enhancer-gene mapping
- atac-seq/motif-deviation - chromVAR for per-cell TF motif activity
- atac-seq/footprinting - scprinter for sc footprinting
# Reference: ggplot2 3.5+ | Verify API if version differs
# Complete 10X Multiome workflow with Seurat and Signac
library(Seurat)
library(Signac)
library(EnsDb.Hsapiens.v86)
library(ggplot2)
library(patchwork)
# Configuration
data_dir <- 'cellranger_multiome_output'
output_dir <- 'multiome_results'
dir.create(output_dir, showWarnings = FALSE)
dir.create(file.path(output_dir, 'plots'), showWarnings = FALSE)
# === Step 1: Load Data ===
cat('=== Step 1: Loading Data ===\n')
# Read multiome data
counts <- Read10X_h5(file.path(data_dir, 'filtered_feature_bc_matrix.h5'))
frags_path <- file.path(data_dir, 'atac_fragments.tsv.gz')
# Create Seurat object with RNA
seurat_obj <- CreateSeuratObject(
counts = counts$`Gene Expression`,
assay = 'RNA',
min.cells = 3,
min.features = 200
)
# Get annotations
annotations <- GetGRangesFromEnsDb(ensdb = EnsDb.Hsapiens.v86)
seqlevelsStyle(annotations) <- 'UCSC'
# Add ATAC assay
seurat_obj[['ATAC']] <- CreateChromatinAssay(
counts = counts$Peaks,
sep = c(':', '-'),
fragments = frags_path,
annotation = annotations
)
cat('Initial cells:', ncol(seurat_obj), '\n')
cat('RNA features:', nrow(seurat_obj[['RNA']]), '\n')
cat('ATAC peaks:', nrow(seurat_obj[['ATAC']]), '\n')
# === Step 2: RNA QC ===
cat('\n=== Step 2: RNA QC ===\n')
seurat_obj[['percent.mt']] <- PercentageFeatureSet(seurat_obj, pattern = '^MT-')
seurat_obj[['percent.ribo']] <- PercentageFeatureSet(seurat_obj, pattern = '^RP[SL]')
# QC plots
pdf(file.path(output_dir, 'plots', 'rna_qc.pdf'), width = 12, height = 4)
VlnPlot(seurat_obj, features = c('nCount_RNA', 'nFeature_RNA', 'percent.mt'), pt.size = 0, ncol = 3)
dev.off()
# Filter RNA
seurat_obj <- subset(seurat_obj,
nCount_RNA > 1000 &
nCount_RNA < 25000 &
nFeature_RNA > 500 &
percent.mt < 20
)
cat('After RNA QC:', ncol(seurat_obj), 'cells\n')
# === Step 3: ATAC QC ===
cat('\n=== Step 3: ATAC QC ===\n')
DefaultAssay(seurat_obj) <- 'ATAC'
seurat_obj <- NucleosomeSignal(seurat_obj)
seurat_obj <- TSSEnrichment(seurat_obj)
# QC plots
pdf(file.path(output_dir, 'plots', 'atac_qc.pdf'), width = 12, height = 4)
VlnPlot(seurat_obj, features = c('nCount_ATAC', 'TSS.enrichment', 'nucleosome_signal'), pt.size = 0, ncol = 3)
dev.off()
# Filter ATAC
seurat_obj <- subset(seurat_obj,
nCount_ATAC > 1000 &
nCount_ATAC < 100000 &
TSS.enrichment > 2 &
nucleosome_signal < 4
)
cat('After ATAC QC:', ncol(seurat_obj), 'cells\n')
# === Step 4: RNA Processing ===
cat('\n=== Step 4: RNA Processing ===\n')
DefaultAssay(seurat_obj) <- 'RNA'
seurat_obj <- SCTransform(seurat_obj, vars.to.regress = 'percent.mt', verbose = FALSE)
seurat_obj <- RunPCA(seurat_obj, npcs = 50, verbose = FALSE)
# === Step 5: ATAC Processing ===
cat('\n=== Step 5: ATAC Processing ===\n')
DefaultAssay(seurat_obj) <- 'ATAC'
seurat_obj <- RunTFIDF(seurat_obj)
seurat_obj <- FindTopFeatures(seurat_obj, min.cutoff = 'q0')
seurat_obj <- RunSVD(seurat_obj, n = 50)
# Check depth correlation
pdf(file.path(output_dir, 'plots', 'lsi_depth_cor.pdf'), width = 8, height = 4)
DepthCor(seurat_obj)
dev.off()
# === Step 6: WNN Integration ===
cat('\n=== Step 6: WNN Integration ===\n')
seurat_obj <- FindMultiModalNeighbors(
seurat_obj,
reduction.list = list('pca', 'lsi'),
dims.list = list(1:30, 2:30),
modality.weight.name = 'RNA.weight'
)
seurat_obj <- RunUMAP(seurat_obj, nn.name = 'weighted.nn',
reduction.name = 'wnn.umap', reduction.key = 'wnnUMAP_')
seurat_obj <- FindClusters(seurat_obj, graph.name = 'wsnn',
algorithm = 3, resolution = 0.5, verbose = FALSE)
cat('Clusters:', length(unique(seurat_obj$seurat_clusters)), '\n')
# === Step 7: Visualization ===
cat('\n=== Step 7: Visualization ===\n')
# Compare embeddings
p1 <- DimPlot(seurat_obj, reduction = 'pca', label = TRUE) + ggtitle('RNA (PCA)')
p2 <- DimPlot(seurat_obj, reduction = 'lsi', label = TRUE) + ggtitle('ATAC (LSI)')
p3 <- DimPlot(seurat_obj, reduction = 'wnn.umap', label = TRUE) + ggtitle('WNN (Joint)')
pdf(file.path(output_dir, 'plots', 'embeddings_comparison.pdf'), width = 15, height = 5)
print(p1 + p2 + p3)
dev.off()
# Modality weights
pdf(file.path(output_dir, 'plots', 'modality_weights.pdf'), width = 8, height = 4)
VlnPlot(seurat_obj, features = 'RNA.weight', group.by = 'seurat_clusters', pt.size = 0)
dev.off()
# === Step 8: Markers ===
cat('\n=== Step 8: Finding Markers ===\n')
# RNA markers
DefaultAssay(seurat_obj) <- 'SCT'
rna_markers <- FindAllMarkers(seurat_obj, only.pos = TRUE, min.pct = 0.25,
logfc.threshold = 0.25, verbose = FALSE)
write.csv(rna_markers, file.path(output_dir, 'rna_markers.csv'), row.names = FALSE)
# ATAC markers
DefaultAssay(seurat_obj) <- 'ATAC'
atac_markers <- FindAllMarkers(seurat_obj, only.pos = TRUE, min.pct = 0.05,
test.use = 'LR', latent.vars = 'nCount_ATAC', verbose = FALSE)
write.csv(atac_markers, file.path(output_dir, 'atac_markers.csv'), row.names = FALSE)
# === Step 9: Save ===
cat('\n=== Step 9: Saving Results ===\n')
saveRDS(seurat_obj, file.path(output_dir, 'multiome_analyzed.rds'))
cat('\n=== Analysis Complete ===\n')
cat('Results saved to:', output_dir, '\n')
cat(' - Seurat object: multiome_analyzed.rds\n')
cat(' - RNA markers: rna_markers.csv\n')
cat(' - ATAC markers: atac_markers.csv\n')
cat(' - Plots: plots/\n')
Multiome Pipeline - Usage Guide
Overview
This workflow analyzes 10X Multiome data (joint scRNA-seq + scATAC-seq from the same cells) using Seurat and Signac for weighted nearest neighbor integration.
Prerequisites
install.packages(c('Seurat', 'ggplot2'))
BiocManager::install(c('Signac', 'EnsDb.Hsapiens.v86', 'BSgenome.Hsapiens.UCSC.hg38'))Quick Start
Tell your AI agent what you want to do:
- "Analyze my 10X Multiome data"
- "Integrate scRNA and scATAC from the same cells"
- "Run WNN clustering on my multiome experiment"
Example Prompts
Processing
"Load my multiome Cell Ranger output"
"Filter cells by RNA and ATAC QC metrics"
Analysis
"Run weighted nearest neighbors integration"
"Find gene-peak links"
"Compare RNA vs ATAC clustering"
Input Requirements
| Input | Format | Description |
|---|---|---|
| Cell Ranger output | Directory | Multiome processed data |
| Fragments file | TSV.gz | ATAC fragment positions |
What the Workflow Does
1. Load Data - Read RNA and ATAC from same cells 2. RNA QC - Standard scRNA-seq filtering 3. ATAC QC - TSS enrichment, nucleosome signal 4. Process RNA - SCTransform, PCA 5. Process ATAC - TF-IDF, LSI 6. WNN - Joint embedding 7. Linkage - Gene-peak correlations
Tips
- LSI component 1: Often depth-correlated, skip it (always use
dims=2:30) - WNN weights: Check modality contribution per cluster; ATAC sparseness can dominate noise
- Gene-peak links: Signac LinkPeaks for direct correlation; for full ABC enhancer-gene see atac-seq/enhancer-gene-linking
- Cell types: Annotate using RNA markers primarily; gene activity scores from ATAC are approximate
- cellranger-arc vs cellranger-atac: Multiome (paired) requires cellranger-arc; barcode universes differ
- Per-cell TF activity: chromVAR via
Signac::RunChromVARafter AddMotifs; see atac-seq/motif-deviation - Cis-regulatory inference: Cicero on the ATAC assay or ArchR getCoAccessibility; see atac-seq/co-accessibility
Related Skills
- single-cell/data-io - 10X data loading
- single-cell/preprocessing - QC and normalization
- single-cell/multimodal-integration - WNN details
- single-cell/scatac-analysis - ATAC-specific processing
- atac-seq/single-cell-atac - Signac / ArchR / SnapATAC2 ecosystem decision
- atac-seq/co-accessibility - Cicero / ArchR getCoAccessibility for cis-regulatory inference
- atac-seq/enhancer-gene-linking - ABC, ENCODE-rE2G for enhancer-gene mapping
- atac-seq/motif-deviation - chromVAR for per-cell TF motif activity
- atac-seq/footprinting - scprinter for single-cell footprinting