4. 数据获取与格式:HDF5、TIFF、CSV 归纳总结

数据,才是空间转录组研究的起点。

模型再漂亮,理论再扎实,拿不到对的数据、搞不清格式里的门道,一切都白搭。我见过太多同学,代码跑了大半天,最后发现是读入的数据格式解析错了,切片坐标全是乱的。说实话,那感觉比 bug 本身还憋屈。

这一章,我就带你把这些「看门」的格式彻底捋清楚。我们不搞教科书式的罗列,而是从实战出发,把 HDF5、TIFF 和 CSV 这几个最常见的格式,它的「性格」、它为什么存在、以及你怎么跟它打交道,一次性讲透。

4.1 为什么我们需要多种格式?

你想想看,一份空间转录组数据里,藏着多少种类型的信息?

  • 基因表达矩阵:几万个基因 × 几千到几万个 spot,稀疏得很。
  • 空间坐标:每个 spot 在组织切片上的 x, y 位置。
  • 高分辨率组织图像:H&E 染色图,动不动就是几个 G 的大小。
  • 元数据:实验条件、组织来源、测序参数……

一种文件格式,根本不可能优雅地装下这所有东西。CSV 太笨,TIFF 只能管图像,HDF5 倒是什么都能装,但生态不如 CSV 通用。所以,现实就是我们必须学会跟它们共存。

4.2 HDF5:你处理高通量数据的瑞士军刀

我个人习惯,只要数据量超过 500 MB,第一选择一定是 HDF5。为什么?因为它天生就是为「大规模科学数据」设计的。

它到底是个啥?

HDF5 本质上是一个文件系统。在单文件里,你就能模拟出目录树的结构。

  • 组 (Group):就像文件夹,用来组织数据。
  • 数据集 (Dataset):就像文件,实际上存的是 N 维数组。

我用一个实际的 10X Visium 数据做例子,你感受一下它的组织方式:

file.h5ad
├── X                 # 表达矩阵 (稀疏格式存储,只存非零值,省空间)
├── obs/              # 观测信息 (就是 spot 的元数据)
│   ├── barcode       # 每个 spot 的条形码
│   ├── tissue        # 是否在组织内部
│   └── cluster       # 聚类结果
├── var/              # 变量信息 (就是基因的元数据)
│   └── gene_name     # 基因符号
├── obsm/             # 多维观测注释
│   └── spatial       # 空间坐标 (形状是 n_spots x 2)
└── uns/              # 非结构化数据
    └── image/        # 组织图像的缩略图 (可选)

你看,清晰得像厨房里的调料架。基因、坐标、图像,各归其位。

我的经验:即使你用的工具(如 ScanPy)帮你封装好了 .h5ad 格式,我也建议你用 h5py 或 tables 库打开看一次。我曾经在调试一个批次效应时,就是因为肉眼查看了 HDF5 的内部结构,才发现官方文档里漏标了一个 hidden group。那感觉,就像找到了图纸上的暗门。

4.3 TIFF:图像界的「活化石」,但你必须懂它

TIFF 这个格式,年纪比很多读我这篇文章的人还大。但它在空间转录组里,地位不可撼动。因为组织切片的原始高分辨率图像,几乎全是 TIFF。

为什么不用 JPEG 或 PNG?

说白了,就是保真度和位深。JPEG 是有损压缩,会丢失像素细节,对定量分析来说是致命的。TIFF 支持 16 位甚至 32 位深度的灰度图,能保留最原始的荧光信号强度。

这里有个坑,我必须得说,尤其是当你用的平台是 Visium HD 或 Xenium 时:

  • 单页 TIFF:只包含一个图像层,通常是 H&E 染色或 DAPI 核染。
  • 多页 TIFF / OME-TIFF:这是空间转录组平台的标配。一个文件里塞了多个通道的图像。比如,通道 0 是 DAPI,通道 1 是某个基因的荧光。
