2、空间转录组数据基础:AnnData数据结构、读取10X Visium数据
空间转录组学里,AnnData 这个数据结构你绕不开。说白了,它就是为单细胞和空间组学量身定做的“数据容器”。我刚开始接触时也被它的嵌套结构绕晕过,但用顺手后会发现——它把所有关联数据打包得清清楚楚,比你自己用 dict 拼一个稳定多了。
2.1 AnnData 核心结构
AnnData 的核心就四个字:表、行、列、附属。你可以把它想象成一个 Excel 表格,但比 Excel 聪明得多。每个 AnnData 对象里藏了这么几个关键属性:
| 属性名 | 中文叫法 | 通俗解释 |
|---|---|---|
.X | 主要数据矩阵 | 基因×细胞的表达矩阵,通常用 numpy 的稀疏矩阵存 |
.obs | 观测(细胞/点)元数据 | 每一行是一个 spot 或 cell,列是各种注释(样本、聚类、QC指标) |
.var | 变量(基因)元数据 | 每一行是一个基因,列是基因名、染色体位置等 |
.obsm | 观测的多维数组 | 存放降维结果(PCA、UMAP)或空间坐标(spatial) |
.varm | 变量的多维数组 | 基因载荷、差异基因等 |
.uns | 非结构化数据 | 可以放任何东西——图像、聚类颜色、分析参数 |
.layers | 多层矩阵 | 例如原始count、标准化count并存 |
你可能觉得记不住,嗯,我有个小技巧:把 .obs 当成“哪桌客人”,.var 当成“菜单上的菜”,.X 就是“每桌点了哪些菜”。理解了这层关系,后面所有 API 调用都顺了。
我个人习惯在读取数据后第一件事就是打印 adata 对象的摘要。AnnData 的 repr 会直接告诉你形状、稀疏程度和关键字段,一眼就能确认数据加载是否正确。
2.2 读取 10X Visium 数据
10X 的 Visium 数据通常由几个文件组成:
filtered_feature_bc_matrix.h5或raw_feature_bc_matrix.h5(HDF5 格式的 count 矩阵)spatial/目录下的tissue_positions_list.csv(每个 spot 的坐标)spatial/目录下的scalefactors_json.json(组织图像缩放参数)spatial/目录下的tissue_hires_image.png或tissue_lowres_image.png
最省事的读取方式是直接用 scanpy.read_visium():
import scanpy as sc
adata = sc.read_visium(
path = "./data/Visium_Mouse_Olfactory_Bulb/",
count_file = "filtered_feature_bc_matrix.h5",
library_id = "sample_1"
)
print(adata)
这段代码做了什么?它自动解析 H5 文件,把 count 矩阵塞进 adata.X;把 spot 的条形码、组织分类放进 adata.obs;把空间坐标(pxl_col, pxl_row)放进 adata.obsm["spatial"];还把图像信息存到 adata.uns["spatial"] 这块复杂的嵌套 dict 里。
我曾经遇到过一个坑:path 参数要求指向包含 filtered_feature_bc_matrix.h5 和 spatial 子目录的根目录。如果你只给了 H5 的完整路径,反而会报错。另外,文件路径里不要有中文或空格,否则底层 h5py 库可能抛奇怪异常。
如果你拿到的是 10X 旧的 MEX 格式(三个文件:barcodes.tsv.gz、features.tsv.gz、matrix.mtx.gz),也可以用 sc.read_10x_mtx() 自己拼,但得手动补空间坐标。我建议在项目中统一用 H5 版本,它的元数据更完整。
2.3 数据基本探索
读取成功后,先看看里面有什么:
# 查看 obs 的前几行
print(adata.obs.head())
# 查看空间坐标
print(adata.obsm["spatial"][:5])
# 查看 var 的列名
print(adata.var.columns)
# 查看 umi 和 gene 的统计
sc.pp.calculate_qc_metrics(adata, percent_top=None, log1p=False, inplace=True)
print(adata.obs[["n_genes_by_counts", "total_counts"]].head())
观察几个关键指标:
- n_genes_by_counts:每个 spot 能检测到多少基因。一般 Visium 的捕获效率下,一个 spot 有 500~3000 个基因是正常的。
- total_counts:总 UMI 数,通常在几千到几万不等。过低意味着组织折叠或捕获失败,过高可能有组织重叠。
- tissue 列(如果存在):标识该 spot 是否在组织区域内。只有组织内的 spot 才应该用于后续分析。
空间坐标的存储格式:adata.obsm["spatial"] 是一个二维数组,第 0 列是 x(像素列),第 1 列是 y(像素行)。注意 y 轴方向与图像坐标一致,从左上角向下递增。当你要用 matplotlib 显示时,记得 flip 一下 y 轴才能和原图匹配。
最后,我们用一张图来总结整个数据读取与结构的关系,方便你快速回顾。