空间转录组数据获取与预处理:常见平台、数据格式、质量控制

空间转录组这两年有多火,不用我多说。但很多人拿到数据就急着跑模型,结果发现结果一团糟。原因在哪?数据预处理没做好。说白了,空间转录组的数据比单细胞数据多了一个空间坐标维度,质量控制也要跟着变。今天我就把数据获取、格式转换和质控这一套流程帮你理清楚。

一、常见平台:你选哪个?

市面上主流的空间转录组平台大概有五六种。每个平台的数据结构、分辨率、通量都不一样。我个人经验是:选平台之前先想清楚你要回答什么生物学问题。

平台分辨率通量RNA捕获方式特点
10x Visium~55 μm 点中等(~5000 点/切片)poly-A 捕获最成熟,分析生态好
Slide-seq V2~10 μm 珠子高(~100k 珠子)随机引物分辨率高,但文库复杂
MERFISH亚细胞级中等(~10000 基因)原位杂交 + 成像单分子分辨率,需定制探针
STARmap亚细胞级低(~1000 基因)原位测序无需解离,适合神经科学
Seq-scope~1 μm 点极低随机引物超高点阵,适合精细结构

威宁:其实选平台还要看你的预算和样本类型。比如组织切片质量不好,MERFISH的背景荧光会比较刺眼,我踩过这个坑。

二、数据格式:别被后缀名吓住

拿到数据的第一步,先搞清楚文件从哪来、后缀名啥意思。10x Visium 输出的一组文件最常见:filtered_feature_bc_matrix.h5、spatial/tissue_hires_image.png、spatial/tissue_positions_list.csv。这里有个小陷阱:h5版本和mtx版本在内存使用上有区别,我建议你直接用h5,省空间。

核心数据文件类型

  • h5 (HDF5):存储表达矩阵+特征+条形码,单文件方便
  • mtx (Market Matrix):三个文件 (matrix.mtx, barcodes.tsv, features.tsv) 古老但兼容
  • CSV/TSV:文本格式,可读但体积大
  • JSON/GeoJSON:空间坐标和元数据,Slide-seq常用
  • TIFF/OME-TIFF:组织染色图或荧光图像

我曾经遇到一个团队,给了我一堆CSV文件每个超过2GB,光读取就崩了。后来发现他们用了一个非标准封装。建议你先用h5ls查看h5结构,确认key名。

下面是一个简单的读取示例,我一般用scanpy来加载Visium数据:

import scanpy as sc

adata = sc.read_visium(
    path="path_to_data_directory",
    count_file="filtered_feature_bc_matrix.h5",
    load_images=True
)
print(adata)
# AnnData object with n_obs × n_vars = 4433 × 34553
#     obs: 'in_tissue', 'array_row', 'array_col'
#     var: 'gene_ids', 'feature_types', 'genome'
#     obsm: 'spatial'

对于Slide-seq的数据,我习惯先用pip install slidseq(注意不是官方包,是社区工具),然后读取重格式。嗯,这里需要留个心眼:每个平台的坐标系统不一样,Visium是array_row/array_col + 像素坐标,Slide-seq是beads_coords,混着用会搞混空间关系。

三、质量控制:别让垃圾数据毁掉模型

空间转录组的QC和单细胞有相似之处,但多了空间层面的检查。我个人习惯把质控分成三个步骤:分子水平、点/珠子水平、空间一致性。千万别漏掉空间一致性检查,很多异常点虽然分子数正常,但空间分布明显与组织边界不齐。

3.1 常用指标

指标含义筛选范围
n_genes_by_counts每个点检测到的基因数通常 >200, <5000
total_counts每个点的UMI总数通常 >1000, <= 数据95%分位
pct_counts_mt线粒体基因占比一般 <20%
doublet_score双胞可能性(非必需)依赖方法,scrublet可做
spot_density相邻点表达相关性低密度点需排除
注意:不要直接套用单细胞QC的过滤阈值。空间转录组中组织边缘的点基因数本身偏低,但可能恰好是结构边界。我建议你做“分区域过滤”——先通过组织染色图像划分组织区域,再在该区域内做QC。

