空间转录组数据格式:Bead-level 与 Puck-level,HDF5 与 CSV 结构
搞 Slide-seq 的人,都绕不开它的数据格式。
我第一次拿到原始数据时,说实话也有点懵。为什么有 bead-level?puck-level 又是什么?HDF5 和 CSV 到底怎么选?
这一章,我就把这两个层级、两种格式给你捋清楚。理解了它们,后续处理才能走得顺。
Bead-level:最原始的观测单元
每个微珠(bead)就是 Slide-seq 的“像素点”。
你看这张图就明白了:Bead 上连了独特的条形码(barcode),捕获它位置上的 mRNA。所以每个 bead 本身的原始数据,就是“一对信息”——空间坐标 + 一条表达量(UMI count)。
Puck-level:一个组织切片的完整数据包
一个 puck 包含了几万个甚至几十万个 bead,对应一个组织切片的所有 spatial transcriptomic profile。
Puck-level 数据是把所有 bead 数据聚合在了一起。相当于一个“大表格”:行是 bead,列是基因,值是对应 UMI count。
我以前处理过一个 10 μm 直径的 puck,差不多有 50 万个 bead。光读元数据就花了十几分钟。倒不是数据量大到吓人,而是格式没选对。
核心认知
Bead-level 强调“单点观测”,Puck-level 强调“全片整合”。
HDF5 vs CSV:两种存储格式的博弈
你可能会问:既然都能存,为什么还要区分?
说白了,CSV 是人类可读的,HDF5 是机器和性能优先的。
我当年刚入行时,习惯把一切数据另存为 CSV——方便拿 Excel 看一眼。直到第一次处理 100 GB 级别的 Slide-seq 数据,单是加载 CSV 就把内存吃光了。
嗯,那次之后我彻底倒向了 HDF5。
一句话概括
小规模探索用 CSV,正式生产级分析首选 HDF5。
| 维度 | HDF5 | CSV |
|---|---|---|
| 压缩 | 支持(LZF / GZIP 等) | 不支持(文本文件) |
| 随机访问 | 支持切片索引 | 只能逐行加载 |
| 跨平台 | 良好(C、Python、R) | 通用 |
| 人群可读 | 需专用工具 | 任何文本编辑器 |
| 大型矩阵性能 | 优秀 | 极差(上 GB 时卡死) |
| 元数据支持 | 原生 Attribute/Groups | 需另写文件 |
HDF5 结构拆解:Group · Dataset · Attribute
HDF5 文件就像一个“文件系统”的迷你版。
- Group:相当于目录(/,/assay,/bead)
- Dataset:真正的数据矩阵(如 gene_matrix)
- Attribute:元数据(单位、版本、切片信息)
以 Puck 级别的 HDF5 为例,内部结构大概是这样:
/
├── assay
│ ├── gene_matrix (Dataset, shape: beads × genes)
│ ├── barcodes (Dataset, 1D array)
│ └── coordinates (Dataset, 2D: x, y)
├── meta
│ ├── puck_id (Attribute: "Puck_20220601")
│ ├── resolution (Attribute: "10μm")
│ └── chemistry (Attribute: "Slide-seq v2")
└── quality
├── qc_flag (Dataset, 0/1 array)
└── umi_count (Dataset)
CSV 结构:粗暴但直观
如果要做轻量级预览,CSV 依然是首选。
bead_barcode, x_pos, y_pos, gene_GAPDH, gene_MALAT1
BCAACGATCG, 10.2, 20.3, 15, 102
BCAATACTTG, 10.4, 20.1, 3, 78
BCAAGTCATG, 10.7, 19.8, 0, 45
...
但注意:一个包含两万个基因的矩阵,CSV 文件大小会膨胀到原始 HDF5 的 2~4 倍。
我曾尝试用 CSV 保存一个完整 Puck(约 30 万 beads、2 万基因),结果文件直接 50 GB,打开一次就要半小时。那之后,我就在脚本里加了一行自动转 HDF5 的逻辑。
避坑指南:格式转换中的那些坑
我曾经踩过的坑:
- CSV 列顺序改变:读入时忘了指定 dtype,字符串跟数字混在一起,统计全错。
- HDF5 的群组重名:用 Pandas 写 HDF5 时,多次写入同一 group 未指定 mode='a',数据被覆盖。
- 大矩阵的 Memory Mapping:直接 .to_numpy() 会把全部数据拉到内存,用 Chunked 读取才行。
- Puck 基因名不统一:不同批次基因 ID 格式不同(ENSEMBL vs Gene Symbol),合并前必须先转换。
我的习惯做法:
- 日常探索 → 用 CSV 子集(只选 1000 个高变基因 + 2000 个 beads)
- 正式分析 → 所有数据转 HDF5,集群上跑 Python 脚本,
h5py配合 chunked I/O - 元数据全部写入 HDF5 的 Attribute,避免丢失批注信息
小结
总结一下这一章的核心:
- Bead-level 数据是基础观测单元,Puck-level 是整合后的全片矩阵
- HDF5 适合大数据量、频繁随机访问、需要保持元数据一致性的场景
- CSV 适合快速预览、小型数据集、或者跨系统交换
- 正式产线项目里,我强烈建议你拥抱 HDF5 结构