1. 问题描述
在利用 ArchR 处理小麦(Wheat)单细胞 ATAC-seq 数据时,于"降维-聚类-可视化"环节观察到两个异常现象:
- 细胞数量的"迷之减少" :在分析流程的不同阶段,细胞总数经历了从 105,317 →\rightarrow→ 101,385 →\rightarrow→ 100,423 的梯度下降。
- Embedding 提取报错:在尝试提取 UMAP 坐标进行绘图时,系统提示找不到对应的 Embedding 对象,导致可视化中断。
2. 源码逻辑与代码对比
源码逻辑(ArchR 标准流程)
ArchR 的设计初衷是先构建一个稳定的低维空间(Reduced Dimensions) ,再基于此空间生成流形投影(Embedding),最后进行聚类分析。
addIterativeLSI():构建降维特征矩阵。addUMAP():基于 LSI 结果生成二维投影。addClusters():基于 LSI 结果进行聚类。
我的错误代码段(循环嵌套逻辑)
R
# 错误示范:在循环中多次尝试生成 UMAP 并动态命名
for(res in c(0.4, 0.6, 0.8)){
proj <- addClusters(input = proj, reducedDims = "IterativeLSI", resolution = res, name = paste0("Cluster_", res))
# 报错诱因:在这里重复运行 addUMAP 并赋予了非标准名称
proj <- addUMAP(ArchRProj = proj, reducedDims = "IterativeLSI", name = paste0("UMAP_", res), force = TRUE)
}
plotEmbedding(ArchRProj = proj, embedding = "UMAP", colorBy = "cellColData", name = "Cluster_0.6")
报错信息
Error: Embedding 'UMAP' does not exist in ArchRProject!或在某些文件操作时出现:
Permission Error: Cannot write to ArrowFile...
3. 原因深度分析
3.1 细胞数量减少的"三部曲"
细胞数的变动并非数据丢失,而是经过了物理过滤 与数学算法排除的双重筛选:
- Step 1 (105,317): 原始 QC(TSS 分数和片段数)达标后的细胞基数。
- Step 2 (101,385): 运行
filterDoublets后,物理剔除了 3,932 个统计学意义上的双细胞(Doublets)。这是为了防止伪影细胞干扰下游分析。 - Step 3 (100,423): 运行
addIterativeLSI时,剩余的 962 个细胞被排除。通过数据回溯发现,这 962 个细胞恰好构成了整个样本C_Z8_2d_3。- 数学原因 :
addIterativeLSI默认开启了outlierQuantiles = c(0.02, 0.98),会自动过滤掉特征极其稀疏或偏倚严重的细胞。当整个样本的测序质量或复杂度处于极端分位数时,会被识别为"离群值"而无法投影到高维空间。
- 数学原因 :
3.2 找不到 Embedding 的逻辑陷阱
在 ArchR 对象中,plotEmbedding 默认寻找名为 "UMAP" 的槽位。
- 在上述循环代码中,用户将 Embedding 命名为
UMAP_0.6等自定义名称。 - 随后调用
plotEmbedding(embedding = "UMAP")时,系统无法在@embeddings槽位中找到该名称,从而报错。 - 此外,在循环中反复对同一个 LSI 空间运行
addUMAP并多次读写 Arrow 文件,极易触发底层 HDF5 文件的权限冲突。
4. 解决方案与优化建议
纠正后的标准代码
原则:先统一构建空间,后并行探索分辨率。
R
# 1. 降维(一次性完成)
proj <- addIterativeLSI(ArchRProj = proj, useMatrix = "TileMatrix", name = "IterativeLSI")
# 2. 生成全局 UMAP 投影(一次性完成,默认名称为 "UMAP")
proj <- addUMAP(ArchRProj = proj, reducedDims = "IterativeLSI", name = "UMAP")
# 3. 循环探索聚类分辨率(仅更新 cellColData,不改动降维和投影)
for(res in c(0.4, 0.6, 0.8)){
proj <- addClusters(input = proj, reducedDims = "IterativeLSI", resolution = res, name = paste0("Cluster_", res), force = TRUE)
}
# 4. 绘图(此时 Embedding 槽位存在 "UMAP")
p <- plotEmbedding(ArchRProj = proj, embedding = "UMAP", colorBy = "cellColData", name = "Cluster_0.6")
为什么"先 UMAP 再循环聚类"更好?
- 计算效率:UMAP 投影非常耗时,且它不依赖于聚类标签,只需运行一次即可。
- 逻辑清晰 :聚类只是给细胞贴标签(存储在
cellColData),而 UMAP 是坐标映射。保持投影稳定,才能客观观察不同分辨率下聚类边界的变化。
5. 核心知识点总结
- IterativeLSI 原理 :采用 TF-IDF 归一化和迭代聚类的方法处理稀疏的 ATAC-seq 数据,通过
outlierQuantiles自动过滤极端偏倚细胞以确保降维空间的鲁棒性。 - 双细胞过滤的影响 :
filterDoublets通过模拟异质性双细胞并计算富集分数来剔除物理上的重叠细胞。 - ArchR 槽位逻辑 :
@reducedDims:存储高维特征(如 LSI 矩阵)。@embeddings:存储二维坐标(如 UMAP 结果)。cellColData:存储细胞属性(如样本来源、聚类标签、TSS 分数)。
小结:在处理单细胞数据时,细胞数量的变动往往意味着算法在自动维护数据的"纯净度"。面对报错,理解 ArchR 对象的底层存储逻辑比盲目修改参数更为高效。