单细胞数据格式介绍:10X Genomics、h5、mtx 等
做单细胞分析,第一关往往不是代码,而是数据格式。
我记得刚入行时,拿到一个10X数据包,里面一堆文件,看得我一脸懵。后来发现,其实核心格式就两种——MEX(Market Exchange)格式和HDF5(.h5)格式。搞懂了它们,后面的流程就顺了。
10X Genomics 标准输出:MEX 格式
Cell Ranger 跑完后,默认会生成一个 outs/ 文件夹。你会在里面看到三个关键文件:
- barcodes.tsv.gz —— 每个液滴的条形码,一行一个
- features.tsv.gz —— 基因列表(旧版叫 genes.tsv),三列:基因ID、基因名、特征类型(比如 Gene Expression)
- matrix.mtx.gz —— 稀疏矩阵,Market Exchange 格式,三列:行索引、列索引、表达值
说白了,这是一个压缩后的稀疏矩阵分件存储。我自己在项目里见过不少人把这三个文件弄混,尤其是 features.tsv 和 genes.tsv 的命名差异——Cell Ranger v2 叫 genes,v3+ 改成了 features。
关键点:这三个文件必须配套使用。缺少任何一个,Seurat 的 Read10X() 都会报错。
来看看它们长什么样:
# barcodes.tsv.gz 示例(前几行)
AAACCCAAGAAACTCA-1
AAACCCAAGAAACTGG-1
AAACCCAAGAAACTTG-1
AAACCCAAGAAACCCA-1
# features.tsv.gz 示例
ENSG00000243485 MIR1302-11 Gene Expression
ENSG00000237613 FAM138A Gene Expression
ENSG00000186092 OR4F5 Gene Expression
# matrix.mtx.gz 示例(前几行,跳过注释头)
%%MatrixMarket matrix coordinate integer general
%
20495 5000 1234567
1 1 1
2 1 2
3 1 1
...
我的习惯:拿到新数据后,先解压一个 barcodes.tsv.gz 看看行数,再 wc -l matrix.mtx 算一下非零元素规模。这样能快速判断数据量级。
HDF5 格式(.h5)
H5 格式其实是一个容器,把所有信息打包在一个文件里。内部用 group 和 dataset 组织,类似文件夹+文件的结构。
Cell Ranger 生成的 filtered_feature_bc_matrix.h5 主要包含:
/matrixgroup —— 存储稀疏矩阵/matrix/barcodes—— 条形码列表/matrix/features—— 基因信息,包括 id、name、feature_type/matrix/data—— 非零表达值(稀疏矩阵的值部分)/matrix/indices—— 行索引/matrix/indptr—— 列指针(CSR 格式)/matrix/shape—— 矩阵维度
我用 R 的 h5ls() 看过内部结构,嗯,比想象中规整。实际读的时候不需要手动解析,Seurat 的 Read10X_h5() 全包了。
两种格式怎么选?
| 对比项 | MEX(三个文件) | HDF5(单个文件) |
|---|---|---|
| 文件数量 | 3 个(通常 gz 压缩) | 1 个 |
| 读取速度 | 较快(并行解压) | 略慢(需解析 HDF5) |
| 可读性 | 文本可预览 | 需要工具查看 |
| 存储空间 | 略大(稀疏矩阵文本) | 更紧凑(二进制) |
| 通用性 | 几乎所有工具支持 | 需 HDF5 库 |
我在实际项目中,绝大多数情况都用 MEX。原因很简单:万一读错了,可以打开文本文件看看;H5 出了问题很难调试。不过如果你数据特别大(比如百万级细胞),H5 的 IO 效率会更好一些。
注意:Cell Ranger 版本不同,H5 内部结构细节有差异。比如 v3 开始支持多模态(CITE-seq、ATAC 等),group 命名可能变化。读取时务必确认 Seurat 版本兼容。
格式选择与转换流程
上面这张图概括了数据流向。从 Cell Ranger 输出开始,你可以选 MEX 或 H5,最终都进入 Seurat。我个人建议:如果你刚开始学,先读 MEX,因为出问题了更容易排查。
读取代码实战
Seurat 提供了两个主要读取函数,用法很直接:
# 读取 MEX 格式(注意:指定目录,目录内要有三个 .gz 文件)
library(Seurat)
data_dir <- "path/to/filtered_feature_bc_matrix/"
counts <- Read10X(data.dir = data_dir)
# 读取 H5 格式
h5_file <- "path/to/filtered_feature_bc_matrix.h5"
counts_h5 <- Read10X_h5(filename = h5_file)
# 创建 Seurat 对象
seurat_obj <- CreateSeuratObject(counts = counts,
project = "my_project",
min.cells = 3,
min.features = 200)
有一点要提醒:Read10X() 默认会解压 gz 文件,速度还行。如果目录里同时有 gz 和未压缩的版本,它会优先读 gz。
我曾经踩过一个坑:H5 文件里基因 ID 用的是 ENSEMBL(ENSG...),而 MEX 的 features.tsv 可能同时有 ENSEMBL 和基因名。Read10X_h5() 默认只取 ENSEMBL 做 row name,后面做注释的时候很容易搞混。建议先 Read10X_h5(…, use.names = TRUE) 直接拿基因名。
其他格式简览
除了 10X 标准格式,你也可能遇到:
- CSV / TSV:手动整理的表达矩阵,行是基因,列是细胞。Seurat 可以直接用
ReadMtx()或CreateSeuratObject()读。 - AnnData (h5ad):Python 单细胞生态常用。R 里可以通过
SeuratDisk或zellkonverter转换。 - Loom:HDF5 的一种封装,支持更多元数据。可以用
loomR或SeuratDisk。
不过,对于标准 10X 项目,你只要玩熟 MEX 和 H5 就够用了。
避坑指南
- 文件路径不要有中文:Cell Ranger 输出的文件通常没问题,但假如你自己移动过文件夹,路径里出现中文或空格,
Read10X()有可能挂掉。 - 内存预估:一个 5000 细胞、2 万基因的 MEX 文件(压缩后约 30 MB),解压后约 500 MB,Seurat 对象构建时还会翻倍。我建议至少准备 8 GB RAM 起步。
- 检查过滤 vs 原始:Cell Ranger 产出两个 h5:
filtered_feature_bc_matrix.h5和raw_feature_bc_matrix.h5。前者只保留检测到的细胞,后者包含所有 barcodes(包括空液滴)。标准流程用 filtered。 - 版本对应:Cell Ranger v2 的输出没有 feature_type 列,Seurat 3.x 以后兼容但最好升级到 v3 以上。
数据读进来,只是第一步。接下来就要面对那堆稀疏矩阵里的零膨胀问题,以及质控……不过那是后面章节的事了。