2、单细胞转录组数据简介:数据矩阵、表达量、细胞与基因的基本概念
说到单细胞转录组,你可能会想:“这跟普通转录组有啥区别?”其实说白了,普通转录组测的是组织里一堆细胞的平均表达,而单细胞转录组能把每个细胞单独揪出来看。我刚开始接触这个领域时,最大的震撼就是——原来同一个组织里的细胞,基因表达可以差这么多。
2.1 表达矩阵:一张表装下整个实验
单细胞转录组数据的核心是一张二维矩阵。行是基因,列是细胞,每个格子里的数值就是该基因在那个细胞里的表达量。你可以把它想象成一张Excel表——嗯,实际上很多工具也确实把它存成CSV或HDF5格式。
我个人习惯把表达矩阵称为“数字宇宙”,每一列是一个独立的细胞,每一行是一条基因在全部细胞里的转录轨迹。来看看它长什么样:
# 用pandas加载一个小型表达矩阵
import pandas as pd
# 假设我们有一个10个基因、5个细胞的矩阵
matrix = pd.DataFrame({
'Cell1': [12, 0, 34, 0, 0, 5, 0, 0, 0, 1],
'Cell2': [0, 0, 29, 0, 0, 0, 0, 0, 2, 0],
'Cell3': [8, 0, 0, 0, 0, 1, 0, 0, 0, 0],
'Cell4': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
'Cell5': [15, 0, 40, 0, 1, 8, 0, 0, 0, 0]
}, index=['GeneA', 'GeneB', 'GeneC', 'GeneD', 'GeneE',
'GeneF', 'GeneG', 'GeneH', 'GeneI', 'GeneJ'])
print(matrix)
你看,矩阵里大部分是0。这在单细胞数据里太正常了——每个细胞只表达几百到几千个基因,大部分基因都不活跃。我记得第一次处理10x Genomics数据时,看到矩阵里99%都是零,还以为数据出错了……后来才知道这叫“稀疏矩阵”,是单细胞数据的常态。
2.2 表达量的含义:从UMI到归一化
表达量不是直接拿测序仪吐出来的原始counts就用的。你想想看,不同细胞捕获的mRNA数量不一样,测序深度也不一样,必须做标准化才能比较。
最常见的表达量单位是UMI(Unique Molecular Identifier)计数。每个mRNA分子都带一个独特的条形码,测序后统计有多少种不同的条形码,就是该基因的UMI count。UMI能有效消除PCR扩增偏差。我曾经犯过一个错误:直接拿原始counts做聚类,结果细胞分组全被测序深度带偏了……嗯,从那以后我每次都会检查UMI分布。
| 表达量类型 | 含义 | 典型范围 |
|---|---|---|
| Raw counts | 直接比对到基因的读段数 | 0 ~ 几千 |
| UMI counts | 绕过PCR偏差的独特分子计数 | 0 ~ 几百(10x数据) |
| CPM / TPM | 每百万 reads 的表达量 | 0 ~ 几十 |
| Log-normalized | log(CPM+1) 或 log(TPM+1) | 0 ~ 10 |
2.3 细胞与基因:两个维度的故事
矩阵的行和列分别代表什么?
- 细胞(列):每个细胞是一个独立的“个体”,有自己的转录状态。细胞之间可能属于不同细胞类型(T细胞、B细胞、上皮细胞等),也可能处于不同分化阶段。
- 基因(行):每个基因是一个特征(feature)。单细胞数据里基因数通常超过两万个,但真正有表达信息的往往只有几千个。
在实际分析中,我们会先过滤掉表达量过低的基因(比如在少于3个细胞中表达的基因),也会过滤掉总UMI太少的细胞。这些操作都是为了让矩阵更“干净”。
关键指标:
- 每个细胞检测到的基因数(n_genes)
- 每个细胞的总UMI数(n_counts)
- 线粒体基因占比(percent_mito)—— 过高表示细胞破裂
2.4 核心知识结构:表达矩阵的骨架
下面这张图展示了单细胞转录组数据的基本骨架。你可以看到数据如何从测序仪经过几个步骤变成可分析的矩阵:
2.5 实战:用Python快速探索矩阵
光看概念不过瘾,我们动手加载一个真实的表达矩阵玩玩。我这里用Scanpy自带的PBMC数据集做演示:
import scanpy as sc
# 加载内置数据集
adata = sc.datasets.pbmc3k_processed()
# 查看表达矩阵形状
print(adata.X.shape) # (2638, 1838) → 2638个细胞,1838个基因
# 查看前5个细胞的前5个基因表达
print(adata[:5, :5].X.toarray())
输出类似:
(2638, 1838)
[[0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0.]]
全是0?别慌!这是因为Scanpy默认用了稀疏存储,而且我选的前5个基因可能恰好都不在这几个细胞里表达。实际上只要换几个基因就能看到非零值:
# 查看CD3D和CD14的表达
print(adata[:, 'CD3D'].X.toarray()[:10]) # 前10个细胞在CD3D上的表达
print(adata[:, 'CD14'].X.toarray()[:10])
sc.pp.normalize_total和sc.pp.log1p就是干这个的。
2.6 细胞与基因的元数据
表达矩阵本身只是数值,真正理解数据还得靠元数据:
- 细胞元数据(obs):每个细胞的相关信息,比如细胞类型标签、实验批次、患者ID、测序深度等。
- 基因元数据(var):每个基因的信息,比如基因名、染色体位置、是否是线粒体基因等。
在Scanpy的AnnData对象里,这些信息都放在adata.obs和adata.var数据框里。我习惯做完QC后,先把n_genes_by_counts和total_counts画个直方图,快速了解数据质量。
一句话总结:单细胞转录组数据的核心就是一张稀疏的计数矩阵,搭配细胞和基因的元数据。理解了这张矩阵,后面所有分析(降维、聚类、差异表达)才有了落脚点。