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"。我在处理多模态数据时,就靠这列区分表达数据和蛋白数据。
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文件做快速预览,因为它不用解压三件套,加载速度快。
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)先看细胞数是否合理。
2.7 其他衍生格式
除了上面这些基础格式,你在实际项目中还会遇到:
- .loom:高效的大规模矩阵存储格式,常用于空间转录组
- .h5ad:Scanpy/AnnData的默认格式,内含X矩阵、obs、var等
- .rds:R对象序列化格式,保存整个Seurat对象,但跨平台兼容性差
- .mtx.gz:压缩后的MTX文件,解压后用法一样
总的来说,单细胞RNA-seq的数据格式核心就这三种文件加一个H5整合包。理解了它们,你就掌握了启动单细胞分析的第一步。后面在GPU加速聚类时,所有操作都围绕这些矩阵展开。