3.2 实战:10x Visium 过滤流程

我通常这样操作:

import scanpy as sc
import matplotlib.pyplot as plt

# 1. 计算QC指标
sc.pp.calculate_qc_metrics(adata, expr_type='counts', 
                            qc_vars=['mt'], 
                            percent_top=None,
                            log1p=False)

# 2. 定义过滤
adata.obs['mt_frac'] = adata.obs['pct_counts_mt']
adata = adata[adata.obs['mt_frac'] < 20, :]
adata = adata[adata.obs['n_genes_by_counts'] > 200, :]
adata = adata[adata.obs['total_counts'] > 1000, :]

# 3. 空间一致性检查:计算局部莫兰指数
import squidpy as sq
sq.gr.spatial_autocorr(adata, 
                        genes=['Hpca', 'Tbr1'], 
                        n_perms=100)
# 低显著性区域(p值>0.05)可能有问题

遇到低表达区域,不要急着删,先看看组织染色图。我有一次发现某个区域基因数很低,但所有点都在同一血管周围,最后确认是血管周围存在大量RNA降解。这时候应该保留但不能参与后续建模。

3.3 平台特有的“雷区”

  • Visium:注意in_tissue列,只有标签为1的点是有效组织区域。
  • Slide-seq:空珠子容易误判为有信号,建议用总UMI阈值 + 背景模型剔除。
  • MERFISH:背景荧光导致假阳编码,需要用spot quality筛选。
  • STARmap:成像漂移导致坐标偏移,必须做配准。
小技巧:不管你用哪个平台,建议输出一张QC violin plot + spatial scatter plot多模态图。一个维度看统计分布,另一个维度看空间分布,两个同时“有问题”的spot才应该排除。

四、知识体系总览:一张图看懂

下面这张流程图总结了从数据获取到预处理、质控的完整链路。我工作时都会贴在墙上当备忘录。

空间转录组数据获取与预处理流程 平台选择 数据获取 格式识别与转换(h5/mtx/csv/geoJSON) 质量控制(三步) 分子QC 空间QC 一致性QC 清洁 AnnData 平台差异在“格式识别”和“QC阈值”中体现

这张图里我把质量控制拆成三个互相独立但需要交叉验证的步骤。分子QC关注点的测序深度、线粒体占比;空间QC看点的空间邻居关系;一致性QC则跨模态对比组织轮廓。我的经验是:永远先跑一次全流程,再根据第一次的QC统计量调整阈值,至少迭代两轮。

五、常见坑及避坑指南

  • 坑一:直接过滤低基因数点——组织边缘点基因数低但包含重要边界信息。
    避坑:结合HE染色图像做视觉验证。
  • 坑二:忽略批次效应——同一个实验不同切片之间也有批次效应,需要在QC阶段做初步校正。
    避坑:运行sc.pp.regress_out()去除切片ID变量。
  • 坑三:5万个点全保留——计算代价高,且大量无信号点会稀释空间信号。
    避坑:先去除in_tissue=0或背景珠子,再降采样到合理数量。

我曾经接手一个项目,10x Visium数据里有30%的点在组织外,但是因为当初没有标记in_tissue,结果聚类时这些点自成一群。折腾了两周才发现。所以拿到数据第一件事就是看obsm['spatial']里每个点的坐标是否落在组织轮廓内。

建议预处理清单:
  1. 确认平台和版本(10x Visium v1 vs v2 差异大)
  2. 读取所有文件并生成汇总报告
  3. 运行标准QC指标(n_genes, total_counts, mt%)
  4. 手动检查前100个异常点空间分布
  5. 保存两个版本:原始备份 & 过滤后备份

嗯,基本上预处理到这个程度就够喂给下游模型了。后续模型搭建阶段,你还会遇到批次校正、数据标准化这些事,但那是后话。数据干净了,模型至少赢了一半。