2. 单细胞RNA-seq数据格式

做单细胞分析,第一件事就是搞明白数据长什么样。我自己刚入行时,就被各种文件后缀搞得头晕——.mtx、.h5、.rds、.h5ad……这章我们就彻底理清它们。

说白了,单细胞RNA-seq的标准输出格式,大部分来自10x Genomics平台。无论你用Cell Ranger、Seurat还是Scanpy,最终都会落到三种核心文件上。我个人习惯在做预处理前,先手动看一眼这些文件的结构,心里有底再动手。

2.1 三种标准文本文件

10x Genomics的Cell Ranger pipeline会输出三个文件,它们一起构成一个完整的表达矩阵。我把它们称为“三件套”:

文件 扩展名 核心内容 对应行列
Barcode表格 barcodes.tsv 每个细胞唯一条形码序列 列(样本/细胞)
Features表格 features.tsv 基因ID、基因名称、特征类型 行(基因/特征)
Matrix文件 matrix.mtx 稀疏坐标格式的计数数据 行×列(基因×细胞)

你想想看,这三个文件缺一个,数据就废了。我记得有一次处理公共数据,下载时漏了features.tsv,结果死活读不进去,花了一整天找原因。所以建议做数据下载时,检查三件套是否齐全。

2.2 Barcode和Features的细节

先说barcodes.tsv。这个文件只有一列,内容是类似AAACCCAAGAGAAGAG-1的序列。每个条目代表一个细胞。为什么会这样?因为微流控芯片把每个细胞和带有独特序列的凝胶珠封装在一起,测序后根据这条序列恢复细胞身份。

再看features.tsv。它通常有三列,用制表符分隔:

ENSG00000243485  MIR1302-2HG      Gene Expression
ENSG00000237613  FAM138A          Gene Expression
ENSG00000186092  OR4F5            Gene Expression

第一列是ENSEMBL基因ID,第二列是基因符号,第三列是特征类型。默认只有"Gene Expression",但如果你做了抗体捕获(CITE-seq)或CRISPR扰动,第三列会出现"Antibody Capture"或"CRISPR Guide Capture"。我在处理多模态数据时,就靠这列区分表达数据和蛋白数据。

避坑: features.tsv 中的基因名可能包含重复项或空项。我曾经遇到一个公共数据集,有几个基因的符号是空字符串,导致后续差异分析报错。建议读入数据后,立即检查features列是否有NA或重复。

2.3 Matrix文件详解

matrix.mtx是核心文件,采用Market Exchange格式。它存储的是稀疏矩阵——因为大部分基因在单个细胞里不表达,所以用稀疏格式能省大量空间。

比如这个片段:

%%MatrixMarket matrix coordinate integer general
%
33538 5000 1234567
1 1 3
1 2 1
2 1 2
2 3 5

第一行是MTX格式声明。第四行的三个数字分别代表:基因数 33538、细胞数 5000、非零条目数 1234567。再往下,每行表示一个非零表达值。以1 1 3为例,意思是第1个基因在第1个细胞中有3个UMI计数。

这就引出了UMI的概念。UMI是Unique Molecular Identifier,是测序前加的随机短序列,用于消除PCR带来的重复偏差。所以matrix里的值都是UMI计数,不是原始reads数。我个人做QC时,会先看UMI总数的分布,如果某个细胞的UMI数异常低(比如少于500),基本就是死细胞或空液滴。

2.4 H5文件:更紧凑的打包格式

2018年后,10x Genomics推出了H5格式,把三件套打包成一个文件。它的优点很明显:一个文件搞定,分发方便。H5文件内部包含多个group,结构类似文件夹:

/matrix
  /barcodes       -> 细胞条形码数组
  /data           -> 非零计数数组
  /features
    /_all_tag_keys
    /feature_type -> 特征类型数组
    /id           -> 基因ID数组
    /name         -> 基因名称数组
    /genome       -> 基因组版本
  /indices        -> 行索引数组
  /indptr         -> 列指针数组
  /shape          -> [基因数, 细胞数]

这里用的是Compressed Sparse Column格式存储。如果你用Python,推荐用h5py或Scanpy直接读取。我个人喜欢在项目初期用H5文件做快速预览,因为它不用解压三件套,加载速度快。

小窍门: 有些公共数据库同时提供MTX和H5两种格式。如果你的网络带宽有限,优先下载H5;如果要做跨平台分析(比如在R和Python之间切换),建议保留三件套格式,兼容性更好。

2.5 Seurat对象的内部结构

当你用Read10X或Read10X_h5读取数据后,Seurat会在内存中构建一个统一的Seurat对象。这个对象包含:

  • assays:存储表达矩阵,默认命名为"RNA"
  • meta.data:存储细胞级别的元数据,如nCount_RNA(总UMI数)、nFeature_RNA(检出基因数)
  • reductions:降维结果,如PCA和UMAP
  • graphs:细胞-细胞关系图

一个典型的Seurat对象,在内存里大概长这样:

An object of class Seurat 
5000 features across 2000 samples within 1 assay 
Active assay: RNA (5000 features, 0 variable features)
 2 layers present: counts, data

我建议新手先理解counts和data的区别。counts是原始整数计数,data是归一化后(如LogNormalize)的值。后面做聚类时,默认用的是data层中的缩放残差。如果你错误地用了raw counts做PCA……嗯,结果会很奇怪。

2.6 预过滤MTX文件

实际项目中,我们经常会用预过滤后的MTX文件。比如cellranger的filtered_feature_bc_matrix目录,就是已经去掉了空液滴和疑似双联体的数据。这和raw_feature_bc_matrix的区别很大:

对比指标 原始MTX 预过滤MTX
基因数 所有检测到的基因 与原始相同
细胞数 所有液滴(含空液滴) 仅真实细胞
数据量 大(包含大量零值细胞) 小(约原始1/10~1/5)
典型计数 总UMI可高达10万+ 总UMI在1000~50000之间
使用场景 质控流程、调试 标准分析流程

我记得有一次分析一个免疫组数据集,用了原始MTX忘记过滤,结果聚类出了几十个“细胞群”,一看全是空液滴的噪声。从那以后,我在任何预处理脚本开头都会写一行检查:dim(x)先看细胞数是否合理。

单细胞RNA-seq数据格式核心结构 单细胞RNA-seq数据格式核心结构 数据来源 10x / BD / Parse 格式类型 三件套 / H5 / MEX 预处理阶段 过滤 / 质控 / 归一化 barcodes.tsv 细胞标识符(液滴级别) features.tsv 基因ID / 符号 / 类型 matrix.mtx 稀疏坐标格式计数 Seurat对象 assays(表达矩阵) meta.data reductions graphs 💡 格式选择建议: H5文件适合快速加载和单平台分析,三件套适合跨平台和自定义预处理 ⚠️ 常见陷阱: features.tsv的基因符号可能重复或缺失,matrix的数据值必须是整数UMI计数

2.7 其他衍生格式

除了上面这些基础格式,你在实际项目中还会遇到:

  • .loom:高效的大规模矩阵存储格式,常用于空间转录组
  • .h5ad:Scanpy/AnnData的默认格式,内含X矩阵、obs、var等
  • .rds:R对象序列化格式,保存整个Seurat对象,但跨平台兼容性差
  • .mtx.gz:压缩后的MTX文件,解压后用法一样

总的来说,单细胞RNA-seq的数据格式核心就这三种文件加一个H5整合包。理解了它们,你就掌握了启动单细胞分析的第一步。后面在GPU加速聚类时,所有操作都围绕这些矩阵展开。