
上节完成了基本构建分析(空转联合单细胞分析(二):【干一件有功德的事】构建一个20w细胞且有意义的Endometrium scRNA参考图谱),接着我们对细胞进行注释,首先使用经典marker进行了大类的注释,此次注释没有涉及亚群或者更详细的注释;同时介绍一款基于大语言模型的scRNA细胞自动注释的包。也是浅尝一下,我们一直强调,自动注释不能代替结合生物学背景的手动注释!
无论是哪种单细胞转录组自动注释工具或者库,都是有局限性的,都无法百分之百准确,它们只能“辅助”研究者,而不能完全取代人工判断。您可能听说过这些工具,CellTypist、ScType、mLLMCelltype、SingleR、Azimuth、Garnett、scmap等,也许还有我没有了解到的。局限性在于大部分自动注释方法都依赖已有参考集。如果你的数据包含:新的细胞类型、组织特异性亚群、未在参考集中出现的状态,那么很难得到正确的注释结果,当然随着单细胞测序的不断发展,数据库的不断完整,效果会好很多,但绝不是100%。再者,没有工具是完美的,例如CellTypist专门针对免疫细胞的注释,如果您的数据来源于免疫组织或者需要注释免疫细胞亚群,是一个很好的选择,但是对于其他类型的细胞效果就不太好了。还有部分工具依赖marker gene,这与marker gene质量息息相关,比如即使是Bcell这种非常确定的marker gene ‘CD79A’,在不同的数据集中表达量是有差异的,其他的celltype自不必说,不同文献使用的marker是不一致的,这对于结果的准确性是有影响的。最后,自动注释无法分辨细胞状态,比如我们这里演示的数据集子宫内膜上皮细胞,在生殖上应该更准确的区分为腺上皮、腔上皮,但是由于两者通用marker表达一样,所以自动注释很难区分,只能注释为上皮细胞。
所以,准确注释celltype的前提有两个,第一是需要有高质量的数据,包括前期的数据质控处理及降维聚类;第二是结合自己生物学背景,结合多种方式验证注释,才能得到较好的结果。
我们强调细胞类型注释要结合文献、组织学与生物学背景,而不是依赖某个工具,故而除了singleR,一直以来从未介绍任何自动注释工具,就目前而言,准确性不是很高。但是对于很难注释的数据,有一定参考,所以这里挑选最新的一种工具进行演示,并不是说这种工具最优秀!
根据文献提供的经典marker先进行手动注释,之后我们会演示几个比较好用的自动注释工具进行对比,看看效果。但是还是建议,注释细胞最准确的依然是依据文献及生物学背景的手动注释,或者结合自动注释手动检查矫正。
library(Seurat)
library(ggplot2)
library(dplyr)
load('~/data_analysis/10X_space/sce_edm_UMAP_cellAnno.RData')这个数据集有213769细胞:
sce_edm## An object of class Seurat
## 30041 features across 213769 samples within 1 assay
## Active assay: RNA (30041 features, 3000 variable features)
## 28 layers present: data, counts, scale.data.LH11_1, scale.data.LH11_2, scale.data.LH11_3, scale.data.LH3_1, scale.data.LH3_2, scale.data.LH3_3, scale.data.LH5_1, scale.data.LH5_2, scale.data.LH5_3, scale.data.LH7_1, scale.data.LH7_2, scale.data.LH7_3, scale.data.LH9_1, scale.data.LH9_2, scale.data.LH9_3, scale.data.RIF1, scale.data.RIF10, scale.data.RIF2, scale.data.RIF3, scale.data.RIF4, scale.data.RIF5, scale.data.RIF6, scale.data.RIF7, scale.data.RIF8, scale.data.RIF9, scale.data
## 3 dimensional reductions calculated: pca, integrated.Harmony, umapDimPlot(sce_edm,label = T,pt.size = 0.5)
经典marker表达量:
classical_markers <- c('DCN','LUM','COL3A1','COL1A1', #stromal
'TOP2A','MKI67', #proliferating cells
'EPCAM','CDH1','FOXJ1','CFAP52', #Ciliated epithelial cells
'KRT18','SOX17','MSX1', #unCiliated epithelial cells
'VWF','CD34','CDH5', #endothelial cells
'GNLY','NKG7','NCAM1','KLRC1', #NK cells
'CD3D','CD3G', #Tcells
'TPSAB1','KIT', #Mast
'CD14','CD68','C1QA', #macrophages
'MS4A1','CD79A', #@Bcells
"S100A8", "CSF3R","FCGR3B") #Neutrophilesp = DotPlot(sce_edm, features =classical_markers) + RotatedAxis()
df <- p$data[,c(3,4,1,2,5)]
df$avg.exp.scaled <- ifelse(df$avg.exp.scaled <0, 0,df$avg.exp.scaled)
#devtools::install_github("Simon-Leonard/FlexDotPlot")
library(FlexDotPlot)
#聚类展示
dot_plot(data.to.plot =df,#input data,如果是dataframe,那么必须第一列是x轴factor,第二列必须是y轴factor,也就是作图的x,y坐标变量。
size_var = "pct.exp", #点大小,需要是数值。如果不需要展示点的大小,设置为NA
col_var = "avg.exp.scaled",#点颜色设置
size_legend = "Percent\nExpressed", #size legend标题
col_legend = "Average\nExpression",#颜色 legend标题
cols.use = c('#f7fcfd','#e0ecf4','#bfd3e6','#9ebcda','#8c96c6','#8c6bb1','#88419d','#810f7c','#4d004b'), #一组颜色向量
shape.scale = 8, #调整点大小
x.lab.pos = "bottom", #x轴标签位置,"bottom","top","both" or "none"
y.lab.pos = "left",#y轴标签位置,"left","right","both"or "none"
x.lab.size.factor = 1, #x轴标签字体大小
y.lab.size.factor = 1,#y轴标签字体大小
display_max_sizes = F,
transpose =F,#坐标轴旋转
size.breaks.number = 5,
color.breaks.number = 3,
hclust_method = "ward.D2",#聚类方法
dend_y_var = c("avg.exp.scaled"))#用于聚类的列
先根据文献经典的marker大致注释一下celltype:至于亚群及更详细的注释,需要提取大群进行再分析!
library(plyr)sce_edm@meta.data$manual_cellAnno <- mapvalues(sce_edm@meta.data$seurat_clusters,
from=c("0","1","2","3","4","5",
"6","7","8","9","10",
"11","12","13","14","15",
"16","17","18","19",
"20","21","22","23","24",
"25","26","27","28","29","30"),
to=c("Stromal","Stromal","NK","NK",
"Stromal","unCiliated",
"Tcell","unCiliated","Tcell","Tcell","unCiliated",
"NK","Myeloid","Bcell","NK","NK",
"Neutrophil","Stromal","unCiliated","Ciliated",
"Mast","Tcell","Stromal","NK",
"endothelial","Stromal","Myeloid",
"Bcell","Stromal","unCiliated","Neutrophil"))
Idents(sce_edm) <- sce_edm@meta.data$manual_cellAnnocolor_pal<-c("#11A579","#F2B701","#66C5CC","#80BA5A","#7F3C8D","#CF1C90","#3969AC","#f97b72","#E73F74","#4b4b8f","#ACA4E2","#31C53F","#B95FBB","#D4D915","#28CECA")
DimPlot(sce_edm, reduction = "umap", label = T,cols = color_pal)+NoLegend()
mLLMCelltype是一个比较新的工具,2025年出现文章在预印本Yang, C., Zhang, X., & Chen, J. (2025). Large Language Model Consensus Substantially Improves the Cell Type Annotation Accuracy for scRNA-seq Data. bioRxiv。mLLMCelltype是一种基于多种大语言模型(Multi-LLM)的迭代共识框架,,用于单细胞RNA 测序数据的自动细胞类型注释。该框架整合了多个大型语言模型,包括OpenAI GPT-5/4.1, Anthropic Claude-4/3.7/3.5, Google Gemini-2.0, X.AI Grok-3, DeepSeek-V3, Alibaba Qwen2.5, Zhipu GLM-4, MiniMax, Stepfun, and OpenRouter,通过基于共识的预测来提高注释的准确性。mLLMCelltype与流行的单细胞分析平台(如Scanpy和Seurat)集成,使研究人员能够将其纳入现有的生物信息学工作流程中。与某些传统方法不同,它不需要参考数据集进行注释。工具提供了R和python两个版本。我们介绍R版的使用并进行测试一下效果。
Github链接:https://github.com/cafferychen777/mLLMCelltype
工具也有网页版,对于中国大陆用户,我们强烈推荐直接使用网页版[haha]:https://mllmcelltype.com,网页版使用很简单,还有中文页面哦,可谓是考虑周到,虽然作者在USA,但是想着我们,谢谢!
#install.packages("mLLMCelltype")
library(mLLMCelltype)输入数据准备-marker genes:
#sce_edm <- JoinLayers(sce_edm)
Idents(sce_edm) <- 'seurat_clusters'
cluster_markers <- FindAllMarkers(sce_edm, min.pct = 0.3, logfc.threshold = 0.3, only.pos = T,assay='RNA')annotate_cell_types函数进行细胞注释:具体使用参照?annotate_cell_types帮助函数:关于参数的一些解释: input:支持多种形式的传入,Seurat’s FindAllMarkers() 分析得到的dataframe;或者是一个gene list,list的name是cluster,元素是每个cluster自己的marker基因; tissue_name:组织来源,例如‘human endometrium’,能够有效提高注释准确率。 model:指定要使用的LLM模型,传入一个向量,可以是单个模型,也可以是多个不同的模型。 api_key:包含所选模型的API密钥的字符串。每个提供商都需要特定的API密钥格式和身份验证方法。 top_gene_count:指定每个cluster使用的top marker基因数量,默认10。
注意:关于api_key不同的模型需要自己申请自己的API key,例如https://cafferyang.com/mLLMCelltype/articles/getting-started.html,有些可能需要付费的,有些在国内网络无法使用,所以没办法就需要使用国内可用的或者免费的比如deepseek、阿里云等,这里我们只是演示一下代码的使用。在线工具也很方便,需要传入数据和APIkey。在线工具要求的输入数据是dataframe,支持csv、tsv、Excel等格式,需要整理成每一行是一个cluster及其基因,这里我们也试一下在线工具,每个cluster选取top50的基因用于分析:
#选取top50 marker gene
analysis_markers <- cluster_markers %>%
group_by(cluster) %>%
slice_max(order_by = avg_log2FC, n = 50) library(data.table)
#转化为宽数据
analysis_markers <- analysis_markers[,c('cluster','gene')]
dt <- as.data.table(analysis_markers)
dt[, gene_id := 1:.N, by = cluster]
dt <- dcast(dt, cluster ~ gene_id, value.var = "gene")
library(tibble)
dt <- dt %>% column_to_rownames(var = "cluster")
colnames(dt) <- paste0("Gene_", colnames(dt))
dtwrite.csv(dt, file = 'analysis_markers.csv')本来以为在线工具不用申请API-key,结果不行。和代码一样。因为大多数的API-key是付费的,演示使用免费的模型:使用 OpenRouter 作为统一接入点。OpenRouter 提供了一个统一的 API 接口,可以访问多种模型,包括部分在中国大陆难以直接访问的模型。通过 OpenRouter,您只需要一个 API 密钥即可使用多种模型:我使用的免费模型,没有结果!
# 使用 OpenRouter 免费模型进行共识注释(无需消耗积分)
free_consensus_results <- interactive_consensus_annotation(
input = cluster_markers,
tissue_name = "human endometrium",
top_gene_count = 50,
models = c(
"deepseek/deepseek-r1:free", # DeepSeek Chat v3(免费)
),
api_keys = list(openrouter = "我的API-key"# 单一 API 密钥访问多种模型
),
consensus_check_model = "deepseek/deepseek-r1:free"# 免费模型用于共识检查
)Integrating with Seurat:
# Assuming you have a Seurat object named 'seurat_obj' and consensus results
library(Seurat)
# Add consensus annotations to Seurat object
seurat_obj$cell_type_consensus <- plyr::mapvalues(
x = as.character(Idents(seurat_obj)),
from = as.character(0:(length(consensus_results$final_annotations)-1)),
to = consensus_results$final_annotations
)
# Extract consensus metrics from the consensus results
# Note: These metrics are available in the consensus_results$initial_results$consensus_results
consensus_metrics <- lapply(names(consensus_results$initial_results$consensus_results), function(cluster_id) {
metrics <- consensus_results$initial_results$consensus_results[[cluster_id]]
return(list(
cluster = cluster_id,
consensus_proportion = metrics$consensus_proportion,
entropy = metrics$entropy
))
})
# Convert to data frame for easier handling
metrics_df <- do.call(rbind, lapply(consensus_metrics, data.frame))
# Add consensus proportion to Seurat object
seurat_obj$consensus_proportion <- plyr::mapvalues(
x = as.character(Idents(seurat_obj)),
from = metrics_df$cluster,
to = metrics_df$consensus_proportion
)
# Add entropy to Seurat object
seurat_obj$entropy <- plyr::mapvalues(
x = as.character(Idents(seurat_obj)),
from = metrics_df$cluster,
to = metrics_df$entropy
)数据下载链接(可作为参考或者学习数据集):
通过网盘分享的文件:sce_edm_UMAP_cellAnno.RData.zip
链接: https://pan.baidu.com/s/11JulEk_v79GRR52PMQgeyw?pwd=4k73 提取码: 4k73