4. 读取数据:CreateSeuratObject
好,到了真正动手的环节了。前面我们下载了数据,也简单看了一下文件长什么样。现在要做的,就是把这些零散的文本文件,变成一个R里的对象——Seurat对象。
说白了,CreateSeuratObject 就是 Seurat 流程的“起跑线”。它把表达矩阵包装起来,加上细胞信息、基因信息,然后丢到后面的标准化、降维、聚类流水线上。我记得自己第一次用的时候,直接就丢进去跑,结果后面各种报错,回头一看,原来是这里参数没调好。
4.1 输入文件长什么样?
先看看我们手里有什么。以10x Genomics的Cell Ranger输出为例,通常有三个关键文件:
| 文件 | 内容 | 典型文件名 |
|---|---|---|
| 特征(基因)表格 | 两列:基因ID(如ENSG...),基因名(如TP53) | features.tsv.gz |
| 条码表格 | 一列:细胞条码序列(如 AAACCCAAG...) | barcodes.tsv.gz |
| 矩阵市场文件 | 三列:行索引、列索引、UMI计数(稀疏格式) | matrix.mtx.gz |
这三种文件加起来,就是一个完整的表达矩阵。Seurat 内部会帮你把它们拼起来。你不需要自己手动合并。
4.2 最基础的调用
把这三样东西放在同一个目录下(比如 ./data/filtered_feature_bc_matrix/),然后用一行代码就能读进来:
library(Seurat)
pbmc.data <- Read10X(data.dir = "./data/filtered_feature_bc_matrix/")
pbmc <- CreateSeuratObject(counts = pbmc.data, project = "PBMC_3k")
这里 Read10X 会返回一个稀疏矩阵(dgCMatrix)。然后 CreateSeuratObject 接收这个矩阵,返回一个 Seurat 对象。
Read10X(data.dir = "...", gene.column = 2) 指定基因名称在哪一列。我之前遇到过一次基因名全成了ENSEMBL ID,后面注释都乱了,加上 gene.column = 2 就正常了。
4.3 关键参数详解
CreateSeuratObject 有很多参数,但大部分情况下你只需要关注这几个:
| 参数 | 默认值 | 作用 |
|---|---|---|
counts | 无(必需) | 表达矩阵,行为基因,列为细胞 |
project | "SeuratProject" | 项目名称,用于后续标记 |
assay | "RNA" | Assay名称,多组学时会用到 |
min.cells | 0 | 一个基因至少在多少个细胞中表达才保留 |
min.features | 0 | 一个细胞至少检测到多少个基因才保留 |
min.cells 和 min.features 是在构建对象时就做的初级过滤。我用的时候喜欢先设成0,等后面质控步骤再精确过滤。不过如果你数据特别大,也可以设 min.cells = 3 先去掉那些在极少细胞中表达的基因,省点内存。
4.4 对象里藏了什么?
创建完成后,用 pbmc 直接看一眼:
> pbmc
An object of class Seurat
13714 features across 2700 samples within 1 assay
Active assay: RNA (13714 features, 0 variable features)
嗯,2700个细胞,13714个基因。注意这里“features”是基因数,不是降维后的特征。你还可以用 pbmc[["RNA"]]@counts 访问原始计数矩阵,不过一般是操作对象的方法,而不是直接取slot。
$ 操作 @counts 改数据。我见过有人直接往稀疏矩阵里写值,结果破坏了稀疏结构,后面计算直接崩。如果要做数据修改,请用 SetAssayData 或新建一个 assay。
4.5 处理其他来源的数据
不是所有数据都来自10x。比如Smart-seq2的数据,一般是每个细胞一个文件,或者一个大的表达矩阵(可能是稠密矩阵)。这时候你不能用 Read10X,得手动读进来,然后传给 CreateSeuratObject。
# 假设你已经读好了表达矩阵(行为基因,列为细胞)
mat <- read.table("expression_matrix.tsv", header = TRUE, row.names = 1, sep = "\t")
mat <- as.matrix(mat) # 转换成稠密矩阵
obj <- CreateSeuratObject(counts = mat, project = "SmartSeqData")
Seurat 会自动把你的稠密矩阵转成稀疏格式。如果矩阵太大(比如>10万细胞),建议先转稀疏矩阵再传入,省内存。
4.6 元数据太重要了
创建对象时,Seurat 会自动生成几个默认的元数据(metadata):
orig.ident:就是你传入的project名称nCount_RNA:每个细胞的UMI总数nFeature_RNA:每个细胞检测到的基因数
后面做质控就是靠这两列。我建议创建后立刻看看分布:
summary(pbmc$nCount_RNA)
summary(pbmc$nFeature_RNA)
如果有细胞 UMI 总数只有几十,那基本就是空液滴或者死细胞,后面要滤掉的。
4.7 一张图让你看懂创建流程
下面这张图概括了从原始文件到Seurat对象的完整过程。我画得比较简洁,重点就是“三个文件 -> 稀疏矩阵 -> Seurat对象 + 元数据”。
你看,从三个文件到最终Seurat对象,中间只经过了两次函数调用。但背后的数据转换和元数据初始化,都是自动完成的。
4.8 常见问题与我的对策
- 内存不够? 如果数据超过几十万细胞,建议用
Read10X时设progress = TRUE看着进度条。还可以考虑先用min.cells过滤掉低表达基因。我曾经处理一个300G的10x数据集,就是靠这个参数把内存占用降到了80G以内。 - 读进去是空的? 检查一下目录路径,尤其是有没有拼错
filtered_feature_bc_matrix还是raw_feature_bc_matrix。raw的是带空液滴的,filtered是Cell Ranger已经过滤过的。 - 基因名重复了? 如果数据里有多个转录本共享同一个基因名,Seurat会报错。这时候最好去原始数据里检查。如果不想改原文件,可以用
Read10X的strip.suffix = TRUE或者make.unique处理。
CreateSeuratObject 不是万能的。如果你的数据是10x Multiome(同时有RNA和ATAC),需要分别读取,然后用 CreateSeuratObject 建RNA和ATAC两个assay,再合并成多模态对象。这个我们后面会专门讲。