4. 创建 Seurat 对象与初步探索

数据读进来了,下一步就是把它变成一个 Seurat 对象。这一步看着简单,但里面有几个坑。我记得刚学单细胞时,直接把原始矩阵塞进 CreateSeuratObject,结果对象很大、过滤参数也没设,后面跑了一大堆没用的小基因。所以,这一节我把从读取到初步探索的完整流程拆开讲,每一步我都说说我的经验和避坑方法。

4.1 读取单细胞数据

最常见的是 10X Genomics 的 Cell Ranger 输出,一般是三个文件:matrix.mtx、barcodes.tsv、features.tsv(或 genes.tsv)。我用 Read10X 一步搞定:

library(Seurat)
counts <- Read10X(data.dir = "data/filtered_feature_bc_matrix/")

如果数据是 H5 格式,可以用 Read10X_h5;CSV 或 TSV 就直接用 read.table 读成矩阵,再手动构建。我个人习惯先用 Read10X,因为它自动把基因名和条形码对齐,出错少。注意,这里要确认你的数据是 filtered_feature_bc_matrix 而不是 raw,前者已经去掉了空液滴,后面质控就省一些事。

小提示: 有时候你拿到的数据文件夹里只有三个文件,没有子目录,那你把 data.dir 直接指向这个文件夹即可。如果报错找不到文件,先检查文件名是不是带后缀(比如 matrix.mtx.gz),Read10X 会自动解压 .gz,不用你手动解。

4.2 创建 Seurat 对象

拿到表达矩阵后,创建对象很简单:

pbmc <- CreateSeuratObject(counts = counts,
                           project = "PBMC",
                           min.cells = 3,
                           min.features = 200)

这里有两个关键参数:min.cells 和 min.features。它们做什么的?

参数含义我的建议
min.cells只在至少 N 个细胞中检测到的基因才保留通常 3 个细胞,太大会丢掉一些稀有基因
min.features细胞中至少要有 N 个基因表达才保留200 是个常见起步值,低质量空细胞或碎片会被过滤掉

我刚开始做项目时,min.cells 用过 10,结果一些重要的低表达标记基因(比如某些 transcription factor)直接被扔了,后面聚类怎么都找不对。后来我老老实实用 3,基本不会误杀。

注意: 这两个参数只是粗过滤,真正的质控在后面用 QC 指标来做。这里不要过滤太狠,保持大部分细胞,后面再根据指标精细化剔除。

4.3 探索对象结构

创建完对象,先看看它长什么样:

pbmc
# 输出示例:
# An object of class Seurat 
# 13714 features across 2700 samples within 1 assay 
# Active assay: RNA (13714 features, 0 variable features)
#  2 layers present: counts, data

还可以用这些命令看看内部结构:

dim(pbmc)           # 基因数 × 细胞数
colnames(pbmc)      # 细胞条形码
rownames(pbmc)      # 基因名
head(pbmc@meta.data) # 元数据,包括 nCount_RNA 和 nFeature_RNA
pbmc[["RNA"]]$counts[1:5, 1:3]  # 原始计数矩阵切片

这里 nCount_RNA 是每个细胞的总 UMI 数,nFeature_RNA 是检测到的基因数。这两个指标是后续质控的基础,我建议你一拿到对象就先看看它们的分布。

4.4 添加线粒体基因比例

线粒体污染是单细胞数据里最常见的问题。细胞破裂后,线粒体 RNA 会残留在空液滴里,导致某些“细胞”的线粒体比例异常高。计算它:

pbmc[["percent.mt"]] <- PercentageFeatureSet(pbmc, pattern = "^MT-")

这个函数会扫描基因名以 ^MT- 开头的基因(人类数据),求和后除以总 UMI。你也可以用 ^mt- 对小鼠,或者用特定基因列表。我遇到过一些数据,线粒体基因名是 MT-ND1、MT-CO1 这样的,用这个 pattern 刚好囊括。

关键点: 线粒体比例 > 20% 的细胞通常质量很差,后面要过滤掉。但不同组织类型阈值可以调整,比如脑组织可以放宽到 25%。

4.5 初步可视化探索

数据整理好了,用图看一眼整体质量。我平时必做两个图:小提琴图和散点图。

小提琴图:

VlnPlot(pbmc, features = c("nFeature_RNA", "nCount_RNA", "percent.mt"),
        ncol = 3)

这张图能快速看出三个指标的分布。如果看到 nFeature_RNA 特别低的尾巴(比如 < 200),说明可能存在空液滴;percent.mt 如果有个别细胞飙到 50%、80%,那肯定有问题。

散点图:

FeatureScatter(pbmc, feature1 = "nCount_RNA", feature2 = "nFeature_RNA")

正常细胞应该呈正相关,因为 UMI 数高通常伴随更多基因被检测到。如果有些细胞 UMI 很高但基因数很少,那可能是技术噪音,比如扩增偏好导致少量基因占了大部分 UMI。反过来,基因数多但 UMI 低也有可能,但少见。

我记得有一次做肿瘤样本,散点图上出现一大团 UMI 极低、基因数却很“正常”的点,后来发现那是红细胞污染——红细胞没细胞核,但残留的 RNA 碎片会影响计数。这个图能提前预警。

4.6 知识体系总览

为了让你对整个流程有整体感,我画了一张框架图,从原始数据到初步探索一目了然:

创建Seurat对象与初步探索流程 创建 Seurat 对象与初步探索流程 原始数据 10X/H5/CSV/TSV 读取 Read10X / 其他函数 创建对象 CreateSeuratObject 结构探索 dim / head / str 添加QC指标 percent.mt / 其他 初步可视化 VlnPlot / FeatureScatter 过滤基数 min.cells=3 min.features=200

这张图把今天讲的步骤串起来了,后面几章我们会基于这个对象继续往下走。

4.7 避坑指南(我的经验)

  • 检查基因名重复: 有些数据集的 features.tsv 里基因名可能有重复(比如同义名),Seurat 会报错。我建议先用 table(duplicated(rownames(counts))) 检查,有重复就手动去重或用 make.unique。
  • 内存问题: 如果你的细胞数超过 10 万,创建对象时别设 min.cells 太小,否则对象会非常大。我一般先 Read10X 后再用 CreateSeuratObject,并调高 min.cells 到 5-10 来节省内存。
  • 线粒体基因的前缀: 小鼠是 ^mt-,斑马鱼是 ^mt-,但有些物种不是标准命名,得自己查。曾经有个客户用非模式生物,我建议先 grep 出线粒体基因名再手动传列表。
  • 不要迷信默认参数: min.features=200 对 10X 数据还行,但如果你的数据是 Smart-seq2(几百个细胞、每个细胞几千基因),这个值可以设为 500 或更高。根据实验来调。

好,到这里你应该能自己创建 Seurat 对象,并看懂初步的质量指标了。这一章内容扎实,把基础打牢,后面做标准化和降维时就不会手忙脚乱。