Note
Why include this lecture? RNA-seq tells you what genes are expressed; ATAC-seq tells you which regulatory regions are accessible. Together they explain what a cell is doing and what it is poised to do. The analysis vocabulary (counts, peaks, dim reduction, clusters) is almost identical to scRNA-seq — the differences are mostly bioinformatic plumbing.
ATAC-seq overview — chromatin accessibility from Tn5 to peaks
| Assay | What it measures | Throughput | Notes |
|---|---|---|---|
| DNase-seq | DNase I cut sites | High input, classic | Requires lots of cells |
| FAIRE-seq | Free, non-nucleosomal DNA | Phenol-extraction | Low resolution |
| ATAC-seq | Tn5-accessible DNA | 50k cells (bulk), 1 cell (sc) | Default modern choice |
| Cut&Run / Cut&Tag | Antibody-targeted accessibility | Low input | Marks TF-bound or modified regions |
ATAC tools comparison — bulk vs single-cell stacks
bwa-mem or bowtie2 (--very-sensitive)+4/-5 bp on the two strands (Tn5 binds as a dimer)MACS2 callpeak --nomodel --shift -75 --extsize 150 -B --SPMR (or -f BAMPE for paired-end)ChIPseeker or HOMER against a TSS databaseTip
Sanity-check the fragment-size distribution. A good ATAC library shows clear nucleosomal periodicity: a sub-100bp peak (NFR — nucleosome-free region), a ~200bp peak (mono-nucleosome), and ~400bp (di-nucleosome). Missing periodicity = library quality problem.
| Metric | Healthy range | What goes wrong |
|---|---|---|
| % reads on chrM | < 30% (often < 10%) | High = poor mitochondrial depletion |
| TSS enrichment score | > 7 (ENCODE), > 10 ideal | Low = scattered, non-specific Tn5 |
| Fraction of reads in peaks (FRiP) | > 0.2 | Low = noisy library |
| Library complexity (NRF, PBC1) | NRF > 0.9 | Low = PCR-duplicated library |
Warning
Tn5 has sequence preferences. Don’t try to call SNVs or look at base-level features near peak summits — Tn5 cleaves preferentially at GC-rich sites and biases the apparent footprint.
| scRNA-seq | scATAC-seq | |
|---|---|---|
| Feature | gene | peak (or bin / motif / TF) |
| Counts | UMI per gene per cell | Tn5 cut events per peak per cell |
| Sparsity | ~90% zeros | ~98–99% zeros (most peaks not seen in most cells) |
| Dim reduction | PCA on log-normalized counts | LSI (TF-IDF + SVD) on binarized counts |
| Standard tools | Seurat / scanpy | Signac / ArchR / SnapATAC2 |
Note
Why LSI not PCA? scATAC counts are nearly binary (a peak is either captured or not in a cell). Variance-stabilizing normalization (TF-IDF) followed by SVD handles this better than naive PCA on log-counts.
The primary input for Signac is a fragments.tsv.gz — every line is one Tn5 cut event:
chr1 10125 10570 AAACGAAAGAGGAGCC-1 2
chrom start end cell_barcode duplicate_count
A tabix index (.tbi) is built so Signac can pull fragments for any genomic region in O(log n).
Tip
Download the fragment file even if you only have the peak matrix. Many downstream operations (gene activity, motif scoring, coverage plots) re-count fragments per region from scratch.
Note
The tutorial (Module 15) uses the 10x PBMC 10k scATAC v1 dataset, which is mapped to hg19 / GRCh37. This requires:
EnsDb.Hsapiens.v75 (last Ensembl annotation pinned to hg19)chr prefix — add seqlevels(annotations) <- paste0("chr", seqlevels(annotations))atac_v1_pbmc_10k_* (not the Next GEM atac_v1_pbmc_10k_nextgem_* which is hg38)Mixing build and annotation causes bizarre coordinate errors — always confirm the build before loading the EnsDb.
| Metric | Healthy range | What it catches |
|---|---|---|
nCount_peaks (fragments in peaks per cell) |
1,000–50,000 | Low = empty droplet; very high = doublet |
pct_reads_in_peaks (FRiP per cell) |
> 15% | Low = noisy / dying cell |
blacklist_ratio |
< 0.05 | High = repetitive / artefactual |
nucleosome_signal |
< 4 | High = degraded library, poor periodicity |
TSS.enrichment |
> 2 (per-cell) | Low = no real chromatin signal |
library(Signac); library(Seurat); library(EnsDb.Hsapiens.v75)
counts <- Read10X_h5("data/atac_v1_pbmc_10k_filtered_peak_bc_matrix.h5")
chrom_assay <- CreateChromatinAssay(
counts = counts,
sep = c(":", "-"),
fragments = "data/atac_v1_pbmc_10k_fragments.tsv.gz",
min.cells = 10, min.features = 200)
pbmc <- CreateSeuratObject(counts = chrom_assay, assay = "ATAC",
meta.data = read.csv("data/atac_v1_pbmc_10k_singlecell.csv",
row.names = 1))
# ATAC-specific normalization + reduction
pbmc <- RunTFIDF(pbmc)
pbmc <- FindTopFeatures(pbmc, min.cutoff = "q5")
pbmc <- RunSVD(pbmc)
# Skip LSI component 1 — usually correlates with depth, not biology
pbmc <- RunUMAP(pbmc, reduction = "lsi", dims = 2:30)
pbmc <- FindNeighbors(pbmc, reduction = "lsi", dims = 2:30) |>
FindClusters(algorithm = 3)Warning
Always drop LSI component 1. It almost always reflects sequencing depth per cell, not biology. dims = 2:30 is the canonical Signac choice.
To label scATAC clusters using familiar gene markers, build a gene activity matrix — sum fragments overlapping each gene body + 2 kb upstream:
gene.activities <- GeneActivity(pbmc)
pbmc[["RNA"]] <- CreateAssayObject(counts = gene.activities)
pbmc <- NormalizeData(pbmc, assay = "RNA",
normalization.method = "LogNormalize",
scale.factor = median(pbmc$nCount_RNA))
DefaultAssay(pbmc) <- "RNA"
FeaturePlot(pbmc, c("MS4A1","CD3D","LEF1","NKG7","TREM1","LYZ"),
max.cutoff = "q95", ncol = 3)Note
Gene activity is a proxy. Accessibility around a gene loosely correlates with expression — but a gene can be open without being transcribed (poised), or transcribed without obvious accessibility (long-range enhancer). Use it for clustering / labelling, not for quantifying expression.
If you also have a matched scRNA dataset (10x Multiome, or a published PBMC reference such as Signac’s pbmc_10k_v3.rds), use anchor-based label transfer:
FindMarkers(test.use = "LR", latent.vars = "nCount_peaks")Signac::FindMotifs against JASPAR2020, or HOMER (findMotifsGenome.pl) from the command linechromVAR deviationsCoveragePlot() to visualize fragment pileups by cell typeTip
Latent variable matters. Use latent.vars = "nCount_peaks" (or "peak_region_fragments") for differential accessibility — depth varies wildly across cells and otherwise drives spurious “DA peaks”.
peak_region_fragments > 3000, TSS.enrichment > 2, etc.) are starting points — always inspect the violin plots and density scatters first before cutting.min.cutoff = "q0" vs "q5" in FindTopFeatures — "q0" keeps all peaks; "q5" drops the lowest-frequency 5%. Use "q0" if you have few cells or rare cell types.Single Cell RNA-seq Workshop · Lecture 15