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/ # 组织图像的缩略图 (可选)
你看,清晰得像厨房里的调料架。基因、坐标、图像,各归其位。
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 的正确姿势是这样的:
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。
4.5 实战归纳:什么时候该用什么?
好了,理论说了这么多,我们来点实在的。你在项目里遇到具体场景,怎么选?
- 场景1:你要把数据从一个分析软件迁移到另一个 (如 SpaceRanger → ScanPy)。 —— 用 HDF5。大多数工具都有直接导出和导入的接口。
- 场景2:你要手动检查某个基因在所有 spot 上的表达值。 —— 用 CSV。导出表格,用 Excel 或 Pandas 扫一眼。跑完删掉,别存为永久文件。
- 场景3:你要把结果图 (如聚类图) 发给合作者看。 —— 用 TIFF。保存为 600 DPI 的 LZW 压缩 TIFF,既清晰又相对较小。
- 场景4:你要归档整个实验的所有数据,留给 5 年后的自己或同行评审。 —— 一个完整的 HDF5 文件,把所有东西都打包进去。我一般在 .h5ad 文件里再加一个 group,专门放缩略图和中间处理参数。
记住,没有万能的格式。只有根据你的数据流,在正确的时间,选择正确的格式。这个能力,才是真正的「专家」和「使用者」之间的分水岭。