关于 cell-type-annotation
用于单细胞 RNA-seq 细胞类型注释的流程技能,面向使用聚类后 AnnData 或 SingleCellExperiment 数据的用户。它指导自动、参考数据、标记基因及分层注释,并包含置信度检查和验证步骤。
主要功能
- 01指导使用 CellTypist 自动注释
- 02从参考数据转移标签
- 03依据标记基因分配类型
- 04分层细化注释标签
- 05检查置信度并验证标签
适用场景
- 01注释聚类后的单细胞 RNA-seq 数据
- 02从参考图谱转移细胞标签
- 03复核不确定的细胞类型分配
- 04将宽泛标签细化为亚型
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ awslabs/ hcls-agent-skills --skill cell-type-annotation安装后在 Claude Code 中输入/cell-type-annotation确认已加载。
详细介绍
细胞类型注释
概述
此流水线技能可生成用于注释单细胞 RNA-seq 数据中细胞类型的代码。它涵盖四种互补方法:
- 自动化 — CellTypist(基于整理后的图谱训练的逻辑回归分类器)。
- 基于参考数据 — SingleR(R)或 scANVI/
sc.tl.ingest(Python)标签转移。 - 基于标记基因 — 根据经典标记基因面板手动分配。
- 分层式 — 先分配粗略细胞区室,再进行专门的精细注释。
当用户拥有聚类后的 AnnData(或 SingleCellExperiment),并需要在 adata.obs 中添加细胞类型标签时,可使用此技能。
用法
决策树:选择注释方法
按照此决策树选择合适的方法:
- 是否有适用于你的组织和物种的 CellTypist 模型?
- 是 → 使用 CellTypist(以下步骤 1–6)
- 否 → 转到步骤 2
- 是否有来自相同组织、带标签的参考
AnnData?- 是,批次/技术相同 → 使用
sc.tl.ingest(步骤 12) - 是,批次不同 → 使用 scANVI 标签转移(步骤 13)
- 否 → 转到步骤 3
- 是,批次/技术相同 → 使用
- 你是否在 R 中使用 bulk 或分选细胞参考数据?
- 是 → 使用 SingleR(步骤 7–9)
- 否 → 转到步骤 4
- 是否有预期细胞类型的经典标记基因面板?
- 是 → 使用基于标记基因的注释(步骤 10–11)
- 否 → 从 CellTypist 最宽泛的模型(
Immune_All_Low.pkl或Developing_Human_Brain.pkl)开始,然后使用标记基因进行细化
注释工作流(15 个步骤)
步骤 1 — 确认归一化。 CellTypist 要求使用 log1p(CP10k)。检查是否存在 adata.uns['log1p'],否则运行归一化。如果数据位于 adata.raw 或某个图层中,请进行相应的子集处理。
步骤 2 — 验证基因符号。 确认 adata.var_names 是 HGNC(人类)或 MGI(小鼠)符号,而不是 Ensembl ID。如有需要,使用 sc.queries.biomart_annotations 进行转换。
步骤 3 — 选择 CellTypist 模型。 选择与物种和组织相匹配的模型。使用 celltypist.models.models_description() 列出可用选项。如果不确定,可从适用于该细胞区室的最广泛模型开始。
步骤 4 — 运行 CellTypist 注释。 使用 celltypist.annotate(),设置 majority_voting=True,并选择合适的 p_thres(默认值为 0.5;若要进行严格判定,可提高到 0.7)。
步骤 5 — 通过 to_adata() 提取结果。 调用 predictions.to_adata(),获取 .obs 中的注释。通过 adata_result.obs['conf_score'] 获取置信度。
步骤 6 — 筛除低置信度细胞。 将 conf_score < 0.5 的细胞(或低于用户指定阈值的细胞)标记为 'Unknown'。
步骤 7 — (SingleR)安装参考数据集。 使用 BiocManager::install("celldex") 并加载合适的参考数据集。
步骤 8 — (SingleR)运行预测。 使用 assay.type.test = "logcounts" 调用 SingleR(test, ref, labels)。
步骤 9 — (SingleR)剔除低质量判定结果。 使用 pruneScores(pred) 找出不可靠的分类,并将其设为 "Unknown"。
步骤 10 — (基于标记基因)定义面板。 根据文献为每种细胞类型提供至少 3 个标记基因。在有参考价值时,也纳入阴性标记。
步骤 11 — (基于标记基因)评分并分配类型。 使用 sc.pl.dotplot 进行可视化,然后将聚类映射到细胞类型。对于有歧义的聚类,标记为 'Unknown'。
步骤 12 — (Ingest)将查询数据投射到参考数据集。 需要共享 PCA 空间;运行 sc.tl.ingest(adata_query, adata_ref, obs='cell_type')。只有当查询数据和参考数据使用相同技术且批次效应极小时,效果才较好。
步骤 13 — (scANVI)半监督迁移。 在参考数据集上训练 SCVI,使用标签扩展为 SCANVI,然后对查询数据运行 load_query_data。可原生处理批次效应。
步骤 14 — 验证注释。 按已分配的细胞类型对经典标记基因绘制 dotplot,以确认注释结果。检查各样本的比例是否符合生物学预期。
步骤 15 — 分层细化。 如果细分类标签不稳定,先注释较粗略的细胞区室(通过 PTPRC/EPCAM/COL1A1 区分免疫/基质/上皮细胞),再进行子集处理,然后使用专用模型或标记基因分别重新注释。
分支判断
- 在步骤 4:如果
>30%的细胞conf_score < 0.5,说明模型不适用 → 改用基于标记基因的方法或尝试其他模型。 - 在步骤 6:如果某个标签意外地占据
>80%的细胞,应怀疑模型不匹配或数据未归一化。 - 在步骤 12:如果 ingest 后查询数据的 UMAP 与参考数据集无法对齐,说明批次效应过强 → 改用 scANVI(步骤 13)。
- 在步骤 14:如果细胞比例不符合生物学预期(例如,PBMC 中成纤维细胞占 60%),请重新检查归一化和模型选择。
响应格式
- 首先给出用户需要的命令或代码,然后再解释。
- 结构按以下顺序组织:确认输入 → 可运行代码 → 关键参数说明 → 注意事项。
- 每项任务提供一个完整且可运行的示例;不要展示所有替代方案。
- 代码注释应精简且实用。
- 目标:50–100 行代码,并附简短说明。
- 始终包含置信度提取和筛选步骤。
核心概念
1. CellTypist(自动化,Python)
import scanpy as sc
import celltypist
from celltypist import models
# Input: log1p-normalized to 10,000 counts/cell
sc.pp.normalize_total(adata, target_sum=1e4)
sc.pp.log1p(adata)
# Download models (first run only)
models.download_models()
print(models.models_description()) # list available models
# Load tissue-matched model
model = models.Model.load(model='Immune_All_Low.pkl')
# Annotate with majority voting
predictions = celltypist.annotate(
adata,
model=model,
majority_voting=True, # smooth labels over neighborhood graph
over_clustering='leiden', # obs key for majority voting resolution
p_thres=0.5, # minimum probability threshold (raise for stringency)
)
# Extract results — modern API (CellTypist >=1.3)
adata_result = predictions.to_adata()
# Annotations now in adata_result.obs:
# 'predicted_labels' — per-cell raw predictions
# 'majority_voting' — smoothed labels
# 'conf_score' — max probability per cell
# Transfer to original adata
adata.obs['cell_type'] = adata_result.obs['majority_voting']
adata.obs['cell_type_conf'] = adata_result.obs['conf_score']
# Filter low-confidence assignments
adata.obs.loc[adata.obs['cell_type_conf'] < 0.5, 'cell_type'] = 'Unknown'
celltypist.annotate() 的关键参数:
| 参数 | 默认值 | 效果 |
|---|---|---|
majority_voting | False | 在 kNN 图上平滑标签;始终启用 |
over_clustering | None | 用于投票分辨率的 obs 键(例如 'leiden');聚类越精细,分辨率越高 |
p_thres | 0.5 | 分配标签所需的最低概率;若只保留高置信度结果,则提高到 0.7 |
mode | 'best match' | 在多标签场景中使用 'prob match' |
常用模型:Immune_All_Low、Immune_All_High、Healthy_COVID19_PBMC、Human_Lung_Atlas、Cells_Intestinal_Tract、Developing_Human_Brain、Adult_Mouse_Gut。请匹配物种和组织。
2. SingleR(基于参考数据,R)
if (!requireNamespace("BiocManager", quietly = TRUE))
install.packages("BiocManager")
BiocManager::install(c("SingleR", "celldex", "SingleCellExperiment"))
library(SingleR)
library(celldex)
library(SingleCellExperiment)
# Choose reference matching species/tissue
ref <- celldex::HumanPrimaryCellAtlasData()
# ref <- celldex::MonacoImmuneData() # sorted human immune
# ref <- celldex::ImmGenData() # mouse immune
# ref <- celldex::BlueprintEncodeData() # human stromal + immune
pred <- SingleR(
test = sce,
ref = ref,
labels = ref$label.main,
assay.type.test = "logcounts"
)
sce$cell_type <- pred$labels
table(pred$labels)
# Prune low-confidence calls (delta < median - 3*MAD)
sce$cell_type[is.na(pred$pruned.labels)] <- "Unknown"
3. 基于标记基因(手动,Python)
import scanpy as sc
marker_genes = {
'T cells': ['CD3D', 'CD3E', 'CD2'],
'CD4 T': ['CD4', 'IL7R'],
'CD8 T': ['CD8A', 'CD8B'],
'B cells': ['CD79A', 'MS4A1', 'CD19'],
'NK cells': ['NKG7', 'GNLY', 'KLRD1'],
'Monocytes': ['CD14', 'LYZ', 'S100A8'],
'Dendritic': ['FCER1A', 'CST3', 'CLEC10A'],
'Fibroblasts': ['COL1A1', 'DCN', 'COL3A1'],
'Endothelial': ['PECAM1', 'VWF', 'CDH5'],
'Epithelial': ['EPCAM', 'KRT18', 'KRT19'],
}
sc.pl.dotplot(adata, marker_genes, groupby='leiden', standard_scale='var')
# Assign after reviewing the dotplot
cluster_to_celltype = {
'0': 'T cells', '1': 'Monocytes', '2': 'B cells',
'3': 'NK cells', '4': 'Unknown',
}
adata.obs['cell_type'] = adata.obs['leiden'].map(cluster_to_celltype).astype('category')
4. 从参考图谱进行标签迁移
# Quick path: sc.tl.ingest (PCA/UMAP projection, kNN labels)
# Only use when batch effects are minimal
import scanpy as sc
sc.pp.pca(adata_ref)
sc.pp.neighbors(adata_ref)
sc.tl.umap(adata_ref)
sc.tl.ingest(adata_query, adata_ref, obs='cell_type')
# Robust path: scANVI (semi-supervised, handles batch)
import scvi
scvi.model.SCVI.setup_anndata(adata_ref, batch_key='batch')
scvi_model = scvi.model.SCVI(adata_ref)
scvi_model.train()
scvi.model.SCANVI.setup_anndata(
adata_ref, labels_key='cell_type', unlabeled_category='Unknown', batch_key='batch'
)
scanvi_model = scvi.model.SCANVI.from_scvi_model(scvi_model, unlabeled_category='Unknown')
scanvi_model.train(max_epochs=20)
scanvi_model = scvi.model.SCANVI.load_query_data(adata_query, scanvi_model)
scanvi_model.train(max_epochs=100, plan_kwargs={'weight_decay': 0.0})
adata_query.obs['cell_type_pred'] = scanvi_model.predict(adata_query)
5. 验证
# Confirm assigned labels express their markers
sc.pl.dotplot(adata, marker_genes, groupby='cell_type', standard_scale='var')
sc.pl.violin(adata, ['CD3D', 'MS4A1', 'CD14'], groupby='cell_type', rotation=90)
# Sanity-check proportions per sample
adata.obs.groupby(['sample', 'cell_type']).size().unstack(fill_value=0)
对于没有明确标记基因支持的簇,请标记为 'Unknown',而不要强行赋予标签。
6. 分层注释
# Step 1: coarse compartments
compartment_markers = {
'Immune': ['PTPRC'], # CD45
'Epithelial': ['EPCAM', 'KRT8'],
'Stromal': ['COL1A1', 'PECAM1'],
}
# Step 2: subset and re-annotate each compartment
adata_immune = adata[adata.obs['compartment'] == 'Immune'].copy()
model = models.Model.load('Immune_All_Low.pkl')
preds = celltypist.annotate(adata_immune, model=model, majority_voting=True)
adata_immune_result = preds.to_adata()
adata_immune.obs['cell_type_fine'] = adata_immune_result.obs['majority_voting']
快速参考
| 任务 | 工具 | 关键调用 |
|---|---|---|
| PBMC / 免疫细胞自动注释 | CellTypist | models.Model.load('Immune_All_Low.pkl') |
| 肺图谱映射 | CellTypist | 'Human_Lung_Atlas.pkl' |
| 肠道 / 肠道组织 | CellTypist | 'Cells_Intestinal_Tract.pkl' |
| 广泛的人类参考数据(R) | SingleR | celldex::HumanPrimaryCellAtlasData() |
| 分选的免疫细胞参考数据(R) | SingleR | celldex::MonacoImmuneData() |
| 小鼠免疫细胞(R) | SingleR | celldex::ImmGenData() |
| 查询数据→参考数据迁移 | scanpy | sc.tl.ingest(adata_q, adata_ref, obs='cell_type') |
| 感知批次的迁移 | scvi-tools | SCANVI.load_query_data(...) |
| 标记基因检查 | scanpy | sc.pl.dotplot(adata, markers, groupby='leiden') |
| 置信度(CellTypist) | CellTypist | predictions.to_adata().obs['conf_score'] |
| 置信度(SingleR) | SingleR | is.na(pred$pruned.labels) 标记低质量结果 |
所需输入:
- CellTypist:
adata.X= 经 log1p(CP10k) 标准化,基因以 HGNC 符号作为adata.var_names。 - SingleR:带有
logcountsassay 的SingleCellExperiment。 - scANVI:
adata.layers['counts']中的原始计数,以及参考数据中的batch_key、labels_key。
数值阈值
| 指标 | 阈值 | 操作 |
|---|---|---|
CellTypist conf_score | < 0.5 | 标记为 'Unknown' |
CellTypist conf_score | < 0.7 | 标记以供人工审核 |
CellTypist p_thres | 0.5(默认值) | 提高到 0.7 以进行严格注释 |
| SingleR delta score | < 中位数 - 3×MAD | 剪枝(设为 NA) |
| 簇大小 | < 20 个细胞 | 不分配稀有亚型;使用父级标签 |
| 标记基因点图比例 | < 0.3 | 标记基因表达不可靠;不用于赋值 |
| 预期比例偏差 | > 文献范围的 3× | 怀疑模型错误或标准化错误 |
常见错误
-
错误: 将原始计数值输入 CellTypist 正确做法: 注释前务必运行
sc.pp.normalize_total(adata, target_sum=1e4)+sc.pp.log1p(adata)原因: CellTypist 需要经过归一化处理、每个细胞总计数为 10,000 的计数值的 log1p;原始数据或经过 SCTransform 处理的数据会产生毫无意义的标签 -
错误: 使用与组织或物种不匹配的参考模型 正确做法: 选择 CellTypist 模型或 SingleR 参考数据时,确保物种(
Human_*与Mouse_*)和组织类型匹配 原因: 对上皮肿瘤细胞使用Immune_All_Low,或对小鼠数据使用HumanPrimaryCellAtlasData,都会产生看似可信、实际错误的标签 -
错误: 将 Ensembl ID 或大小写混杂的基因符号传给 CellTypist/SingleR 正确做法: 注释前将
adata.var_names转换为 HGNC(人类)或 MGI(小鼠)符号 原因: Ensembl ID 或大小写错误(Cd3d与CD3D)会导致特征被静默丢弃,在没有警告的情况下降低模型性能 -
错误: 使用
predictions.probability_matrix.max(axis=1)获取置信度分数 正确做法: 使用predictions.to_adata().obs['conf_score'],这是自 CellTypist ≥1.3 起稳定的 API 原因: 概率矩阵的做法很脆弱,并且依赖版本;更新后可能会失效或返回错误值 -
错误: 为少于 20 个细胞的簇指定罕见亚型标签 正确做法: 当簇大小少于 20 个细胞时,归并到父细胞类型,或标记为
'Unknown'原因: 将小簇指定为罕见亚型时,统计效力不足;若缺少强区分标记,这类结果并不可靠 -
错误: 查看点图后,仍强行为缺少明确标记的簇指定标签 正确做法: 将有歧义的簇保留为
'Unknown',而不是选择次优猜测 原因: 强行指定标签会将错误传递到下游分析(差异表达、轨迹分析)中,并形成错误的生物学解释 -
错误: 运行 CellTypist 时设置
majority_voting=False,并直接使用单细胞标签 正确做法: 务必启用majority_voting=True,通过局部邻域图平滑标签 原因: 单细胞预测噪声较大;多数投票利用簇结构生成一致、稳定的注释 -
错误: 未进行批次校正就运行
sc.tl.ingest进行标签转移 正确做法: 先整合查询数据与参考数据,或使用 scANVI 的load_query_data路径,该路径可原生处理批次 原因:ingest假定查询数据与参考数据具有相同的嵌入;批次效应会导致错位和错误的标签转移 -
错误: 不根据生物学预期验证细胞类型比例 正确做法: 务必将各样本的细胞类型比例与已知组织组成进行交叉核对 原因: 如果 PBMC 样本中有 80% 是成纤维细胞,就是一个危险信号,表明模型错误、归一化有误或数据质量存在问题
-
错误: 只在一个分辨率下进行注释,不进行分层细化 正确做法: 先注释粗略的区室(免疫/基质/上皮),再使用专用模型或标记细化各区室 原因: 对区分不佳的簇进行细粒度标注并不稳定;分层注释能产生更可靠的结果
参考文献
- CellTypist:Dominguez Conde 等,Science 2022,https://doi.org/10.1126/science.abl5197
- SingleR:Aran 等,Nat Immunol 2019,https://doi.org/10.1038/s41590-018-0276-y
- scANVI:Xu 等,Mol Syst Biol 2021,https://doi.org/10.15252/msb.20209620
- celldex:Aran 等,2019,https://bioconductor.org/packages/celldex