做单细胞分析这几年,最常被问的问题就是:“我拿到了10X Genomics下机的FASTQ文件,然后呢?”说实话,这个问题背后藏着一整套流程——从原始测序数据到最终能在文章里放出来的UMAP图,中间隔着Cell Ranger比对定量、Seurat降维聚类、可视化调参等一堆环节,每一步都能让人卡上几天。
这篇博文就按照我自己跑通的实际路径来写:从拿到FASTQ开始,一路走到UMAP可视化,把中间每一步为什么要这么做、参数怎么定、踩过哪些坑,都尽量讲清楚。适合刚接触10X Genomics单细胞数据分析、手里有数据但不知道怎么下手的初学者,也适合已经跑过流程但想回头看看某些环节原理的同行。
1. 整体流程设计与核心思路拆解
1.1 从FASTQ到UMAP,到底经历了什么
一张10X Genomics的文库,下机之后你拿到的是FASTQ文件。FASTQ只是把每个读段(read)的碱基序列和质量值堆在一起,本身毫无生物学意义。你要做的,是把这些序列“放回”到基因组上,搞清楚每个读段来自哪个细胞、哪个基因,然后才能进入真正的数据分析环节。
整个流程可以拆成两大段:
- 上游处理:用Cell Ranger把FASTQ比对到参考基因组,得到基因表达矩阵。这一步解决的是“每个细胞里有哪些基因被检测到了、表达量是多少”。
- 下游分析:用Seurat或其他工具读入表达矩阵,经过质控、标准化、降维、聚类,最终画出UMAP图。这一步解决的是“样本里有哪些细胞类型、每种类型的分子特征是什么”。
我见过的初学者最容易犯的错误,就是直接跳过上游的理解,拿到表达矩阵就开始跑Seurat。这会导致一个很尴尬的局面:下游分析出了问题,你根本不知道是上游比对参数不合适,还是下游阈值没调对。所以我把两部分都写进来,哪怕你用的是公共数据集,也建议把上游流程的产出逻辑搞清楚。
1.2 为什么选择Cell Ranger + Seurat这套组合
目前10X Genomics官方数据最标准的处理工具是Cell Ranger,这一点没什么争议。它是10X自家开发的,针对Chromium平台的文库结构做了深度优化,比对效率和准确性都是最优的。虽然也有STARsolo、alevin等替代方案,但在做10X数据时,Cell Ranger依然是最稳的起点,尤其是它产出的filtered_feature_bc_matrix文件夹,可以直接被Seurat、Scanpy等下游工具读取,生态非常完善。
下游分析我选Seurat而不是Scanpy,主要理由是:
- Seurat的文档和社区教程非常丰富,遇到问题基本都能搜到答案。
- R语言的数据框操作对生信背景的人来说更顺手,可视化体系(ggplot2生态)也更成熟。
- Seurat的
FindAllMarkers、SCTransform等函数在单细胞分析里的表现经过了大量验证,结果更容易被审稿人接受。
当然,如果你更熟悉Python,Scanpy也完全够用,分析思路是相通的。我这里以Seurat为例,但讲到的逻辑同样适用于Scanpy。
提示:Cell Ranger输出的表达矩阵是一切下游分析的基础。建议永远保留
filtered_feature_bc_matrix原始输出,不要在上面直接做修改,所有的过滤操作都在Seurat里以“子集化”的方式完成,这样可复现性最强。
2. 上游准备:Cell Ranger环境搭建与比对定量
2.1 硬件与软件准备
Cell Ranger对计算资源的要求不低。以10X标准文库(大约5000-10000个细胞)为例,比对参考基因组(human GRCh38)时,建议至少分配8核CPU、64GB内存。如果细胞数多(比如10万+),内存需求会直线上升,128GB都不宽裕。
我自己的建议是:
- 尽量在Linux服务器上跑,macOS也能装,但性能受限。
- 确认服务器磁盘空间充足,原始FASTQ + Cell Ranger产出,单个样本往往要占100GB以上。
- 用
nohup或screen把任务挂后台,避免ssh断开导致任务中断。
安装Cell Ranger只需要从10X官网下载压缩包解压即可,没有复杂的依赖。但注意,它依赖tar版本,新版Cell Ranger要求GNU tar,不要在精简版容器环境里直接跑,否则会报莫名其妙的错误。
2.2 mkfastq要不要跑
如果你的FASTQ文件是直接从测序平台拿到的,通常有两种情况:
- 测序公司已经根据10X的文库index把FASTQ拆分好了,你拿到的就是一个样本一个文件夹,里面是
*_R1_001.fastq.gz和*_R2_001.fastq.gz。 - 你拿到的是整个lane的原始BCL文件,或者是一个包含多个样本混合数据的FASTQ,需要自己拆分。
第一种情况,直接跳过mkfastq,进入count。第二种情况,需要用cellranger mkfastq根据样本index拆分。
实际项目中,我遇到第二种情况的比例其实不低。有些测序公司图省事,不帮客户拆分样本,直接把混样数据给你。这时候mkfastq的正确用法是:
cellranger mkfastq --run /path/to/bcl_dir \ --csv sample_sheet.csv \ --output-dir /path/to/fastq_outputsample_sheet.csv里需要包含Lane、Sample、Index三列。这个文件格式很严格,少一列都会报错。我的经验是,先看一眼RunInfo.xml里实际用的index序列,确认和sample_sheet.csv里的Index列一致,再开跑。否则样本拆分出来全是空的,或者不同样本互相污染,排查起来非常痛苦。
2.3 count命令与关键参数详解
进入核心的比对定量环节,命令如下:
cellranger count --id=Sample1 \ --transcriptome=/path/to/refdata-gex-GRCh38-2020-A \ --fastqs=/path/to/fastq_dir \ --sample=Sample1 \ --expect-cells=8000 \ --localcores=16 \ --localmem=128几个参数需要特别说明:
--id:输出文件夹名字,建议用样本名命名,方便后续管理。--transcriptome:参考基因组目录。10X官网可以下载预构建的refdata-gex-GRCh38-2020-A(人类),物种不对会直接报错。--sample:必须和FASTQ文件名里的样本名匹配,Cell Ranger会自动识别。--expect-cells:预期的细胞数。这个参数影响测序饱和度的估算和过滤阈值的自动判断,不要随意填。如果你不确定,可以先跑一个小的cellranger count试运行,从web_summary.html里看实际捕获的细胞数,再用更准确的expect-cells重跑一遍。--localcores和--localmem:控制资源占用,设成服务器能承受的上限即可。
跑完后重点关注两个文件:
web_summary.html:整体质控报告,包含测序饱和度、Q30碱基比例、细胞数、中位基因数等关键指标。我习惯先看这个,确认质量没问题再做下游。filtered_feature_bc_matrix文件夹:里面是过滤后的表达矩阵,包含barcodes.tsv.gz、features.tsv.gz、matrix.mtx.gz三个文件,就是Seurat读入用的数据。
2.4 web_summary.html结果怎么判断
这个环节很多初学者容易忽略,直接拿表达矩阵就去跑下游了。但实际上,上游质量直接决定下游分析能不能做。我一般重点看这几个指标:
- Estimated Number of Cells:和
expect-cells是否接近。差太远说明捕获效率有问题。 - Mean Reads per Cell:每个细胞平均读数。10X官方建议5000以上,太低的话基因检出率不高。
- Median Genes per Cell:每个细胞检测到的中位基因数,人类PBMC一般在1200-2000之间,如果低于500,要警惕数据质量。
- Fraction Reads in Cells:有效细胞中读数占比,通常应大于70%。这个比例低说明背景RNA污染严重。
- Q30 Bases in Barcode:barcode和UMI的测序质量,建议大于65%。
只要这几个指标在合理范围内,就可以放心进入下游分析。如果某一项明显异常,建议先排查上游,别急着往下走。
注意:Cell Ranger跑出来的
raw_feature_bc_matrix是未经过滤的完整矩阵,filtered_feature_bc_matrix是根据Cell Ranger自动判定的细胞barcode过滤后的矩阵。Seurat分析应使用filtered版本,否则会带入大量空液滴(empty droplets)的噪声。
3. Seurat分析起步:数据读入与对象构建
3.1 R环境准备与Seurat安装
Seurat基于R语言,建议使用R 4.x版本。安装Seurat本身不复杂,但依赖包比较多,国内网络环境下经常会出现安装超时的问题。解决方法是配置镜像源:
options(repos = c(CRAN = "https://mirrors.tuna.tsinghua.edu.cn/CRAN/")) install.packages("Seurat")如果安装过程中报错缺少某个依赖包,直接用install.packages()补装即可。Bioconductor系列的包用BiocManager::install()安装。建议同时安装tidyverse、patchwork,后面数据处理和画图会非常方便。
3.2 读取Cell Ranger数据:Read10X与CreateSeuratObject
Seurat读取10X数据有专门的函数:
library(Seurat) library(dplyr) library(patchwork) data_dir <- "path/to/filtered_feature_bc_matrix" data <- Read10X(data_dir) obj <- CreateSeuratObject(counts = data, project = "Sample1", min.cells = 3, min.features = 200)Read10X会自动识别文件夹里的三个文件,返回一个稀疏矩阵。CreateSeuratObject是构建Seurat对象的入口,这里有两个参数值得细讲:
min.cells = 3:一个基因至少在3个细胞里有表达才会被保留。这个过滤能去掉那些在极少数细胞中零星检测到的基因,减少噪声。min.features = 200:一个细胞至少检测到200个基因才会被保留。这个过滤能去掉那些基因检出数过低的barcode——大概率是空液滴或者破碎细胞。
用默认值没问题吗?大多数场景下没问题,但如果你处理的是极端情况(比如超低质量样本),建议先不设过滤,把数据读进来看看分布再决定。实际操作中,我是先读取不过滤的完整对象,画一下nFeature_RNA的分布图,再决定阈值。
3.3 手工构建矩阵的替代方案
有时候你的数据来源不是标准的Cell Ranger产出,比如从GEO下载的公共数据,是一个现成的表达矩阵文件。这种情况下就不能用Read10X了,需要手工读入:
expr_mat <- read.table("expression_matrix.tsv", header = TRUE, row.names = 1, sep = "\t", as.is = TRUE) expr_mat <- as(as.matrix(expr_mat), "dgCMatrix") # 转为稀疏矩阵 obj <- CreateSeuratObject(counts = expr_mat)需要注意的是,从公共数据库下载的数据,基因名格式可能五花八门(有的是Ensembl ID,有的是基因symbol,有的带版本号)。建议统一用Seurat::RenameGenesSeurat或者HGNChelper包把基因名转换为标准symbol,否则后面找marker基因时会非常痛苦。
4. 质控过滤:决定分析质量的关键一步
4.1 为什么线粒体基因比例是重要的质控指标
拿到Seurat对象后,第一件事就是计算每个细胞的线粒体基因比例。原理很简单:细胞凋亡或破碎时,胞浆内的mRNA会降解流失,但线粒体基因因为线粒体结构相对稳定,会相对富集。所以线粒体比例高,往往意味着这个细胞状态不健康。
在Seurat中计算线粒体比例的标准做法是:
obj[["percent.mt"]] <- PercentageFeatureSet(obj, pattern = "^MT-")人的线粒体基因以MT-开头,小鼠则是mt-。如果你分析的是其他物种,比如斑马鱼或果蝇,需要先用grep确认线粒体基因的命名规则,这一行代码里的正则表达式要对应调整。
4.2 nFeature、nCount和percent.mt的联合过滤
画三个指标的小提琴图和散点图,看看分布情况:
VlnPlot(obj, features = c("nFeature_RNA", "nCount_RNA", "percent.mt"), ncol = 3) plot1 <- FeatureScatter(obj, feature1 = "nCount_RNA", feature2 = "percent.mt") plot2 <- FeatureScatter(obj, feature1 = "nCount_RNA", feature2 = "nFeature_RNA") plot1 + plot2过滤阈值没有统一标准,必须结合数据分布来判断。我通常的起步阈值是:
nFeature_RNA > 200 & nFeature_RNA < 6000:过滤掉双细胞和空液滴。上限6000是根据经验定的,超过这个值的很可能是两个细胞被同一个barcode捕获了。percent.mt < 20:过滤掉破碎细胞。不同组织阈值差异很大,血液样本可以卡5%,肿瘤组织或者保存条件不好的样本,20%已经是比较宽松的标准了。
实际执行:
obj <- subset(obj, subset = nFeature_RNA > 200 & nFeature_RNA < 6000 & percent.mt < 20)这里特别提醒:阈值一定要结合自己的数据分布来定。我见过有人用固定阈值直接把好数据过滤得七七八八,也见过阈值太松导致聚类结果里大量死细胞团块。灵活一点,以分布图的拐点为准。
4.3 双细胞过滤要不要做
双细胞(doublet)是指同一个液滴里包了两个细胞,这在10X建库中不可避免,比例通常在0.4%-0.8%/千个细胞左右。如果不处理,双细胞会形成独立的“伪cluster”,干扰后续细胞类型注释。
常用的双细胞预测工具有DoubletFinder、scDblFinder等。以DoubletFinder为例:
library(DoubletFinder) ## 先做标准预处理 obj <- NormalizeData(obj) obj <- FindVariableFeatures(obj) obj <- ScaleData(obj) obj <- RunPCA(obj) ## 预测双细胞 nExp <- round(ncol(obj) * 0.04) # 假设双细胞率为4% obj <- doubletFinder_v3(obj, pN = 0.25, pK = 0.09, nExp = nExp, PCs = 1:20)双细胞过滤要不要做,取决于你后续分析的精度要求。如果只是做初步探索,可以不做;如果要做精细的细胞亚群分析,建议做。我自己一般会做,因为双细胞对聚类的影响往往比想象中大。
5. 标准化、高变基因筛选与PCA降维
5.1 为什么不能直接用原始表达量比较
单细胞的测序深度差异很大,有的细胞测到5万条UMI,有的只有5000条。如果直接用原始counts做比较,测序深度高的细胞会在所有基因上都“显得”表达量更高,而这完全不是生物学差异,而是技术噪声。
Seurat的标准化逻辑是:先算出每个细胞的总UMI,把每个基因的counts除以总UMI,再乘以一个缩放因子(默认10000),然后做log1p变换。这一套下来,测序深度的影响就被消除了,不同细胞之间可以公平比较。
obj <- NormalizeData(obj, normalization.method = "LogNormalize", scale.factor = 10000)5.2 SCTransform和LogNormalize怎么选
Seurat里其实有两套标准化方案:经典流程的LogNormalize和更新一些的SCTransform。前者是每批处理一个细胞,后者会用正则化负二项回归模型,把测序深度的影响更彻底地回归掉,同时还能顺便识别高变基因。
我个人的经验是:
- 常规分析用
LogNormalize就够了,稳定、速度快、资料也多。 - 如果数据存在明显的技术差异(比如不同批次合并),或者细胞类型之间的测序深度差异特别大,用
SCTransform效果更好。
SCTransform的调用方式:
obj <- SCTransform(obj, vars.to.regress = "percent.mt")注意:用SCTransform后,obj的内部结构会变化,后续的FindVariableFeatures和ScaleData都不需要再跑了,它的输出已经包含了标准化后的数据和特征选择结果。
5.3 高变基因筛选的意义
表达矩阵里有两万多个基因,但并非所有基因都对区分细胞类型有贡献。大部分基因在所有细胞里表达水平差不多,属于“背景基因”。如果让它们参与聚类,反而会淹没真正的差异信号。
FindVariableFeatures会计算每个基因在不同细胞间的离散程度,挑出变异最大的前2000个基因(默认),作为后续PCA输入。
obj <- FindVariableFeatures(obj, selection.method = "vst", nfeatures = 2000)这里不推荐把nfeatures调到很高(比如5000以上),因为计算时间会增加,但聚类效果并不会显著提升。2000是经过大量验证的平衡点。
5.4 PCA:把高维数据压缩到几十个维度
写到这里,很多新手会困惑:既然已经筛选出2000个高变基因了,为什么不直接用这些基因做聚类,还要跑PCA?
原因是这2000个基因之间高度相关——很多基因的表达模式是同步的。PCA的作用就是把这种相关性提取出来,用少数几个“主成分”来代表整体的变异模式。
obj <- ScaleData(obj, vars.to.regress = "percent.mt") obj <- RunPCA(obj, npcs = 50, verbose = FALSE)npcs = 50表示计算前50个主成分。实际上不需要全部用上,前面十几个PC通常已经捕获了绝大部分生物学差异。接下来的问题是:用多少个PC做后续分析。
最常用的方法是看ElbowPlot:
ElbowPlot(obj, ndims = 50)图中PC贡献方差的比例会有一个明显的“拐点”,拐点之后的PC贡献变得非常平缓,通常意味着它们主要是噪声。我一般选拐点前1-2个PC的区间,比如拐点在15,就选dims = 1:15。
如果要更严格一点,可以用JackStraw做显著性检验:
obj <- JackStraw(obj, num.replicate = 100) obj <- ScoreJackStraw(obj, dims = 1:50) JackStrawPlot(obj, dims = 1:20)不过JackStraw速度慢,大数据集会跑很久。日常分析用ElbowPlot就够用了。
提示:
ScaleData默认会把所有基因都做缩放,这在内存消耗上非常奢侈。如果只想跑PCA,可以只对高变基因做缩放,方法是ScaleData(obj, features = VariableFeatures(obj)),速度会快很多。
6. 聚类与细胞类型注释:从数字到生物学意义
6.1 KNN图、Louvain算法与分辨率的选择
PCA降维后,每个细胞被表示成几十个维度的坐标。聚类的第一步,是根据这些坐标找到每个细胞的“邻居细胞”——欧氏距离最近的那些。然后基于这些邻居关系构建KNN图,再用Louvain算法对图结构进行划分,找到连接紧密的细胞群落。
在Seurat里,两步分别对应:
obj <- FindNeighbors(obj, dims = 1:15) obj <- FindClusters(obj, resolution = 0.5)resolution参数直接决定聚类的“细粒度”。数值越大,分出的cluster越多;越小,cluster越少。这个参数没有标准答案,通常:
- 刚开始探索时用
0.5左右,看整体细胞分群情况。 - 对某个亚群进一步细分时,可以
subset出该群细胞,再跑一遍FindClusters,用更高的resolution(比如0.8或1.0)进行分析。
有一点要留意:Louvain算法本身有随机性,不同seed可能导致聚类结果略有差异。正式分析时建议设置随机种子(set.seed(1)),保证结果可复现。
6.2 marker基因鉴定:FindAllMarkers的用法与参数
聚类完成后,每个cluster只是一个编号,它们代表什么细胞类型,需要靠marker基因来判定。FindAllMarkers会为每个cluster找到相对于其他所有cluster表达显著上调的基因:
markers <- FindAllMarkers(obj, only.pos = TRUE, min.pct = 0.25, logfc.threshold = 0.25)几个参数的含义:
only.pos = TRUE:只保留上调的marker,因为注释细胞类型主要看哪些基因在该群中高表达。min.pct = 0.25:基因至少在25%的细胞中有表达才会被测试。过滤掉那些只有极少数细胞表达的基因,减少计算量。logfc.threshold = 0.25:表达差异倍数的阈值(log2尺度),低于这个值的不算显著变化。
跑完后,可以用dplyr按cluster和avg_log2FC排序,挑出每个cluster最有代表性的top 5-10个marker:
top_markers <- markers %>% group_by(cluster) %>% top_n(n = 10, wt = avg_log2FC)然后画一个热图或者气泡图,直观检查marker是否合理:
DoHeatmap(obj, features = top_markers$gene) + NoLegend()6.3 手动注释还是自动注释
拿到marker列表后,需要结合背景知识判断每个cluster是什么细胞类型。比如PBMC数据里,CD3D、CD3E高表达的是T细胞,MS4A1是B细胞,NKG7、GNLY是NK细胞,LYZ是单核细胞。
人工注释的缺点是耗时,而且容易受主观判断影响。更高效的方式是先用自动注释工具做一个初步预测,再结合marker人工确认。
常用的自动注释工具有:
- SingleR:基于纯细胞系参考数据,通过相关性打分预测细胞类型,优点是速度快。
- CellTypist:基于已标注的人体细胞图谱,用机器学习模型预测,准确率较高。
- Garnett:基于marker基因和分类器,适用于自定义参考集。
我的一般流程是:先用SingleR跑一遍,得到一个粗略的注释结果,然后和FindAllMarkers的结果做对比。如果两者一致,基本可以确定细胞类型;如果不一致,再去查原始文献或数据库确认。这样做既能提高效率,也能降低错误注释的风险。
6.4 无法区分的cluster怎么处理
实际分析中经常遇到某个cluster的marker基因不明确,既不像T细胞也不像单核细胞。这时不要强行给它安一个名字,先做以下排查:
- 把这个cluster的top marker列出来,搜索已知数据库(CellMarker、PanglaoDB)。
- 检查它的
percent.mt是不是偏高——如果是,很可能是没过滤干净的死细胞群。 - 检查它是不是两个细胞类型的过渡态——比如增殖中的T细胞,会同时表达T细胞marker和增殖marker(如
MKI67)。
如果是过渡态或低质量群,可以在后续分析中根据实际需要,选择保留或者去除。
7. UMAP可视化:把几十维的数据变成一眼能看懂的图
7.1 为什么选UMAP而不是tSNE
PCA把数据降到15-20维后,仍然无法直接作图。UMAP和tSNE都是把这十几维的数据进一步压缩到2维(或3维),便于可视化。
两者的核心区别是:tSNE擅长保留局部结构,但全局结构会失真;UMAP在保留局部结构的同时,也能较好保留全局拓扑关系,而且计算速度更快。对于单细胞数据,绝大多数情况下UMAP是更优的选择。
在Seurat中运行UMAP非常简单:
obj <- RunUMAP(obj, dims = 1:15, n.neighbors = 30, min.dist = 0.3)这里有两个调整频率最高的参数:
n.neighbors:考虑多少个邻近细胞。值越大,图上细胞团越松散,全局结构更明显;值越小,局部结构越精细,团更紧凑。一般范围是5-50,默认30。min.dist:细胞点之间允许的最小距离。值越大,点分布越松散;值越小,团内点越紧密。一般范围是0.01-0.5,默认0.3。
有不少人在这里纠结参数。我的建议是,先用默认值跑一遍,如果成团效果不理想(比如所有细胞挤成一坨,或者完全散开没有分群),再适当调整min.dist。大多数情况下默认参数就够了,不必为了“更漂亮”而乱调,因为figure的审美有时不如结果的可解释性重要。
7.2 DimPlot与FeaturePlot的读图方法
降维完成后,画图:
p1 <- DimPlot(obj, reduction = "umap", label = TRUE, pt.size = 0.5) p2 <- FeaturePlot(obj, features = c("CD3D", "MS4A1", "NKG7", "LYZ"), ncol = 2, pt.size = 0.5) p1 + p2DimPlot展示的是聚类和注释结果,一个颜色代表一个cluster。FeaturePlot展示的是某个基因在各细胞中的表达量,颜色越深代表表达量越高。
读图时有个常用技巧:把DimPlot里的cluster分布和FeaturePlot里marker基因的表达模式叠在一起看。比如某个cluster在CD3D图中恰好是颜色最深的区域,那这个cluster大概率是T细胞。这种“聚类图+marker表达图”的组合,是单细胞数据可视化中最常用的验证方式。
7.3 交互式可视化:把静态图变成能探索的界面
UMAP静态图用于最终发表没问题,但在探索阶段,我更推荐用交互式可视化工具。UCSC Cell Browser是一个非常好用的单细胞可视化工具,它可以把Seurat对象导出为网页格式,支持在浏览器里缩放、点选细胞、查看基因表达。
导出方式:
library(UCSCCellBrowser) CB <- cbBuild(seurat.obj = obj, output.dir = "cellbrowser_output", experiment.title = "Sample1", gene.matrix = obj@assays$RNA@counts)对于一个数据可视化项目来说,交互式界面带来的信息增量不是一点半点。你可以实时查询任意基因的表达分布,还能按cluster、按样本筛选,这对快速理解数据结构帮助极大。
从某种程度上说,UMAP可视化和我们平时做数据可视化大屏的思维是相通的——目的是让人能在最短时间内从复杂数据里抓住关键信息。单细胞数据的UMAP图加上交互式浏览器,本质上就是一种“单细胞版的可视化大屏”,只不过这里的“看的人”不只是你自己,还有合作者和审稿人。
7.4 也可以试试这些可视化图表
单细胞分析里,UMAP虽然是最常用的图,但绝不是唯一的选择。不同的数据类型和分析目的,适合不同的可视化方式:
- 小提琴图(VlnPlot):展示某个基因在不同cluster中的表达分布,比FeaturePlot更精确,适合展示marker基因的差异表达。
- 火山图:展示cluster之间差异表达基因的显著性和倍数变化,适合展示组间差异。
- 气泡图(DotPlot):同时展示多个marker基因在多个cluster中的表达水平(气泡大小)和阳性细胞比例(气泡颜色),是细胞类型注释中最常用的图表之一。
- 细胞轨迹图(Monocle3等):展示细胞分化过程中的连续状态变化,适合发育生物学研究。
8. 常见问题与排查技巧实录
8.1 问题速查表
我把这几年被问到最多的问题整理成了一个表格,方便大家快速定位:
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| Cell Ranger比对率很低(<50%) | 参考基因组与物种不匹配,或样本污染 | 检查--transcriptome是否正确,查看FASTQ的物种来源 |
| 细胞数远低于预期 | expect-cells设置不准确,或建库效率低 | 检查web_summary.html中饱和度指标,必要时重跑 |
| Seurat读入后细胞数远小于barcode数 | 使用了raw_feature_bc_matrix而不是filtered版本 | 改用filtered_feature_bc_matrix |
| UMAP图上所有细胞团黏在一起 | 没有正确聚类,可能PC数量选择过少 | 调整dims,参考ElbowPlot重新选择合适的PC数 |
| UMAP图上出现一团“垃圾细胞” | 线粒体基因比例高或基因检出数低,是死细胞或空液滴 | 严格质控,检查percent.mt和nFeature_RNA分布 |
| 注释结果与marker表达不符 | 快速注释工具误判 | 结合FindAllMarkers结果人工核查,必要时换参考集 |
| 内存不足(R session aborted) | 数据量太大或矩阵未稀疏化 | 确保表达矩阵是dgCMatrix格式,用subset分批操作 |
8.2 独家避坑技巧
最后分享几个“纸上不写但实战极有用”的经验:
技巧一:先跑小数据测试流程。不要一上来就跑全部细胞。先用subset抽5000个细胞把整个流程跑通,确认代码没问题、结果合理,再跑全量数据。能省下大量调试时间。
技巧二:随时保存中间结果。Seurat对象可以用saveRDS(obj, "obj_processed.rds")保存,尤其是QC后、聚类后各存一份。数据分析不是一锤子买卖,经常需要回头调整参数。有保存的中间结果,就不用每次从头跑。
技巧三:善用sessionInfo()记录环境版本。单细胞分析涉及的R包版本更新很快,不同版本的Seurat跑出的结果可能有细微差异。记录环境版本,既能保证自己结果可复现,也能在投稿时应对审稿人关于“软件版本”的提问。
技巧四:注意基因名的物种差异。很多marker基因在人和小鼠中的命名大小写不同,比如人的CD3D在小鼠中是Cd3d。当你从网上找marker列表时,务必先确认物种,再统一基因名格式,否则FeaturePlot会画不出任何内容。
我个人在实际操作中最深的体会是:单细胞分析流程看似固定,但每一步的参数选择都需要结合自己的数据特性来灵活调整。不要盲目照搬教程里的阈值,也不要看到别人用什么参数就直接复制——把自己的数据分布看清楚、把每一步的原理弄明白,才是跑通全流程的关键。
这个流程本身还可以继续向很多方向扩展,比如做细胞轨迹分析、细胞通讯分析、转录因子调控网络推断等。但无论做多复杂的分析,从FASTQ到UMAP这一步的地基一定要打牢。手里有一份质量可控、注释清晰的细胞图谱,后续所有高级分析才有意义。