第二讲:数据格式入门——HDF5、h5ad、mtx 与空间文件夹
做空间转录组分析,第一步就是跟各种数据格式打交道。我刚入行那会儿,光搞清楚文件后缀就花了两天。你知道的,空间转录组不像普通单细胞,它涉及的东西特别杂:基因表达矩阵、空间坐标、组织切片图像……每个供应商、每种工具都给你弄一套不同的格式。
这一讲我就带你快速过一遍常见的格式:HDF5、h5ad、mtx,还有那个让人头疼的 spatial 文件夹结构。搞懂了这些,后面跑质控就不会一头雾水。
1. HDF5:大容量数据的容器
HDF5 全称是 Hierarchical Data Format version 5。你可以把它理解成一个压缩包,里面能装多个数据集。空间转录组的原始计数矩阵、基因注释、细胞条形码,经常被打包在一个 .h5 文件里。
举个例子,10x Genomics 的空间 Visium 数据,官方给的过滤后的表达矩阵就是 filtered_feature_bc_matrix.h5。里面分三个组:matrix 存计数,features 存基因信息,barcodes 存细胞/点索引。
# 用 h5py 看一眼内部结构
import h5py
with h5py.File("filtered_feature_bc_matrix.h5", "r") as f:
def print_attrs(name, obj):
print(name)
f.visititems(print_attrs)
# 输出大概长这样:
# matrix
# matrix/barcode_idx
# matrix/data
# matrix/indices
# matrix/indptr
# matrix/shape
# features
# features/feature_type
# features/genome
# features/id
# features/name
# barcodes
我个人习惯用 h5py 直接读,但日常分析更推荐用 scanpy.read_10x_h5(),它帮你解析好了。保存时也建议用 HDF5,压缩率高,读起来快。
2. h5ad:单细胞分析的标准格式
h5ad 是 AnnData 的后缀。AnnData 是 scanpy 生态的核心对象,它用 HDF5 作为底层存储,但把数据结构暴露得更加友好。一个 h5ad 文件包含 X(表达矩阵)、obs(观测元数据,例如每个 spot 的总 UMIs、批次标签)、var(特征元数据,例如基因名、高变基因标志)、obsm(观测水平的嵌入,例如空间坐标 spatial)、uns(非结构化数据,例如图像路径)。
import anndata as ad
adata = ad.read_h5ad("sample.h5ad")
print(adata)
# AnnData object with n_obs × n_var = 4890 × 31053
# obs: 'batch', 'n_counts', 'pct_mito'
# var: 'gene_ids', 'n_cells'
# obsm: 'spatial' # 空间坐标,形状 (4890, 2)
# uns: 'spatial' # 包含组织图像路径和缩放因子
我在项目里遇到过同事直接把 mtx + 坐标分开存,结果后期整合数据时坐标和表达矩阵对不上。我建议从一开始就统一用 h5ad 格式,obs 里记批次、条件,obsm 里放坐标,一起写进一个文件。以后迁移、分享都省心。
adata.write("output.h5ad") 保存时,如果数据比较大,可以加 compression='gzip' 参数,文件能小 40%。
3. mtx:市场交换格式,三件套
mtx 是 Matrix Market 格式,常见于 10x 早期版本和 CELESTA 等自定义 pipeline。它永远跟两个文件配套:barcodes.tsv.gz 和 features.tsv.gz,合称“三件套”。
结构很简单:
matrix.mtx.gz
%%MatrixMarket matrix coordinate real general
% metadata
4890 31053 14532288
0 5 1
0 8 2
1 3 1
...
# 每行:spot_index(0-based) gene_index(0-based) count
如果你拿到的是 mtx,我建议你立马用 scanpy.read_10x_mtx() 转成 h5ad。为啥?因为 mtx 存不了坐标和图像。你得额外再读 tissue_positions_list.csv,然后手动写到 obsm 里。多一步就多一个坑。
head 看一眼。我曾经就因为 features.tsv 里第三列多了个空值,导致基因名全是 NaN,排查了三个小时。
4. spatial 文件夹:图像与坐标的藏身处
这个文件夹是 10x Visium 数据的标配,名字通常叫 spatial/,放在 outs/ 下。里面长这样:
spatial/
├── tissue_hires_image.png # 高分辨率组织图,通常 2000×2000
├── tissue_lowres_image.png # 低分辨率缩略图,600×600
├── scalefactors_json.json # 缩放比例、spot 直径等
├── tissue_positions_list.csv # 所有 spot 坐标、是否在组织内
└── aligned_fiducials.jpg # 对齐用的 fiducial 标记(不常用)
最核心的是 tissue_positions_list.csv,每行一个 spot:
barcode,in_tissue,array_row,array_col,pxl_row_in_fullres,pxl_col_in_fullres
AACCCAAGTCATTTCG-1,1,0,0,2003.5,2175.2
AACCCACCTAGCACAT-1,1,0,1,1981.6,2235.4
...
pxl_row_in_fullres 和 pxl_col_in_fullres 是原始像素坐标,基于 tissue_hires_image.png。如果你想在低分辨率图上画点,需要乘 scalefactors_json.json 里的 tissue_lowres_scalef 因子。
# scalefactors_json.json 内容示例
{
"spot_diameter_fullres": 89.5,
"tissue_hires_scalef": 0.5,
"tissue_lowres_scalef": 0.25,
"fiducial_diameter_fullres": 144.5
}
tissue_positions_list.csv 时,一定要检查 in_tissue 字段。只有值为 1 的 spot 才在组织区域里,那些值为 0 的点是背景或 fiducial 标记,通常要过滤掉。否则质控指标会漂。
5. 图像与坐标的关系
空间转录组的“空间”二字,核心就是坐标系统。一般有两种:
- 像素坐标(pixel coordinates):对应原始图像,单位是像素。
pxl_row是 y 轴(从图像顶部往下),pxl_col是 x 轴(从左到右)。 - 阵列坐标(array coordinates):
array_row和array_col,是探针阵列上的逻辑位置,跟图像无关。用于 spot 之间的几何关系分析,比如找邻域。
我把它们画成一张流程图,方便你理解:
6. 实战建议:如何选择一个好格式
| 格式 | 适用场景 | 需配套文件 | 推荐指数 |
|---|---|---|---|
| HDF5 (.h5) | 10x 官方输出,后续要转 adata | 无额外坐标文件 | ⭐⭐⭐⭐ |
| mtx 三件套 | 自定义 pipeline,但缺坐标 | tissue_positions_list.csv | ⭐⭐ |
| h5ad | 分析全流程,整合批次 | 无,自带全部信息 | ⭐⭐⭐⭐⭐ |
| spatial 文件夹 | 图像可视化,坐标溯源 | 配合 h5ad 使用 | ⭐⭐⭐⭐ |
scanpy.read_10x_h5() 读 HDF5,然后用 sc.read_json() 读 scalefactors,再读 tissue_positions_list.csv 构建 obsm['spatial'],最后统一存成 h5ad。全程不超过 10 行代码。