数据获取与格式介绍:Visium的HDF5、图像文件与Slide-seq Bead-level数据
做空间转录组整合,第一步不是跑代码,而是搞清楚你手里拿到的到底是什么。Visium和Slide-seq虽然都是空间转录组技术,但底层数据格式完全两码事。搞混了,后面所有流程都得崩。
我自己刚入行时,就吃过这个亏——拿到Visium数据,以为是普通单细胞表达矩阵,结果死活跑不通。后来才发现,人家是用HDF5存的,而且图像文件才是空间坐标的真正来源。今天咱们就把这两大技术的数据“解剖”清楚。
2.1 Visium数据:藏在HDF5里的秘密
Visium官方输出是一个文件夹,里面通常包含:一个HDF5文件(比如filtered_feature_bc_matrix.h5),一个图像文件夹(含高分辨率组织切片图),以及一个spatial目录(放坐标和缩放信息)。你如果用10x Genomics的Space Ranger跑完,会得到这些。
HDF5是什么?说白了,它是一种层级式数据结构,类似一个压缩的“文件柜”。里面可以嵌套多个数据集(datasets),还自带索引。你想想看,一个10x Visium样本,可能有5000个spots,每个spot测到2000个基因,如果用CSV存,不仅体积大,读取也慢。HDF5天生适合这类稀疏数据。
| HDF5内部结构 | 说明 |
|---|---|
| matrix | 表达矩阵主体(稀疏格式) |
| barcodes | 每个spot的barcode序列 |
| features | 基因名或基因ID |
| feature_type | 是“Gene Expression”还是“Antibody Capture” |
读取HDF5,我习惯用h5py或scanpy自带的read_10x_h5。下面是示例代码,你可以直接跑跑看:
import h5py
import scanpy as sc
# 方法一:直接用scanpy(推荐)
adata = sc.read_10x_h5("filtered_feature_bc_matrix.h5")
adata.var_names_make_unique()
print(adata)
# 方法二:手动探索HDF5结构
with h5py.File("filtered_feature_bc_matrix.h5", "r") as f:
print(f.keys()) # 查看顶层group
print(f["matrix"].keys()) # 查看matrix下的dataset
图像文件呢?Visium的高分辨率图像(通常是.tif或.png)用作空间对齐的底图。Space Ranger会利用图像和tissue_position.csv把spots映射到组织轮廓上。你拿到原始数据后,别只盯着表达矩阵,图像里的空间信息才是关键。
2.2 Slide-seq Bead-level数据:极简但高效
Slide-seq跟Visium最大的不同,是它没有固定阵列,而是用随机排列的微珠(beads)捕获RNA。每个珠子直径约10微米,位置靠化学印迹和测序来确定。所以它的数据格式是“一珠一行”——每个珠子有一个唯一的barcode和一个空间坐标(x, y, z)。
官方提供的数据通常是一个数字表达矩阵(.csv或.tsv),配一个坐标文件(.csv)。但也有标准化格式叫Bead-level H5,里面包含barcode、bead类型、UMI矩阵等。不过我自己用过最多的,还是最原始的“三联文件”:
- expression_matrix.txt:行为beads,列为基因
- bead_barcodes.txt:每行一个barcode
- bead_coordinates.txt:每行对应barcode的x,y坐标(有时也有z)
你想想看,这种格式有多直接——没有层级,没有压缩。但坏处是文件体积可能很大,一个10万beads × 2万基因的矩阵,存成txt要几百兆,读取慢得想哭。
scipy.sparse的coo_matrix加载。
下面是读取Slide-seq典型数据的示例:
import numpy as np
import pandas as pd
from scipy.sparse import coo_matrix, csr_matrix
# 以稀疏格式读入表达矩阵
expr = pd.read_csv("expression_matrix.txt", sep="\t", header=None)
# 假设expr三列:bead_idx, gene_idx, count
bead_idx = expr.iloc[:, 0].values
gene_idx = expr.iloc[:, 1].values
counts = expr.iloc[:, 2].values
# 构建稀疏矩阵
sparse_mat = coo_matrix((counts, (bead_idx, gene_idx))).tocsr()
# 读取barcode和坐标
barcodes = pd.read_csv("bead_barcodes.txt", header=None)[0].tolist()
coords = pd.read_csv("bead_coordinates.txt", sep="\t", header=None)
coords.columns = ["x", "y"]
2.3 两大格式对比:一张表看明白
为了让你更直观,我把核心差异列了个表。记牢这些,后面整合时心里有数。
| 对比维度 | Visium (10x) | Slide-seq |
|---|---|---|
| 数据存储方式 | HDF5 + 图像 | 文本矩阵 + 坐标文件 |
| 空间分辨率 | ~55 μm/spot | ~10 μm/bead |
| 坐标来源 | 图像对齐 + 预设阵列 | 测序后的化学标记位置 |
| 表达矩阵结构 | 稀疏HDF5,内含barcode/feature | 稀疏或稠密文本,需手动组装 |
| 典型文件大小 | 几MB到几十MB | 几百MB到GB级 |
| 示例读取工具 | scanpy、Seurat、h5py | pandas、scipy、Scanpy(需转换) |
2.4 数据获取渠道与合规
数据从哪里搞?Visium数据:官方10x Genomics官网有公共数据集,也可以从GEO/SRA搜10x Visium关键词。Slide-seq数据:哈佛的Feinberg实验室网站、Broad Institute的Single Cell Portal,以及GEO都能找到。记得看使用条款。
我个人习惯是先下载README文件,很多时候格式说明就写在里面。有一次我偷懒没看,结果发现下载的Visium HDF5里用了版本2的格式,scanpy旧版本读不出来,折腾了半天。所以,一定先看文档。
h5py读就行,省去了拼坐标的麻烦。优先找这种。
2.5 本章知识结构图
下面这张图梳理了Visium和Slide-seq数据从获取到格式解析的关键路径,你看一眼就能形成整体印象。
2.6 格式转换实用备忘录
做整合时,你需要把Visium的HDF5和Slide-seq的文本转换成统一的AnnData对象。这里给出我常用的两个函数骨架:
# 将Slide-seq bead-level数据转成AnnData
def slide_seq_to_anndata(expr_file, coord_file, barcode_file=None):
import scanpy as sc
import pandas as pd
from scipy.sparse import coo_matrix
# ... 组装代码(参考前文)...
adata = sc.AnnData(X=sparse_mat, obs=obs, var=var)
adata.obsm["spatial"] = coords[["x", "y"]].values
return adata
# 读取Visium HDF5到AnnData(scanpy已封装)
def visium_to_anndata(h5_path, image_dir=None):
adata = sc.read_10x_h5(h5_path)
# 添加空间坐标(需额外读tissue_positions.csv)
positions = pd.read_csv("spatial/tissue_positions.csv", index_col=0)
adata.obs["tissue"] = positions.loc[adata.obs_names, "in_tissue"]
adata.obsm["spatial"] = positions.loc[adata.obs_names, ["x", "y"]].values
return adata
注意:Visium的tissue_positions.csv默认使用像素坐标,而Slide-seq坐标是物理微米。整合前一定要先统一单位。我一般把Visium坐标乘以图像的分辨率(如0.219 μm/pixel),再跟Slide-seq对齐。