3. 10x与Drop-seq数据格式差异
做单细胞整合,第一步就是看懂数据。我见过不少同学,整合代码写得飞起,结果预处理就把样本目录搞混了。说白了,不同平台的输出差异很大,你得先搞清楚它们各自长什么样,才能下手处理。
这一章,我们就来拆解Cell Ranger和Drop-seq tools的输出格式。我保证,看完你就能自己从零开始整理两个平台的数据了。
3.1 两套工具的“世界观”对比
先看个总览表格。你想想看,这两个工具虽然都是为了从测序数据里提取表达矩阵,但设计思路有不少区别。
| 对比维度 | 10x Genomics (Cell Ranger) | Drop-seq (Drop-seq tools) |
|---|---|---|
| 核心输出目录 | outs/ 文件夹 |
当前工作目录,一堆散文件 |
| 表达矩阵格式 | Market Exchange (mtx) + 两个TSV | Digital Expression Matrix (DEM) 一个文件 |
| 过滤与否 | 提供 filtered_feature_bc_matrix 和 raw_feature_bc_matrix 两份 |
直接输出所有barcodes,需要自己过滤 |
| 特征定义 | features.tsv.gz (多列, 含类型、基因组) |
genes.tsv (两列: ID和Name) |
| Barcode命名 | 完整序列 (如 AAACCCAAGAAACTCA-1) |
数字索引 (如 0, 1, 2) |
| 是否提供H5 | 有 (filtered_feature_bc_matrix.h5) |
无,需自行转换 |
对,你没看错。Cell Ranger的输出组织得像个正经软件项目,而Drop-seq tools的输出更像一组科研脚本跑完的结果。这两种设计各有道理,但对于我们做整合的人来说,关键是理解它们的数据结构。
3.2 “三兄弟”文件深度解读
不论是10x还是Drop-seq,最核心的表达矩阵都由三个文件组成。只是名字和格式略有差异。我把它们叫做“三兄弟”:
- barcodes.tsv:记录了每个微滴(bead)的条码序列。
- genes.tsv 或 features.tsv:记录基因ID和名字。
- matrix.mtx:矩阵市场格式,记录每个barcode-基因对的UMI计数。
3.2.1 barcodes.tsv — 谁是谁的标签
我个人习惯,拿到数据第一时间就检查这个文件的行数。它会告诉你:这份数据一共识别出了多少个barcodes。
# --- 10x Cell Ranger 输出示例 ---
# barcodes.tsv (压缩为 barcodes.tsv.gz)
AAACCCAAGAAACTCA-1
AAACCCAAGAAACTCC-1
AAACCCAAGAAACTCG-1
...
# --- Drop-seq tools 输出示例 ---
# barcodes.tsv (注意:输出的是数字索引)
0
1
2
...
千万注意:Drop-seq tools输出的barcodes.tsv是数字索引,不是真实序列。我刚开始处理Drop-seq数据时,还以为程序出错了。后来才明白,这是为了节省存储空间。整合时,你需要把这个索引映射回真正的barcode序列。
3.2.2 genes.tsv vs features.tsv — 基因清单
这是两个平台最直观的差异之一。
Drop-seq tools的输出很简洁:
# 取自 genes.tsv
ENSG00000121410 A1BG
ENSG00000148516 A1BG-AS1
ENSG00000175899 A2M
...
两列:Ensembl ID + 基因名。
Cell Ranger v3+的features.tsv.gz就复杂多了:
# 取自 features.tsv.gz (解压后)
ENSG00000121410 A1BG Gene Expression
ENSG00000148516 A1BG-AS1 Gene Expression
ENSG00000242268 AC007325.2 Gene Expression
...
多了一列“类型”。这列让Cell Ranger可以同时存储基因表达、抗体捕获、CRISPR扰动等多种assay的数据。嗯,这里要注意,如果你在做多模态分析,这个设计就很重要。
3.2.3 matrix.mtx — 数字矩阵的“稀疏之美”
单细胞矩阵极其稀疏,mtx格式就是为了高效存储这种数据而生的。
文件结构长这样:
%%MatrixMarket matrix coordinate integer general
%
12345 6789 1234567
23 45 1
23 67 3
45 12 2
...
我来给你拆解一下:
- 第一行:Magic number,标识这是一个Matrix Market格式文件。
- 第三行:行数(基因数) 列数(barcodes数) 非零元素个数(UMI>0的条目数)。
- 后续行:每一行代表一个非零元素:
行号 列号 计数值。
一个关键知识点:行号和列号都是从1开始计数的。我曾经用Python的0-based索引直接读mtx,结果整整错位了一下午。记住,行号1对应的是genes.tsv里的第一个基因,列号1对应的是barcodes.tsv里的第一个barcode。
3.3 容器升级:H5与MTX的抉择
除了“三兄弟”,10x还提供了HDF5文件(.h5)。这是个“集装箱”,把三个文件打包到一起了。
| 特征 | MTX + TSV | HDF5 (H5) |
|---|---|---|
| 结构 | 三个独立文本文件 | 一个二进制文件,内部分组 |
| 可读性 | 高,可直接head查看 |
低,需专用工具(h5py等) |
| 读写速度 | 较慢(特别是大矩阵) | 快,支持部分读取 |
| 磁盘占用 | 较大(文本格式有冗余) | 略小(二进制压缩) |
| 适用场景 | 手动检查、小项目 | 大规模分析、频繁I/O |
我的建议:整合前,先用Python或R把H5转成标准的mtx+tsv格式,然后统一处理。这样你能每步都检查文件是否正确。我在项目中遇到过H5文件内部group名称不一致的情况,直接用H5读很容易翻车。
3.4 知识图谱:一张图说清差异
下面这张SVG图总结了本章核心。它展示了10x和Drop-seq两个平台从原始数据到可整合表达矩阵的路径差异。你可以把它当作快速参考卡。
3.5 实战:在Python里读取这两个格式
光说不练假把式。我们写点代码来实际读取它们。
读取10x的mtx文件:
import scanpy as sc
# 用ScanPy一行搞定
adata = sc.read_10x_mtx(
"path/to/filtered_feature_bc_matrix/",
var_names='gene_symbols', # 用基因名作为变量名
cache=True
)
print(adata)
# 输出:AnnData object with n_obs × n_vars = 5000 × 30000
读取Drop-seq的mtx文件(注意索引转换):
import pandas as pd
from scipy.io import mmread
import anndata as ad
# 步骤1:读取三个文件
barcodes = pd.read_csv("barcodes.tsv", header=None, names=["barcode_idx"])
genes = pd.read_csv("genes.tsv", header=None, names=["gene_id", "gene_name"])
mat = mmread("matrix.mtx")
# 步骤2:构建AnnData
adata = ad.AnnData(
X=mat.T.tocsc(), # mtx默认是 gene x cell,转置为 cell x gene
obs=pd.DataFrame(index=barcodes["barcode_idx"].astype(str)),
var=pd.DataFrame(index=genes["gene_name"].values)
)
# 步骤3:barcode重命名(示例)
# 假设你有一个映射文件 mapping.csv
# mapping = pd.read_csv("mapping.csv")
# adata.obs_names = ...
print(adata)
避坑指南:我曾经在整合一批Drop-seq数据时,发现取出的数值全部是零。排查了半天,才发现是mmread返回的矩阵是COO格式,默认是基因×细胞,而AnnData需要的是细胞×基因。记住加个.T。
嗯,看到这里,你应该已经能区分10x和Drop-seq的数据了。下一环节,我们会手把手把这些数据整合在一起。但在此之前,建议你亲自跑一下上面的代码,看看你手里的真实数据长什么样。打通这第一步,后面的整合才有意义。”