BioF3 组学数据分析

10 scATAC-seq 分析

导出日期:2026年5月11日

10 scATAC-seq 分析

scATAC-seq 测的是单细胞层面的染色质可及性:哪些基因组区域在这个细胞里是"打开的",通常对应启动子、增强子等顺式调控元件。和 scRNA-seq 的一个直接区别是稀疏得多——每个细胞只有几千到几万个 fragment,落在 peak 上的更少——所以工具链另成一套。

本节用 10x Genomics 的 PBMC scATAC 10k 数据演示两套主流方案:ArchR(全流程、适合大数据)和 Signac(和 Seurat 无缝衔接,适合已经熟 Seurat 的用户)。

分析流水线的关键差异

步骤 scRNA-seq scATAC-seq
原始数据 counts matrix fragment file(.tsv.gz
特征 基因 peak 或 tile
主降维 PCA TF-IDF + SVD(称 LSI)
聚类 基于 PCA 的 KNN 图 基于 LSI 的 KNN 图
标志特征 差异基因 差异 peak / motif

大多数思路是通的:归一化 → 降维 → 找邻居 → 聚类 → 注释。差别在"归一化/降维怎么做"(TF-IDF + SVD)和"特征是什么"(peak 而非基因)。

用 ArchR 做全流程

ArchR 用 Arrow 文件作为底层存储,几十万细胞也能在普通服务器上跑动。

# 初次安装
if (!requireNamespace("devtools", quietly = TRUE)) install.packages("devtools")
devtools::install_github(
  "GreenleafLab/ArchR",
  ref   = "master",
  repos = BiocManager::repositories()
)

library(ArchR)
addArchRThreads(threads = 8)
addArchRGenome("hg38")   # PBMC 10k 是人类数据

# 从 fragment 文件创建 Arrow(按 sample 分开)
ArrowFiles <- createArrowFiles(
  inputFiles  = "~/biof3-data/pbmc10k-scatac/atac_fragments.tsv.gz",
  sampleNames = "pbmc10k",
  minTSS      = 4,       # TSS enrichment 最低值(典型 > 6 就是好)
  minFrags    = 1000,    # 细胞最低 fragment 数
  addTileMat  = TRUE,
  addGeneScoreMat = TRUE
)

proj <- ArchRProject(ArrowFiles, outputDirectory = "pbmc10k_ArchR")

# 去除 doublet
proj <- addDoubletScores(proj)
proj <- filterDoublets(proj)

minTSSminFrags 是 scATAC 的两条关键 QC 门槛:TSS enrichment 反映信号是否集中在转录起始位点附近,fragment 数衡量测序深度。

降维和聚类走 LSI:

proj <- addIterativeLSI(proj, useMatrix = "TileMatrix", name = "IterativeLSI")
proj <- addUMAP(proj,     reducedDims = "IterativeLSI")
proj <- addClusters(proj, reducedDims = "IterativeLSI")

plotEmbedding(proj, colorBy = "cellColData", name = "Clusters")

IterativeLSI 是 ArchR 特色:它会先用 top 可变 tile 做一次 LSI,聚类后再选 top 可变 tile 做第二次——对于稀疏数据这样更稳。

peak 层面的分析

仅靠 tile 做完聚类,真正做差异分析需要先 call peak:

# 为每个 cluster 生成 pseudobulk bigWig,再调用 MACS2
proj <- addGroupCoverages(proj, groupBy = "Clusters")
proj <- addReproduciblePeakSet(proj, groupBy = "Clusters")
proj <- addPeakMatrix(proj)

# 按 cluster 找特征 peak
markerPeaks <- getMarkerFeatures(
  ArchRProj = proj,
  useMatrix = "PeakMatrix",
  groupBy   = "Clusters"
)

plotMarkerHeatmap(markerPeaks, cutOff = "FDR <= 0.01 & Log2FC >= 1")

peak 本身只是"开放区域",要看它背后哪些转录因子在起作用,就做 motif 富集:

proj <- addMotifAnnotations(proj, motifSet = "cisbp", name = "Motif")

enrichMotifs <- peakAnnoEnrichment(
  seMarker          = markerPeaks,
  ArchRProj         = proj,
  peakAnnotation    = "Motif",
  cutOff            = "FDR <= 0.1 & Log2FC >= 0.5"
)

plotEnrichHeatmap(enrichMotifs, n = 7, transpose = TRUE)

每个 cluster 富集出来的 motif 列表是判断"这个 cluster 是什么细胞类型"的重要证据,尤其和 RNA 层面的 marker 基因交叉验证时。

把 ATAC 和 RNA 对齐

如果同时有 scRNA-seq 的参考数据(或来自同一批样本的 Multiome GEX),可以把 RNA 的 cell type 标签"传"到 ATAC 上:

library(Seurat)
rna <- readRDS("pbmc_rna_annotated.rds")

proj <- addGeneIntegrationMatrix(
  ArchRProj       = proj,
  useMatrix       = "GeneScoreMatrix",
  matrixName      = "GeneIntegrationMatrix",
  reducedDims     = "IterativeLSI",
  seRNA           = rna,
  addToArrow      = TRUE,
  groupRNA        = "cell_type",
  nameCell        = "predictedCell",
  nameGroup       = "predictedGroup",
  nameScore       = "predictedScore"
)

plotEmbedding(proj, colorBy = "cellColData", name = "predictedGroup")

GeneScoreMatrix 是 ArchR 在 peak 基础上估计出的"基因活跃度"代理,跟 RNA 匹配效果通常不错。如果数据是 10x Multiome,RNA 和 ATAC 来自同一细胞,就不用做整合,直接把 barcode 对齐即可。

用 Signac 的版本

Signac 把 ATAC 建模成 Seurat 的 Assay,如果流程已经建在 Seurat 上,衔接会很自然:

library(Signac)
library(Seurat)

counts   <- Read10X_h5("~/biof3-data/pbmc10k-scatac/atac_filtered_peak_bc_matrix.h5")
metadata <- read.csv("~/biof3-data/pbmc10k-scatac/atac_singlecell.csv", row.names = 1)

chrom_assay <- CreateChromatinAssay(
  counts    = counts,
  sep       = c(":", "-"),
  genome    = "hg38",
  fragments = "~/biof3-data/pbmc10k-scatac/atac_fragments.tsv.gz",
  min.cells = 10,
  min.features = 200
)

pbmc <- CreateSeuratObject(chrom_assay, assay = "peaks", meta.data = metadata)

pbmc <- NucleosomeSignal(pbmc)
pbmc <- TSSEnrichment(pbmc, fast = FALSE)

pbmc <- RunTFIDF(pbmc)
pbmc <- FindTopFeatures(pbmc, min.cutoff = "q0")
pbmc <- RunSVD(pbmc)
pbmc <- RunUMAP(pbmc, reduction = "lsi", dims = 2:30)
pbmc <- FindNeighbors(pbmc, reduction = "lsi", dims = 2:30)
pbmc <- FindClusters(pbmc, algorithm = 3, verbose = FALSE)

DimPlot(pbmc, label = TRUE) + NoLegend()

dims = 2:30 是 Signac 教程里的一个 convention:LSI 的第一个维度通常和测序深度高度相关,跳过它能避免聚类被测序深度主导。

工具选择建议

参考资源