空间数据读取:从H5到Seurat对象
这一章我们聊聊数据读取。很多新手拿到10X Visium数据时,第一反应就是直接甩一个Load10X_Spatial完事。但说实话,真到项目里往往没这么简单——数据目录结构、H5版本、图像对齐、质量控制,每一步都有可能翻车。我个人习惯是先搞清楚背后到底在干啥,再决定用哪个函数。
1. Load10X_Spatial — 一把梭还是拆步骤?
Load10X_Spatial是Seurat提供的“一站式”数据读取函数。它把你从原始Visium输出目录(包含filtered_feature_bc_matrix/、tissue_positions_list.csv、spatial/下的图像和图谱)直接打包成一个Seurat对象。
函数签名与关键参数
Load10X_Spatial(
data.dir, # 包含子目录的Visium输出根目录
filename = "filtered_feature_bc_matrix.h5", # 可选H5文件或mex目录
assay = "Spatial",
slice = "slice1",
filter.matrix = TRUE,
to.upper = FALSE,
image = NULL,
...)
| 参数 | 说明 | 避坑点 |
|---|---|---|
data.dir | 指向包含spatial/和矩阵目录的文件夹 | 路径不要带中文,我曾经被Windows反斜杠折腾过 |
filename | 默认读H5,也可改成"filtered_feature_bc_matrix"(mex格式) | 如果目录下有多个H5,指定清楚 |
filter.matrix | 是否只保留组织覆盖到的spots | 建议TRUE,否则会有一大堆背景噪声 |
image | 自定义VisiumV1对象,可跳过自动图像加载 | 少用,除非你想手动校正图像 |
你想想看,如果数据目录里的tissue_positions_list.csv缺失或者行列不对,这个函数会直接报错。嗯,这里要注意:Visium v2的数据目录结构略有不同,我建议先看一眼目录内容再调用。
2. 手动拆解:H5文件 + image数据
为什么要手动拆?两个场景:一是你的数据不是标准Visium输出(比如定制芯片),二是你想单独检查H5的分子信息。
我一般分三步走:
- 读取H5矩阵 —— 用
Read10X_H5 - 加载图像和组织信息 —— 用
ReadVisiumImage - 组装 —— 手动构造
Seurat对象
读取H5文件
library(Seurat)
library(SeuratObject)
h5_path <- "filtered_feature_bc_matrix.h5"
counts <- Read10X_H5(h5_path)
# 返回一个dgCMatrix,行是基因/features,列是spots
Read10X_H5会读取基因表达、feature类型(Gene Expression / Antibody Capture等)和barcode信息。我遇到过一种情况:同一个样本跑了两次,H5里有两个barcode列名完全一样,结果Read10X_H5直接合并了……记得后续去重。
加载图像与组织覆盖
library(Seurat)
img_path <- "spatial/tissue_lowres_image.png"
# 已有ReadVisiumImage函数
image <- ReadVisiumImage(img_path, scale = "lowres", image.name = "tissue_lowres_image.png")
# 返回一个VisiumV1对象
别忘了还要加载tissue_positions_list.csv和scalefactors_json.json。这些文件会被自动封装到VisiumV1对象里。
手动拼接
spatial.data <- list(
counts = counts,
image = image,
tissue.positions = read.csv("spatial/tissue_positions_list.csv", row.names = 1),
scalefactors = jsonlite::fromJSON("spatial/scalefactors_json.json")
)
# 创建Seurat对象
so <- CreateSeuratObject(counts = spatial.data$counts, assay = "Spatial")
so[["slice1"]] <- spatial.data$image
这一步其实和Load10X_Spatial内部做的差不多,手动来能给更大的控制权,比如做图像偏移校正。
3. 创建空间对象:SpatialExperiment vs Seurat对象
现在有两个主流选择:Seurat的Assay5和Bioconductor的SpatialExperiment。说白了,两者都能存空间坐标、图像和表达矩阵,但设计哲学不一样。
| 对比维度 | Seurat对象 | SpatialExperiment |
|---|---|---|
| 数据容器 | Assay5(支持多模态) | 单模态SummarizedExperiment扩展 |
| 空间坐标 | 存储在images和meta.data | 专门的spatialCoords()访问器 |
| 图像处理 | 内嵌VisiumV1/ST类 | 通过SpatialImage对象或外部链接 |
| 下游分析 | Seurat全流程(降维、聚类、差异) | 需要配合scran/zinbwave等 |
| 个人感受 | 上手快,集成性强 | 更灵活,适合自定义pipeline |
我在项目中曾一度想用SpatialExperiment来加速大矩阵操作,但后来发现团队的后续分析全都依赖FindSpatiallyVariableFeatures这类Seurat专用函数,最后还是切回了Seurat。建议你提前搞清楚下游要用哪些包。
如何将数据转成SpatialExperiment
library(SpatialExperiment)
spe <- SpatialExperiment(
assays = list(counts = counts),
spatialCoords = as.matrix(tissue.positions[, c("col", "row")]),
colData = tissue.positions[, c("in_tissue", "barcode")],
sample_id = "slice1"
)
不过要注意SpatialExperiment不自动加载图像,你得手动添加SpatialImage对象或者用imgData链接。
4. 实战避坑
⚠️ 常见坑位
- 文件名大小写:
tissue_positions_list.csvvstissue_positions_list.csv,Linux下严格区分。我曾因小写l写成大写L,报错找了一下午。 - H5版本不兼容:某些旧版10X输出是hdf5 1.8,而Read10X_H5默认用h5py 3.x,会报“Not a HDF5 file”。解决办法:用
rhdf5::h5ls先检查文件是否可读。 - 图像分辨率:
tissue_lowres_image.png通常是600x600,但有些自定义数据可能不同。读取时scale参数要和scalefactors_json.json里的tissue_lowres_scalef匹配。
5. 本章核心流程
下面这张SVG图把从原始数据到Seurat对象的完整路径画出来了,建议你把它贴在笔记开头,随时回顾。
💡 我的小习惯:每次拿到新数据先用list.files("data/spatial")扫一眼,确认有tissue_lowres_image.png和scalefactors_json.json,再决定用自动还是手动模式。如果项目中有多个切片需要整合,强烈建议先用Load10X_Spatial试跑一个切片,确认所有文件可读,再批量处理。