单细胞数据格式介绍: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 主要包含:

  • /matrix group —— 存储稀疏矩阵
  • /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 (3个文件) HDF5 (.h5) Seurat 对象构建 Read10X / Read10X_h5 注意基因ID格式: ENSEMBL vs Symbol

上面这张图概括了数据流向。从 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 以上。

数据读进来,只是第一步。接下来就要面对那堆稀疏矩阵里的零膨胀问题,以及质控……不过那是后面章节的事了。