第4章 数据读取:scanpy.read_visium 与 scanpy.read_h5ad 详解
数据读取,是整个分析流程的起点。你拿到10x Visium的空间转录组数据,可能是从官方Space Ranger输出的文件夹,也可能是别人已经处理好的h5ad文件。怎么把它们正确读进来?这里面坑不少。
我个人最早做空间转录组时,就栽在路径和文件格式上。读进去的数据要么是空的,要么坐标全乱掉。后来才明白,scanpy.read_visium 和 scanpy.read_h5ad 这两个函数,用好了能省大量时间,用错了就是灾难。
这一章,咱们把这两个函数拆开揉碎,从参数、行为到避坑,一次讲清楚。
4.1 scanpy.read_visium:从Space Ranger输出目录加载空间数据
你手头如果是10x Visium原始的测序数据,通常会先跑Space Ranger流程,输出一个包含以下文件的目录:
filtered_feature_bc_matrix.h5(或filtered_feature_bc_matrix文件夹)spatial/tissue_positions_list.csvspatial/scale_factors.jsonspatial/tissue_hires_image.png(高分辨率组织图像)spatial/tissue_lowres_image.pngspatial/detected_tissue_image.jpg
read_visium 就是专门为这种结构设计的。它一次性把基因表达矩阵、空间坐标、组织图像都读进来,打包成一个 AnnData 对象。
4.1.1 基本用法
import scanpy as sc
adata = sc.read_visium(
"path/to/SpaceRanger/output",
genefile="filtered_feature_bc_matrix.h5",
library_id="sample1",
source_image_path=None,
load_images=True
)
解释一下关键参数:
| 参数 | 说明 | 我的习惯 |
|---|---|---|
path |
Space Ranger输出目录的路径 | 建议用绝对路径,省得找半天 |
genefile |
h5文件路径,可以是“filtered_feature_bc_matrix.h5”或“raw_feature_bc_matrix.h5” | 我一般用 filtered,除非你想看空白背景的全局表达 |
library_id |
样本ID,用于标识该样本的切片 | 同一个项目里用统一命名,比如 donor1_region1 |
source_image_path |
手动指定高分辨率图像路径,不指定会自动找 | 很少用,除非你手动重命名了图像文件 |
load_images |
是否加载图像数据到AnnData | 默认True。如果只做表达分析,可以False加快加载 |
返回值是一个 AnnData 对象。obsm 里包含了 spatial 坐标,uns 里存了图像和缩放因子。
4.1.2 关键避坑点
我曾经遇到过:Space Ranger输出的 spatial 目录下,如果没有 tissue_lowres_image.png,read_visium 会报错,但又不告诉你缺哪个文件。后来我检查文件清单,才发现是手动删除图片后没更新。
所以,如果你的数据读不进去,先确认 spatial 文件夹里都有什么文件。
如果你拿到的是 filtered_feature_bc_matrix 文件夹(三个文件:matrix.mtx, barcodes.tsv, features.tsv),read_visium 也支持!只需把 genefile 参数设置成文件夹路径,它会自动识别。
4.2 scanpy.read_h5ad:通用h5ad格式读取
h5ad是Scanpy原生的数据持久化格式。你可以把之前保存的 AnnData 对象直接读回来,比如:
adata = sc.read_h5ad("sample1.h5ad")
简单到离谱,对吧?但这里也有几个细节需要注意。
4.2.1 参数一览
| 参数 | 作用 | 说人话版 |
|---|---|---|
filename |
文件路径 | 唯一必填项 |
backed |
是否以“后援”模式打开(只读,不加载全部到内存) | 超大文件时用,内存省一半 |
cache |
是否使用缓存加速 | 我一般True,第二次打开飞快 |
gcs_url |
Google Cloud Storage路径(很少用) | 跳过 |
关键区别:read_visium 是“组装”函数,它从多个文件里提取信息再合并;read_h5ad 是“反序列化”函数,直接恢复之前保存的整个对象。
4.2.2 版本兼容性问题
你问我哪个问题最常见?h5ad版本不兼容!比如你用Scanpy 1.8保存的h5ad,用Scanpy 1.9可能读不了。为什么会这样?因为h5ad底层用的是HDF5,而anndata的schema会随着版本更新变化。
解决方案:
- 保存时指定一个稳定的版本:
adata.write("file.h5ad", compression="gzip") - 更新Scanpy后,如果读旧文件报错,试试
anndata.read_h5ad("file.h5ad")而不是scanpy.read_h5ad - 实在不行,用
h5py手动解包然后重新写成h5ad(这个我踩过两次坑)
我曾经在合作项目中,对方用Scanpy 1.9.0,我用1.8.2,他给我的h5ad我死活读不了。最后我用 anndata.read_h5ad("file.h5ad", backed="r") 才勉强读出来。
所以,团队协作时,最好统一anndata和scanpy版本。
4.3 两者的典型使用场景比较
你想想看,什么时候该用哪个?
| 场景 | 推荐函数 | 原因 |
|---|---|---|
| 10x Visium原始Space Ranger输出 | read_visium |
自动解析文件夹结构,加载图像和坐标 |
| 别人处理好的AnnData | read_h5ad |
直接恢复全部结果,包括归一化、降维等 |
| 自己保存的中间结果 | read_h5ad |
速度快,占用空间小(压缩) |
| 想自己构造空间坐标矩阵 | 都不合适 | 需要用 sc.pp.read_visium 底层函数或手动构造 |
4.4 数据读取的常见流程
下面这张图,把从原始文件到 AnnData 对象的路径画了出来。我建议你把它保存下来,以后遇到数据读取问题时,看一眼就明白走哪条路。
4.5 实战:用read_visium加载一份真实的10x Visium数据
假设你下载了10x官网的“Human Breast Cancer”示例数据。解压后目录结构大概是这样:
Breast_Cancer_FFPE/
├── filtered_feature_bc_matrix.h5
├── spatial/
│ ├── tissue_positions_list.csv
│ ├── scale_factors.json
│ ├── tissue_hires_image.png
│ ├── tissue_lowres_image.png
│ └── detected_tissue_image.jpg
└── raw_feature_bc_matrix.h5 (可选)
在Python中这样加载:
import scanpy as sc
adata = sc.read_visium("Breast_Cancer_FFPE/",
genefile="filtered_feature_bc_matrix.h5",
library_id="breast_cancer1")
print(adata)
# AnnData object with n_obs × n_vars = 3657 × 19135
# obs: 'in_tissue', 'array_row', 'array_col'
# uns: 'spatial'
# obsm: 'spatial'
看到 obsm['spatial'] 和 uns['spatial'] 了吗?这就是空间坐标和图像信息。我建议你加载完后,先打印一下这两个属性确认坐标值是否合理。
个人习惯:加载完成后立刻执行 sc.pl.spatial(adata, img_key="hires") 画一张图,看看成像和点阵有没有对齐。如果不对齐,很可能 scale_factors.json 里的缩放因子写错了,或者图像文件名不对。
4.6 数据读取后的快速检查清单
- 检查
adata.X的形状:行数一般就是spot数,列数是基因数。常见Visium V1有~4000~5000个spot,V2有~5000~6000。 - 检查
adata.obsm['spatial']的列名:通常是["pixel_x", "pixel_y"],在组织图像上的像素坐标。不是阵列坐标(array_row,array_col在adata.obs里)。 - 检查
adata.uns['spatial'][library_id]['scalefactors']里是否有tissue_hires_scalef等字段。 - 如果使用
read_h5ad,打印adata时注意是否有obsm和uns,确保之前保存时没有丢失关键信息。
最后,说一个我踩过的大坑:如果 read_visium 报错 "FileNotFoundError: No such file or directory: 'tissue_lowres_image.png'",别慌。看看 spatial 文件夹里是不是只有 tissue_hires_image.png 而没有低分辨率图?10x官方有些示例数据只包含高分辨率图。解决办法:在 read_visium 里加上 source_image_path="spatial/tissue_hires_image.png" 强制指定高分辨率图。
嗯,读取这一步搞定,后面的分析才能站得住。记住:数据读不对,后面所有分析都是白费功夫。多用 print() 和 sc.pl.spatial() 来验证,别信自己的直觉。