第2章 原始数据下载与格式解析

空间转录组的第一道坎,就是把原始数据拿到手,并且搞懂每个文件到底在说什么。我当年第一次接触Visium数据时,对着文件夹里一堆.h5、.csv、.json和图片,愣是看了半天才理清关系。这一章我们就把这个过程拆开揉碎,你跟着走一遍,后面分析就顺了。

2.1 去哪里下载Visium数据

10x Genomics官方提供了不少示例数据集。我个人最常用的是正式公开的Visium Spatial Gene Expression数据集,比如Human Breast Cancer(FFPE)、Mouse Brain(Fresh Frozen)等。下载地址一般在10x的官方数据集页面(注意,这里只是为了说明来源,我们不在正文中放链接,只是提一下)。

另外,GEO(Gene Expression Omnibus)上也存了大量Visium数据。搜索“Visium”加上你想研究的组织,就能找到很多。

下载时注意区分两种输出:Space Ranger 输出(通常是一个tar.gz压缩包,解压后包含spatial文件夹和filtered_feature_bc_matrix.h5等)和原始FASTQ文件。我们这个教程主要用Space Ranger处理后的结果,也就是下游分析的标准输入。

我的小习惯: 下载后立刻在终端里用ls -lh看一眼文件大小。有些数据集动辄几个GB,先确认下载完整,免得后面解压报错。

解压命令很简单:

tar -xzvf Visium_Mouse_Brain.tar.gz
ls -R

2.2 标准文件夹结构长啥样

解压后你会看到类似这样的目录结构(以Space Ranger v2.0.0输出为例):

.
├── filtered_feature_bc_matrix.h5
├── raw_feature_bc_matrix.h5
├── filtered_feature_bc_matrix
│   ├── barcodes.tsv
│   ├── features.tsv
│   └── matrix.mtx
├── raw_feature_bc_matrix
│   ├── barcodes.tsv
│   ├── features.tsv
│   └── matrix.mtx
├── spatial
│   ├── tissue_hires_image.png
│   ├── tissue_lowres_image.png
│   ├── scalefactors_json.json
│   ├── tissue_positions_list.csv
│   └── aligned_tissue_image.jpg (可选)
└── analysis (可选,包含聚类等) 

很多人一开始会被两个.h5文件弄晕。我解释一下它们的区别:

  • filtered_feature_bc_matrix.h5 —— 只保留检测到的有效spots(通常是组织覆盖区域)。我们大部分分析都用这个。
  • raw_feature_bc_matrix.h5 —— 包含所有barcodes(包括背景区域),数据未经滤除。做质量控制时可能需要它来对比。
我曾经踩过的坑: 有一次我直接用raw矩阵做聚类,结果发现背景spots也掺了进来,导致组织边界完全模糊。所以除非你有特殊需求,否则默认用filtered的。

2.3 核心文件逐个解析

我们重点聊spatial文件夹里的内容,因为它们是空间信息的核心。

2.3.1 组织图片:tissue_hires_image.png / tissue_lowres_image.png

这两张图是染色后的组织切片扫描图。high-resolution (hires) 是完整分辨率,低分辨率 (lowres) 则是缩略图。算法在匹配spots坐标时,会用这些图做基准。

你可以直接用看图软件打开,但注意它们的大小——hires图可能高达20000×20000像素,加载慢。代码里我用matplotlib.image.imread读取后通常会先缩放。

2.3.2 尺度因子:scalefactors_json.json

这个JSON文件包含了将原始像素坐标转换为分析坐标的缩放参数。里边的字段如下:

字段名 含义 典型值(示例)
tissue_hires_scalef 高分辨率图缩放因子 (实际像素 / 原始像素) 0.1
tissue_lowres_scalef 低分辨率图缩放因子 0.025
spot_diameter_fullres 全分辨率下每个spot的直径(像素) 89.0
fiducial_diameter_fullres 全分辨率下fiducial点直径(像素) 79.0

举个例子:如果你的hots图实际宽度是20000像素,但tissue_hires_scalef=0.1,那么代码里用的时缩放到2000像素。我们需要这个值把spot坐标正确映射到图片上。

2.3.3 位置信息:tissue_positions_list.csv

这个文件是空间转录组里最“灵魂”的数据。每一行代表一个barcode(spot),记录了它在组织上的位置以及是否属于组织区域。列结构:

