空间转录组数据格式: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 强调“全片整合”。

Slide-seq 数据结构分级 Bead-level 单个微珠:条形码 + 位置 + UMI CSV 文件:空间坐标|UMI 矩阵 HDF5 文件:Groups 分类存储 Puck-level 整个组织切片:数万~数十万 Beads CSV 大表:Bead × Gene 矩阵 HDF5 文件:压缩 + 索引 + 属性 聚合 → 拼接 数据流向 Raw Bed → Bead-level CSV/HDF5 → 质量控制 → 过滤 → Alignment → 合并 → Puck-level 完整矩阵 Bead —— 单点观测 Puck —— 全片整合

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 结构