第3章:创建Seurat对象——从原始数据到可分析对象
拿到单细胞测序的原始数据后,第一件事就是把它们装进Seurat对象。说白了,就是把表达矩阵、细胞条形码、基因信息打包成一个结构体,方便后续操作。我刚开始用Seurat时总觉得这步就是走个过场,后来发现参数没调对,后面聚类结果全歪了……嗯,咱们还是认真对待。
3.1 数据长什么样?
Seurat对象本质上是一个容器。它至少需要三样东西:
- counts矩阵——基因×细胞,每个元素是UMI计数。通常是稀疏矩阵(dgCMatrix),别用普通稠密矩阵,否则内存直接爆炸。
- 细胞条形码——一行一个细胞ID,比如"AAACATACAACCAC-1"。
- 基因特征——一行一个基因名,比如"CD3D"。
最常见的来源是10X Genomics的cellranger输出。你用Read10X()读进来,返回的就是一个稀疏矩阵+两个向量。我遇到过目录结构不对导致读取失败的情况——注意filtered_feature_bc_matrix文件夹里必须包含matrix.mtx.gz、barcodes.tsv.gz、features.tsv.gz三个文件,文件名一个字母都不能错。
| 数据组件 | 文件 | 格式 |
|---|---|---|
| 表达矩阵 | matrix.mtx.gz | Market Exchange Format(稀疏) |
| 细胞条形码 | barcodes.tsv.gz | 每行一个细胞ID |
| 基因特征 | features.tsv.gz | 两列:基因ID、基因名 |
3.2 CreateSeuratObject核心参数
创建对象就用一个函数:CreateSeuratObject()。参数不多,但个个关键。我个人习惯把常用的几个参数列出来,避免遗漏。
library(Seurat)
# 读入10X数据
pbmc.data <- Read10X(data.dir = "filtered_gene_bc_matrices/hg19/")
# 创建Seurat对象
pbmc <- CreateSeuratObject(
counts = pbmc.data, # 表达矩阵(必须)
project = "pbmc3k", # 项目名称(建议给一个有意义的)
min.cells = 3, # 基因至少在3个细胞中表达才保留
min.features = 200, # 细胞至少表达200个基因才保留
assay = "RNA" # 默认assay名称(一般不改)
)
你看,关键参数就这五个。min.cells和min.features其实是做预过滤,帮你扔掉那些垃圾基因和空液滴。我建议永远不要省略它们——我见过一次项目里数据量极大,没设min.features结果保留了一堆只有几个UMI的细胞,后面归一化时直接报错。咱提前过滤掉,省心。
| 参数 | 作用 | 默认值 |
|---|---|---|
| counts | 表达矩阵(稀疏矩阵最佳) | 无 |
| project | 项目名称,用于后面合并对象时区分来源 | "SeuratProject" |
| min.cells | 基因至少在N个细胞中表达才保留 | 0 |
| min.features | 细胞至少表达N个基因才保留 | 0 |
| assay | 存储assay的槽位名称 | "RNA" |
assay = "RNA"或自定义名称。另外,project参数在后续MergeSeurat时非常重要,千万别用默认值,否则两个样本合并后你根本分不清哪个细胞来自哪个样本。
3.3 装填元数据
Seurat对象创建后会自动生成一些元数据:nCount_RNA(每个细胞的总UMI数)和nFeature_RNA(每个细胞表达的基因数)。但你肯定还想加自己的信息,比如样本分组、批号、细胞周期评分等。可以直接用AddMetaData()添加。
# 假设你有一个data.frame,第一列是细胞ID,第二列是分组
metadata <- data.frame(
cell_id = colnames(pbmc),
group = c(rep("control", 1000), rep("treatment", 1000)),
stringsAsFactors = FALSE
)
rownames(metadata) <- metadata$cell_id
pbmc <- AddMetaData(pbmc, metadata = metadata)
# 查看一下现在的元数据
head(pbmc[[]])
注意:添加元数据时,行名必须和Seurat对象中的细胞ID完全一致。我犯过低级错误——读取的细胞条形码末尾多了个"-1",合并时没对上,结果所有元数据都是NA。后来我强制用colnames()去对一遍,从此再没出过问题。
3.4 常见陷阱——创建对象的那些坑
Read10X_h5()返回的矩阵维度是基因×细胞,而CreateSeuratObject()也默认基因在行、细胞在列,这点没问题。但如果你自己构建表达矩阵(比如从scanpy导出),注意矩阵的方向——一定要基因在行、细胞在列,否则对象创建成功但后面归一化结果全是错的。建议创建后马上打印dim(pbmc)检查:行数应该是基因数,列数是细胞数。
min.cells = 3和min.features = 200是一个经验值,适合10X平台10k-5k细胞的数据。如果你的数据是smart-seq2(每个细胞基因数很多但细胞数少),可以把min.features调高到500以上,min.cells设成1。你想想看,参数不是死的,要根据实际数据调整。
3.5 创建对象后的快速检查
对象创建完别急着跑下游,先看一眼基本统计。我习惯用下面几行代码快速扫一眼:
# 查看对象基本信息
pbmc
# 看一下过滤后的细胞数和基因数
dim(pbmc)
# 查看元数据列(包括自动生成的QC指标)
head(pbmc[[]])
# 绘制UMI数与基因数的关系(快速判断空液滴)
VlnPlot(pbmc, features = c("nFeature_RNA", "nCount_RNA"), ncol = 2)
运行pbmc会输出类似这样的信息:
An object of class Seurat
13714 features across 2638 samples within 1 assay
Active assay: RNA (13714 features, 0 variable features)
1 layer present: counts
如果看到features数量和细胞数量符合预期,就可以放心继续了。如果数据量异常大或异常小,回查min.cells和min.features的设置。