避坑指南:千万不要用普通的图片查看器打开 OME-TIFF。它大概率只会显示第一个通道,让你误以为图像缺失。我曾经因为这个浪费了两天时间,后来用 Python 的 tifffile 库一读,才知道后面还有 3 个通道。正确的做法是用专门支持 OME 格式的工具,或者在代码里显式地指定要读取的通道。

读取 OME-TIFF 的正确姿势是这样的:

import tifffile

# 用 tifffile 库读取,它会自动处理多页
with tifffile.TiffFile("sample.ome.tiff") as tif:
    # 读取所有系列 (这里通常只有一个系列)
    series = tif.series[0]
    # 读取所有通道的图像,形状一般是 (C, H, W)
    image = series.asarray()
    # 例如提取 DAPI 通道 (假设是第0个通道)
    dapi_channel = image[0, :, :]

print(f"Image shape: {image.shape}")

看到了吗?用对工具,就是这么简单。

4.4 CSV:最简单的,也是最容易出错的

CSV,每个做数据分析的都不可能避开它。空间转录组里,它通常扮演轻量级数据的载体,比如:

  • 表达矩阵的子集 (比如只选前 100 个高变基因)
  • 元信息表格 (像 4.2 节中 obs 的内容)
  • 手动标注的 ROI 区域坐标

但越简单的东西,越容易在细节上翻车。

问题 现象 我建议的解决方案
分隔符不一致 有人用逗号,有人用制表符(tsv),甚至有人用分号 打开文件时,永远先用 head -n 10 <file.csv> 看看第一行,确认分隔符。Python 里用 pd.read_csv(..., sep=',') 显式指定。
引号内嵌逗号 基因名如 "ENSG0001.1, alt" 会被错误拆分 用 quotechar='"' 和 quoting=csv.QUOTE_ALL 或直接上 pandas,它自动处理。
缺失值五花八门 有的是空字符串,有的是 'NA',有的是 'NaN' 读取后统一用 .fillna() 或 .replace() 处理。
浮点精度损失 坐标值 123.456789 存成 CSV 再读回来,可能变成 123.456790 对于坐标或表达式值,建议保留 6-8 位小数,或者直接存成二进制格式。如果非要用 CSV,那就用高精度数据类型读:pd.read_csv(..., dtype={'x': 'float64'})

一句话总结:CSV 只适合做「数据交换」和「人眼查看」,不适合做「主要分析格式」。正式的分析管线,请务必用 HDF5。

空间转录组数据处理中的数据格式选择逻辑 原始配对数据 需要存储高分辨率图像? 需要存储结构化的表达数据? 选择1: HDF5 + 外部 TIFF 元数据 + 稀疏矩阵 → HDF5 图像切片 → OME-TIFF 选择2: 纯 HDF5 (建议) 所有数据塞进一个文件 (如 .h5ad, .loom) 补充: CSV 用于子集交换 / 元数据 核心结论:主分析链用 HDF5,图像通道用 OME-TIFF,人机交互用 CSV。

4.5 实战归纳:什么时候该用什么?

好了,理论说了这么多,我们来点实在的。你在项目里遇到具体场景,怎么选?

  • 场景1:你要把数据从一个分析软件迁移到另一个 (如 SpaceRanger → ScanPy)。 —— 用 HDF5。大多数工具都有直接导出和导入的接口。
  • 场景2:你要手动检查某个基因在所有 spot 上的表达值。 —— 用 CSV。导出表格,用 Excel 或 Pandas 扫一眼。跑完删掉,别存为永久文件。
  • 场景3:你要把结果图 (如聚类图) 发给合作者看。 —— 用 TIFF。保存为 600 DPI 的 LZW 压缩 TIFF,既清晰又相对较小。
  • 场景4:你要归档整个实验的所有数据,留给 5 年后的自己或同行评审。 —— 一个完整的 HDF5 文件,把所有东西都打包进去。我一般在 .h5ad 文件里再加一个 group,专门放缩略图和中间处理参数。

记住,没有万能的格式。只有根据你的数据流,在正确的时间,选择正确的格式。这个能力,才是真正的「专家」和「使用者」之间的分水岭。