列名 说明
barcode spot的唯一标识符,例如 AAACCCAAGTATTGAG-1
in_tissue 0/1标记,1表示该spot在组织覆盖区域(有用),0代表背景
row spot在网格中的行坐标
col spot在网格中的列坐标
imagerow 对应组织图像上的像素行坐标(全分辨率下)
imagecol 对应组织图像上的像素列坐标(全分辨率下)

你可以用Python的pandas快速查看前几行:

import pandas as pd
pos = pd.read_csv('spatial/tissue_positions_list.csv', index_col=0)
print(pos.head())

输出会像这样:

             in_tissue  row  col   imagerow   imagecol
barcode                                             
AAACCCAAGTATTGAG-1         1  20  42  538.194  9103.821
AAACCCAACACTGGCT-1         1  50  23  1345.670  5307.450
...

2.3.4 表达矩阵:filtered_feature_bc_matrix.h5

这是标准的10x HDF5格式,可以通过scanpy.read_10x_h5直接读入。如果你喜欢用R,Seurat也有Read10X_h5函数。里面包含三个核心元素:barcodes、features(基因)和counts矩阵。

读入后检查一下基因数和细胞数:

import scanpy as sc
adata = sc.read_10x_h5('filtered_feature_bc_matrix.h5')
print(adata.shape)
# 输出示例: (2000, 34523) 表示2000个spots和34523个基因

注意:这里的“细胞”实际上是spots——每个spot包含多个细胞,但分析时通常当成一个“样本”。

2.4 数据结构的整体关系

为了让这些文件之间的关系更直观,我画了一张示意图。你看完就明白了:

Visium 原始数据组成与解析流程 10x / GEO 下载 Space Ranger 输出 tar -xzvf 解压 生成标准文件夹 spatial 文件夹 tissue_hires_image.png tissue_lowres_image.png scalefactors_json.json tissue_positions_list.csv 表达矩阵 filtered_feature_bc_matrix.h5 raw_feature_bc_matrix.h5 解析核心数据 表达矩阵 + 位置 + 图片 AnnData 解读小贴士 1. 位置文件和.h5 必须通过barcode匹配 2. scalefactors用于 将坐标转成像素位置 3. 推荐用scanpy读取

图2-1: Visium原始数据下载、解压、文件组成及核心解析流程

从图中你可以看到,核心任务就是把spatial文件夹里的位置信息、图像缩放因子和.h5表达矩阵整合到一起,得到一个AnnData对象(scanpy标准格式),后续所有分析都基于它。

2.5 实际代码整合示例

我来写一段最常用的读取流程,你照着做就能把数据读干净:

import scanpy as sc
import pandas as pd
import json

# 1. 读取表达矩阵
adata = sc.read_10x_h5('filtered_feature_bc_matrix.h5')

# 2. 读取位置信息
pos = pd.read_csv('spatial/tissue_positions_list.csv', index_col=0)

# 3. 仅保留组织上的spots
pos_in_tissue = pos[pos['in_tissue'] == 1]
# 注意:.h5里的barcodes可能比pos少,我们取交集
common_bc = adata.obs.index.intersection(pos_in_tissue.index)
adata = adata[common_bc]  # 过滤

# 4. 添加空间坐标
adata.obs['imagerow'] = pos_in_tissue.loc[common_bc, 'imagerow']
adata.obs['imagecol'] = pos_in_tissue.loc[common_bc, 'imagecol']

# 5. 读取缩放因子
with open('spatial/scalefactors_json.json') as f:
    sf = json.load(f)
print('Hires scale factor:', sf['tissue_hires_scalef'])

这样,你的adata就包含了表达矩阵和空间坐标,下一步就能画组织切图了。

避坑指南: 我曾经遇到barcode后缀不一致的问题。有些Space Ranger版本输出的barcode带“-1”,但位置文件里偶尔不带。读入后先检查一下adata.obs.index[:5]和pos.index[:5],如果发现不匹配,用pos.rename(index=lambda x: x + '-1')手动补上。

2.6 小结

这一章我们干了三件事:下载数据、搞懂文件夹里每个文件的用途、用代码把它们组装起来。你看,其实没有多玄乎,就是几个文件之间的对应关系。下结论前我多说一句——学会一口气把位置和表达矩阵对齐,是后续所有空间分析的基础。很多人卡在格式不匹配上,你用我上面那段代码,基本能一次性搞定。