4、数据下载与格式了解:获取公共数据集、H5与MEX格式、元数据文件解析
好,这一章我们来实战一下——先把数据搞到手,再搞明白它长什么样。我个人觉得,很多同学分析半天结果不对,往往不是算法的问题,而是数据格式就搞错了。嗯,咱们从源头开始。
4.1 公共Visium数据集去哪下
目前最主流的来源有三个,我按推荐顺序列一下:
- 10x Genomics 官方数据集页面:最标准、最干净。每个数据集都附带完整的H5和MEX格式,还有图片和元数据。
- GEO (Gene Expression Omnibus):很多公开发表的论文会把原始数据存这里。但要注意,GEO里经常只给MEX格式,H5可能得自己转。
- SpatialData / spatialdata.scverse.org:社区整理好的参考数据集,适合练习。我个人的习惯是先用官方数据跑通流程,再碰GEO的“野生”数据。
举个具体的例子,你可以在10x官网搜索“Human Breast Cancer Visium”或“Mouse Brain Visium”,下载页面会给一个压缩包,里面包含raw_feature_bc_matrix.h5和filtered_feature_bc_matrix.h5。我去年做项目时遇到过下载页面上同时列了两种格式,结果团队里有人只下了MEX没下H5,后面解析元数据时还得重新下。
4.2 H5格式的结构与读取
H5全名叫HDF5,说白了就是一个分层容器,里面能塞矩阵、字符串、属性。Visium生成的H5文件打开后,你会看到类似下面的结构:
/
├── matrix
│ ├── barcodes # 细胞条形码,shape (n_spots,)
│ ├── data # 非零元素的count值,shape (n_nonzero,)
│ ├── indices # 非零元素的行索引(稀疏矩阵的列索引)
│ ├── indptr # 稀疏矩阵的列指针
│ └── shape # 矩阵形状 [n_features, n_spots]
└── features
├── _all_tag # 类型标记
├── feature_type # 特征类型(Gene Expression, Antibody Capture等)
├── genome # 基因组
├── id # 基因ID(ENSEMBL)
├── name # 基因名称
└── interval # 坐标信息
用Python读取其实非常直接。我习惯用scanpy.read_10x_h5(),一行搞定:
import scanpy as sc
adata = sc.read_10x_h5("filtered_feature_bc_matrix.h5")
print(adata.shape) # (36601 个基因, 4689 个spot)
print(adata.obs_names) # barcode 列表
print(adata.var_names) # gene 名称
如果你坚持用h5py库手撸,也能拿到原始数组,但我不推荐——因为你还要自己手动把CSR矩阵拼起来,容易出错。我曾经在一次内部培训中演示手动读取,结果indices和indptr的维度搞反了,多花半小时debug。所以,能用ScanPy就别自己造轮子。
4.3 MEX格式的三大核心文件
MEX是Market Exchange格式的缩写,Visium用三个文件来描述同一个表达矩阵:
| 文件名 | 内容 | 行数 |
|---|---|---|
matrix.mtx | 非零表达的坐标和count值 | n_nonzero + 3行头 |
barcodes.tsv | 每个spot的条形码 | n_spots |
features.tsv | 基因ID、基因名称、类型 | n_features |
这三个文件必须放在同一个目录下,名字不能改。ScanPy读取MEX的方法也很简单:
import scanpy as sc
adata = sc.read_10x_mtx("filtered_feature_bc_matrix/")
# 注意这里是目录路径,不是文件路径
print(adata)
你想想看,为什么10x要提供两种格式?我个人的理解是:H5把所有数据锁在一个文件里,方便程序化加载;MEX是文本+稀疏矩阵的经典组合,方便你用命令行grep、awk查一下基因名对应的barcode,或者给其他工具用。我在跨平台迁移数据时,就经常在MEX目录下用head -n 5 barcodes.tsv快速验证spot数量。
scanpy.read_10x_mtx,它自动处理列数变化。
4.4 元数据文件里有什么
除了表达矩阵,每次Visium实验还会生成一个spatial/目录,里面包含元数据。
tissue_hires_image.png– 高分辨率组织切片图片tissue_lowres_image.png– 低分辨率预览图scalefactors_json.json– 缩放因子、spot直径等物理参数tissue_positions_list.csv– 每个spot的像素坐标和组织区域判断(in_tissue=1表示在组织上)
来看一个典型的scalefactors_json.json:
{
"tissue_hires_scalef": 0.17012685,
"tissue_lowres_scalef": 0.05103706,
"fiducial_diameter_fullres": 206.73,
"spot_diameter_fullres": 110.34,
"regist_target_img_scalef": 0.17012685
}
这些参数对后续空间坐标映射至关重要。比如,你想把低分辨率图上的像素坐标转回高分辨率,就得乘以tissue_lowres_scalef的倒数。我记得刚做空间转录组时,直接拿低分辨率图的像素去匹配H5里的barcode坐标,结果偏了半个视野。后来一查,原来是少乘了一个scalefactor。嗯,这个坑你别再踩了。
tissue_positions_list.csv长这样:
barcode,in_tissue,array_row,array_col,pxl_row_in_fullres,pxl_col_in_fullres
AAACAAGTATCTCCCT-1,1,0,0,334,353
AAACAAACATTCGTTT-1,1,0,1,537,353
...
其中in_tissue=1表示该spot位于组织切片上,=0表示在背景区。后面的array_row/col是捕获阵列的格子坐标,而pxl_row/col是H&E全分辨率图像上的像素坐标。
adata.obs.head()看是不是有in_tissue列。如果没有,说明你漏了spatial目录或者没正确合并元数据。
看完这个流程图你就明白了。从数据源下载后,不管选H5还是MEX,最终喂给ScanPy的都是一个Anndata对象。再往后,你就可以做标准化、降维、去批次等一系列操作了。
最后再啰嗦一句:元数据文件里的spatial/scalefactors_json.json和tissue_positions_list.csv,很多人一开始会忽略。但如果你要做空间可变基因分析(SPARK、Seurat等),这两个文件是必需的。别不信,我曾经就吃过亏——跑完流程才发现没有合并空间坐标,只好回头再读一次。