第四章:Visium数据格式——空间barcode、UMI与基因表达矩阵

好,咱们现在进入一个实操上非常关键的话题。你拿到的Visium原始数据,到底长什么样?

我记得第一次拿到10x Visium的数据时,对着那个文件夹愣了好一会儿。一堆`.bam`、`.fastq`、`.h5`,完全不知道从哪下手。后来才发现,这些东西的核心就三个概念:空间barcode、UMI、基因表达矩阵。

说白了,这就是Visium给每个“像素点”打上的唯一标识和计数规则。今天咱们就把它拆明白。

4.1 空间barcode:每个组织点的身份证

Visium的捕获芯片上,整齐排列着几千个捕获点。每个点都有一个独一无二的序列,这个序列就是空间barcode。

为什么需要它?

  • 定位:每个barcode对应芯片上一个固定的坐标(row, col)。
  • 溯源:测序后,只要读到这个序列,就知道它来自组织片的哪个位置。
  • 去重:防止同一个点被多次统计。

我建议你把它想象成超市里每个商品的条码。只不过,这里的“商品”是组织切片上55微米直径的一个小点。

关键参数:Visium v1芯片包含约5000个捕获点,v2版本提升到了约14000个。每个barcode长度为16碱基(v1)或27碱基(v2)。

在实际项目中,我曾经遇到一个坑:

有个样本的barcode质量分布图显示很多低质量点。排查后发现是组织切片贴歪了,导致边缘的捕获点只粘到部分细胞,barcode信号极弱。嗯,这里要注意——barcode的质量直接决定了你后续哪些点能用。

4.2 UMI:真正的分子计数器

聊完空间barcode,咱们说说UMI。全称是Unique Molecular Identifier,唯一分子标识符。

为什么需要它?

PCR扩增的过程中,同一个mRNA分子会被复制成很多份。如果没有UMI,你统计出来的“表达量”其实是PCR扩增后的产物数量,不是初始mRNA分子的数量。你想想看,这样会带来多大的偏差?

UMI就是解决这个问题的。它是一个随机的8-10碱基序列,在反转录前就连接到每个mRNA分子上。测序后,我们通过UMI去重,就能还原出初始的分子数。

举个例子:

测序得到的读段:
Read1: AAGCCTATGCC...  (空间barcode + UMI)
Read2: GCTGATCGTAC...  (cDNA序列)

如果两个读段有相同的barcode和UMI,只算一次。这就是“分子计数”的核心逻辑。

提示:我个人习惯用UMI计数而非读段计数来做下游分析。因为前者更能反映真实的基因表达水平。你可以在Space Ranger的输出表格里看到这两列。

4.3 基因表达矩阵:最终的数据产品

现在我们把所有东西串起来。经过Space Ranger的处理(barcode校正、UMI去重、基因比对),最终会产出一个基因表达矩阵。

The 矩阵长这样:

空间barcode 基因A 基因B 基因C ...
AAACCCAAGTTTACCA-1 12 0 5 ...
AAACCCAGTCCGAGTA-1 0 8 0 ...
AAACCCAGTCGCGCAA-1 3 2 7 ...
... ... ... ... ...

每一行是一个空间barcode(也就是一个捕获点),每一列是一个基因。单元格里的数字是UMI计数,代表该点在组织切片上检测到的该基因表达量。

这个矩阵是空间转录组分析的核心数据源。后续的聚类、差异表达、空间可视化,全都基于它。

4.4 数据流程与文件类型

为了让你更清楚整个过程,我画了一个流程图:

Visium数据生成与格式流程 组织切片 探针结合 + 空间barcode (16-27bp唯一ID) 文库构建 + UMI (8-10bp随机序列) 测序 Space Ranger处理 基因表达矩阵 核心输出文件 • filtered_feature_bc_matrix.h5 • filtered_feature_bc_matrix/ • analysis/ 聚类结果

从图中可以看到,最终你会得到两种主要格式的文件:

  • HDF5格式 (.h5):二进制的压缩文件,里面包含了表达矩阵、barcode列表、基因列表、以及空间坐标信息。适合用R或Python直接读取。
  • Market Exchange格式 (Matrix Market):包含三个文件——matrix.mtx(稀疏矩阵)、barcodes.tsv、features.tsv。文本格式,便于查看和调试。

注意:不要直接打开.mtx文件去数数字,几百兆甚至上G的文件会让你的文本编辑器直接崩掉。我推荐用R的Seurat或Python的Scanpy来加载。

4.5 实测:如何加载数据

理论讲完了,咱们看个实际例子。假设你的输出目录结构如下:

output/
├── filtered_feature_bc_matrix.h5
├── filtered_feature_bc_matrix/
│   ├── barcodes.tsv.gz
│   ├── features.tsv.gz
│   └── matrix.mtx.gz
├── analysis/
│   ├── clustering/
│   │   └── graphclust/
│   └── pca/
└── spatial/
    ├── tissue_lowres_image.png
    └── tissue_positions_list.csv

用R加载时我会这样做:

library(Seurat)
# 从h5文件读取
visium_data <- Load10X_Spatial(
    data.dir = "output/",
    filename = "filtered_feature_bc_matrix.h5"
)
# 查看表达矩阵的维度
dim(GetAssayData(visium_data, assay = "Spatial"))
# 前6个barcode的空间坐标
head(visium_data@images[[1]]@coordinates)

输出类似这样:

[1] 31053  4992
# 31053个基因,4992个捕获点

          tissue     row    col imagerow   imagecol
AAACCCAAGTTTACCA-1    0     28     80     24.7     31.2
AAACCCAGTCCGAGTA-1    0     32     85     28.1     34.8
AAACCCAGTCGCGCAA-1    0     35     91     30.4     37.5

看到没有?每个barcode都对应了它在组织片上的物理坐标(row, col),以及它在低分辨率图像上的像素坐标(imagerow, imagecol)。这些坐标就是后续画空间特征图的依据。

核心结论:空间barcode + UMI + 基因表达矩阵,三者合一才构成了Visium数据的完整格式。缺少任何一个,你都无法做出正确的空间聚类分析。

好了,Visium的数据格式就讲到这里。现在你可以清楚地知道,那些看似杂乱的文件夹里到底藏着什么。记住,barcode决定了“在哪”,UMI决定了“多少”,矩阵决定了“表达什么”。搞清楚这三者的关系,你就能自如地操控Visium数据了。