mcpField
S
scanpy-scrna-seq

scanpy-scrna-seq

github.com/jaechang-hits数据与数据库38440 次安装6 天前更新
代码仓库

关于 scanpy-scrna-seq

面向单细胞 RNA-seq 研究者的 Python 分析工作流,使用 Scanpy 和 AnnData 完成从质控到细胞类型注释的常见步骤。还涵盖轨迹分析、条件间差异表达及用于发表的图表。

主要功能

  1. 01导入并质控单细胞数据
  2. 02归一化与高变基因筛选
  3. 03PCA、UMAP 与 Leiden 聚类
  4. 04差异表达和标记基因识别
  5. 05细胞类型注释与轨迹分析
  6. 06生成分析图表并导出结果

适用场景

  1. 01分析 10X 或 h5ad 表达矩阵
  2. 02识别细胞群及其标记基因
  3. 03比较不同条件下的细胞表达
  4. 04推断细胞发育轨迹

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jaechang-hits/sciagent-skills --skill scanpy-scrna-seq

安装后在 Claude Code 中输入/scanpy-scrna-seq确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
api_reference.md
plotting_guide.md
standard_workflow.md

详细介绍

Scanpy 单细胞 RNA-seq 分析

概览

Scanpy 是基于 AnnData 格式构建的可扩展 Python 工具包,用于分析单细胞 RNA-seq 数据。本技能涵盖端到端标准工作流:质量控制、归一化、高变基因筛选、降维、聚类、标记基因识别和细胞类型注释。它会生成带注释的数据集和达到发表质量的可视化结果。

适用场景

  • 分析单细胞 RNA-seq 计数矩阵(10X Genomics、h5ad、CSV、loom)
  • 对 scRNA-seq 数据集进行质量控制筛选(线粒体比例、基因计数)
  • 执行降维:PCA、UMAP、t-SNE
  • 通过 Leiden 社区检测识别细胞簇
  • 查找每个簇的差异表达标记基因(Wilcoxon、t 检验、逻辑回归)
  • 根据已知标记基因面板注释细胞类型
  • 进行轨迹推断和伪时间分析(PAGA、扩散伪时间)
  • 生成达到发表质量的单细胞图表(点图、热图、堆叠小提琴图)
  • 比较不同实验条件下各细胞类型中的基因表达
  • 对导出的表格绘制通用图表(火山图/热图/柱状图/KM),请使用 omics-plotting SKILL;单细胞视图仍使用 scanpy sc.pl.*
  • 若要在现有 R 工作流中分析 scRNA-seq,或需要 Seurat 专属的 assay 类型,请改用 Seurat(R/Bioconductor)

前提条件

  • Python 包:scanpy>=1.10、leidenalg、igraph、anndata
  • 数据要求:基因表达计数矩阵(细胞 x 基因)。常见格式:10X Cell Ranger 输出(filtered_feature_bc_matrix/)、.h5ad、.h5、.csv
  • 环境:Python 3.9+;细胞数超过 50k 时建议使用 16GB+ RAM
pip install "scanpy[leiden]" anndata

预先访谈

编写任何分析代码之前,先与用户确认以下事项。

decisions:
  - id: D1
    param: pct_counts_mt
    kind: required
    source: data
    ask: "Dying cells leak cytoplasmic RNA and read as high-mitochondrial - where should the cut sit for this tissue?"
    default: "20% (PBMC ~5%, solid tumour ~20%; propose from the observed distribution)"

  - id: D2
    param: batch_key
    kind: required
    source: data
    ask: "Which variable separates samples that were processed apart (donor, run, 10X lane), so its effect is not read as biology?"
    default: null
    skip_if: "all cells come from one sample and one processing run"

  - id: D3
    param: resolution
    kind: optional
    source: user
    ask: "How finely should cells be split - broad lineages, or subtypes within them?"
    default: "0.8 (0.1-0.3 broad lineages, 1.0-2.0 fine subtypes)"

  - id: D4
    param: n_top_genes
    kind: optional
    source: user
    ask: "How many variable genes should drive the embedding? More captures subtle states but adds noise."
    default: 2000

  - id: D5
    param: n_pcs
    kind: optional_conditional
    source: data
    depends_on: [D4]
    ask: "How many principal components carry real structure, by the variance-ratio elbow?"
    default: 40

  - id: D6
    param: min_genes, min_cells
    kind: optional_conditional
    source: data
    ask: "Where should empty droplets and undetected genes be cut off?"
    default: "cells with <200 genes, genes in <3 cells"

  - id: D6b
    param: annotationStrategy
    kind: required
    source: user
    ask: "After clustering, should clusters be named from canonical markers by hand, or handed to a reference-based annotator?"
    default: "manual marker-based within this skill; see `single-cell-annotation-guide` to choose"

  - id: D6c
    param: markerPanel
    kind: required
    source: literature
    depends_on: [D6b]
    ask: "Which canonical markers define the populations expected in this tissue and state?"
    default: null
    skip_if: "annotation delegated to a reference-based tool"

  - id: D7
    param: method (rank_genes_groups)
    kind: optional
    source: user
    ask: "Which test should rank marker genes per cluster?"
    default: "wilcoxon"

  - id: D8
    param: target_sum, flavor
    kind: optional
    source: user
    ask: "Should the standard library-size normalization and Seurat v3 highly-variable-gene selection be used?"
    default: "target_sum 1e4, flavor seurat_v3"

  - id: D9
    param: random_state
    kind: never_ask
    source: data
    reason: "Fixes the draw for reproducibility; does not change what the data supports"
    default: 0

D5 取决于 D4,因为肘部位置是根据选定的变异基因计算出的 PCA 来判断的——基因数量不同,肘部位置也会变化。D2 会影响整个流程,而不仅仅是单次调用:指定批次后,必须在邻居图构建之前增加校正阶段(harmony-batch-correction 或 ComBat)。

工作流

步骤 1:设置和数据加载

配置 scanpy 设置,并将计数矩阵读入 AnnData 对象。确保基因名称唯一。

import scanpy as sc
import numpy as np
import pandas as pd

# Configure settings
sc.settings.verbosity = 3  # errors=0, warnings=1, info=2, hints=3
sc.settings.set_figure_params(dpi=80, facecolor="white")

# Load data — pick the reader matching your format
adata = sc.read_10x_mtx(
    "path/to/filtered_feature_bc_matrix/",
    var_names="gene_symbols",
    cache=True,
)
# Alternatives:
# adata = sc.read_h5ad("data.h5ad")
# adata = sc.read_10x_h5("data.h5")

adata.var_names_make_unique()
print(f"Loaded: {adata.n_obs} cells x {adata.n_vars} genes")
print(f"Sparsity: {1 - adata.X.nnz / (adata.n_obs * adata.n_vars):.1%}")
步骤 2:质量控制

标注线粒体/核糖体基因,计算质量控制指标,可视化其分布,并筛选低质量细胞。

# Annotate gene groups
adata.var["mt"] = adata.var_names.str.startswith("MT-")    # human; use "mt-" for mouse
adata.var["ribo"] = adata.var_names.str.startswith(("RPS", "RPL"))

# Calculate QC metrics
sc.pp.calculate_qc_metrics(
    adata, qc_vars=["mt", "ribo"], percent_top=None, log1p=False, inplace=True
)

# Visualize QC distributions
sc.pl.violin(
    adata,
    ["n_genes_by_counts", "total_counts", "pct_counts_mt"],
    jitter=0.4,
    multi_panel=True,
)
sc.pl.scatter(adata, x="total_counts", y="pct_counts_mt")
sc.pl.scatter(adata, x="total_counts", y="n_genes_by_counts")

# Filter — adjust thresholds based on the QC plots above
sc.pp.filter_cells(adata, min_genes=200)
sc.pp.filter_genes(adata, min_cells=3)
adata = adata[adata.obs.n_genes_by_counts < 5000, :]  # remove potential doublets
adata = adata[adata.obs.pct_counts_mt < 20, :]         # remove dying cells

print(f"After QC: {adata.n_obs} cells x {adata.n_vars} genes")
步骤 3:归一化和特征筛选

归一化文库大小,进行对数转换,并识别高变基因(HVG)。保存原始计数,以便后续进行差异表达分析。

# Preserve raw counts in a layer
adata.layers["counts"] = adata.X.copy()

# Normalize to 10,000 counts per cell, then log-transform
sc.pp.normalize_total(adata, target_sum=1e4)
sc.pp.log1p(adata)

# Save normalized data as .raw for visualization
adata.raw = adata

# Identify highly variable genes
sc.pp.highly_variable_genes(adata, n_top_genes=2000, flavor="seurat_v3", layer="counts")
sc.pl.highly_variable_genes(adata)

# Subset to HVGs
adata = adata[:, adata.var.highly_variable]
print(f"HVG subset: {adata.n_obs} cells x {adata.n_vars} genes")
步骤 4:缩放和回归

回归去除混杂因素,并缩放基因表达值,为 PCA 做准备。

# Regress out unwanted sources of variation
sc.pp.regress_out(adata, ["total_counts", "pct_counts_mt"])

# Scale to unit variance, clip extreme values
sc.pp.scale(adata, max_value=10)

print(f"Scaled matrix: mean={adata.X.mean():.4f}, std={adata.X.std():.4f}")
步骤 5:降维

计算 PCA,查看方差比肘部图,然后构建邻域图并使用 UMAP 进行嵌入。

# PCA
sc.tl.pca(adata, svd_solver="arpack", n_comps=50)
sc.pl.pca_variance_ratio(adata, log=True, n_pcs=50)

# Determine n_pcs from elbow plot (typically 30-50)
n_pcs = 40

# Compute k-nearest neighbor graph
sc.pp.neighbors(adata, n_neighbors=15, n_pcs=n_pcs)

# UMAP embedding
sc.tl.umap(adata)
sc.pl.umap(adata, color=["n_genes_by_counts", "total_counts", "pct_counts_mt"])

print(f"UMAP shape: {adata.obsm['X_umap'].shape}")
步骤 6:聚类

使用多个分辨率运行 Leiden 聚类,并通过目视检查选出最佳粒度。

# Test multiple resolutions
for res in [0.3, 0.5, 0.8, 1.0, 1.2]:
    sc.tl.leiden(adata, resolution=res, key_added=f"leiden_res{res}", flavor="igraph", n_iterations=2)

# Compare resolutions side-by-side
sc.pl.umap(
    adata,
    color=["leiden_res0.3", "leiden_res0.5", "leiden_res0.8", "leiden_res1.0"],
    ncols=2,
    legend_loc="on data",
)

# Pick final resolution based on biological plausibility
adata.obs["leiden"] = adata.obs["leiden_res0.5"]
n_clusters = adata.obs["leiden"].nunique()
print(f"Selected resolution=0.5: {n_clusters} clusters")
步骤 7:标记基因识别

使用原始计数,通过 Wilcoxon 秩和检验查找每个簇的差异表达基因。

# Rank genes per cluster
sc.tl.rank_genes_groups(adata, groupby="leiden", method="wilcoxon", use_raw=True)

# Visualization
sc.pl.rank_genes_groups(adata, n_genes=20, sharey=False)
sc.pl.rank_genes_groups_dotplot(adata, n_genes=5)
sc.pl.rank_genes_groups_heatmap(adata, n_genes=10, use_raw=True, show_gene_labels=True)

# Extract as DataFrame
markers_df = sc.get.rank_genes_groups_df(adata, group=None)
top_markers = markers_df[markers_df["pvals_adj"] < 0.05].groupby("group").head(10)
print(f"Significant markers: {len(top_markers)} genes across {top_markers['group'].nunique()} clusters")
print(top_markers[["group", "names", "logfoldchanges", "pvals_adj"]].head(15))
步骤 8:细胞类型注释和导出

根据经典标记基因分配细胞类型,进行可视化,并保存所有结果。

# Define canonical markers (example: human PBMC)
marker_genes = {
    "CD4 T cells":      ["CD3D", "CD3E", "IL7R"],
    "CD8 T cells":      ["CD8A", "CD8B", "GZMK"],
    "B cells":          ["MS4A1", "CD79A", "CD79B"],
    "NK cells":         ["NKG7", "GNLY", "KLRD1"],
    "CD14+ Monocytes":  ["CD14", "LYZ", "S100A9"],
    "FCGR3A+ Monocytes":["FCGR3A", "MS4A7"],
    "Dendritic cells":  ["FCER1A", "CST3"],
    "Platelets":        ["PPBP", "PF4"],
}

# Dot plot — rows = clusters, columns = markers
sc.pl.dotplot(adata, var_names=marker_genes, groupby="leiden", use_raw=True)

# Map clusters → cell types (adjust based on your dot plot)
cluster_to_celltype = {
    "0": "CD4 T cells",
    "1": "CD14+ Monocytes",
    "2": "B cells",
    "3": "CD8 T cells",
    "4": "NK cells",
    "5": "FCGR3A+ Monocytes",
    "6": "Dendritic cells",
    "7": "Platelets",
}
adata.obs["cell_type"] = adata.obs["leiden"].map(cluster_to_celltype).fillna("Unknown")
sc.pl.umap(adata, color="cell_type", legend_loc="on data", frameon=False)

# Save results
adata.write("results/annotated_data.h5ad", compression="gzip")
adata.obs.to_csv("results/cell_metadata.csv")
markers_df.to_csv("results/marker_genes.csv", index=False)
print(f"Saved: {adata.n_obs} cells with {adata.obs['cell_type'].nunique()} cell types")

关键参数

参数默认值范围 / 选项作用
min_genes (filter_cells)200100-500每个细胞检测到的最少基因数;值越低,保留的细胞越多
min_cells (filter_genes)33-10表达某个基因的最少细胞数;值越高,筛选越严格
pct_counts_mt 阈值205-30线粒体比例上限;取决于组织类型(PBMC 为 5%,肿瘤为 20%)
n_top_genes (HVG)20001000-5000高度可变基因的数量;数量越多,捕捉到的细微变化越多
flavor (HVG)"seurat_v3""seurat"、"seurat_v3"、"cell_ranger"HVG 检测算法
n_pcs (neighbors)4020-50邻居图使用的 PC 数量;请查看肘部图
n_neighbors155-50k-NN 图中的 k;值越低,越强调局部结构
resolution (leiden)0.50.1-2.0聚类粒度;值越高 → 聚类数量越多
method (rank_genes)"wilcoxon""wilcoxon"、"t-test"、"logreg"DE 检验;推荐在发表研究中使用 Wilcoxon
target_sum (normalize)1e41e4-1e5每个细胞的目标文库大小

常见方案

方案:使用 ComBat 进行批次校正

适用场景:多个样本或批次在 UMAP 上呈现明显的批次效应。

# Requires 'batch' column in adata.obs
sc.pp.combat(adata, key="batch")
# Re-run PCA, neighbors, UMAP, clustering after correction
sc.tl.pca(adata, svd_solver="arpack")
sc.pp.neighbors(adata, n_pcs=40)
sc.tl.umap(adata)
sc.pl.umap(adata, color=["batch", "leiden"])
方案:使用 PAGA + Diffusion Pseudotime 推断轨迹

适用场景:细胞呈现发育连续谱,而非离散的聚类。

# PAGA graph abstraction
sc.tl.paga(adata, groups="leiden")
sc.pl.paga(adata, color="leiden", threshold=0.03)

# Reinitialize UMAP with PAGA layout
sc.tl.umap(adata, init_pos="paga")

# Diffusion pseudotime — set root cell
adata.uns["iroot"] = np.flatnonzero(adata.obs["leiden"] == "0")[0]
sc.tl.diffmap(adata)
sc.tl.dpt(adata)
sc.pl.umap(adata, color=["dpt_pseudotime", "leiden"])
方案:绘制适用于发表的图表

适用场景:为论文或演示文稿生成图表。

sc.settings.set_figure_params(dpi=300, frameon=False, figsize=(5, 5))

# UMAP with custom palette
sc.pl.umap(
    adata, color="cell_type",
    palette="Set2",
    legend_loc="on data",
    legend_fontsize=10,
    legend_fontoutline=2,
    title="",
    save="_celltype_publication.pdf",
)

# Stacked violin plot of top markers
flat_markers = [g for genes in marker_genes.values() for g in genes[:2]]
sc.pl.stacked_violin(
    adata, var_names=flat_markers, groupby="cell_type",
    use_raw=True, swap_axes=True,
    save="_markers_violin.pdf",
)
方案:比较不同条件下的差异表达

适用场景:在特定细胞类型中比较处理组与对照组。

# Subset to cell type of interest
adata_t = adata[adata.obs["cell_type"] == "CD4 T cells"].copy()

# DE between conditions
sc.tl.rank_genes_groups(adata_t, groupby="condition", groups=["treated"], reference="control", method="wilcoxon", use_raw=True)
sc.pl.rank_genes_groups_volcano(adata_t, groups=["treated"])

de_results = sc.get.rank_genes_groups_df(adata_t, group="treated")
sig_genes = de_results[de_results["pvals_adj"] < 0.05]
print(f"Significant DE genes: {len(sig_genes)}")

预期输出

  • results/annotated_data.h5ad — 完整的 AnnData,包含嵌入(PCA、UMAP)、聚类标签、细胞类型注释和原始计数层
  • results/cell_metadata.csv — 按细胞排列的表格:条形码、聚类、细胞类型、QC 指标(n_genes、total_counts、pct_mt)
  • results/marker_genes.csv — 每个聚类的 DE 基因:基因名称、对数倍数变化、校正后的 p 值
  • QC 图表:小提琴图(n_genes、total_counts、pct_mt)、散点图
  • 嵌入图表:按聚类、细胞类型、QC 指标、伪时间着色的 UMAP
  • 标记基因图表:点图、热图、经典标记基因的堆叠小提琴图

故障排除

问题原因解决方案
ModuleNotFoundError: leidenalg未安装 Leiden 包pip install leidenalg igraph
PCA 期间出现 MemoryError稠密矩阵超出 RAM 容量使用 sc.pp.pca(adata, chunked=True, chunk_size=20000)
UMAP 显示单个团块过滤过度或 HVG 数量过少放宽 QC 阈值;将 n_top_genes 增加到 3000-5000
聚类过多或过少分辨率不匹配将 resolution 从 0.1 调整到 2.0,并比较 UMAP 图
找不到 KeyError: 'MT-'物种前缀错误小鼠使用 mt-(小写),人类使用 MT-
批次效应主导 UMAP未校正的技术变异应用 ComBat 方案,或使用 Harmony/scVI 进行整合
出现 adata.raw is None 错误子集筛选前忘记保存 raw归一化后、筛选 HVG 子集前,设置 adata.raw = adata
标记基因缺乏特异性分辨率过低,导致细胞类型合并提高分辨率,或使用 logistic_regression 方法
regress_out 运行缓慢细胞数量较大跳过 regress_out;仅使用 sc.pp.scale()(通常已足够)
rank_genes_groups 中出现 ValueError某个聚类中的细胞过少在进行 DE 前移除细胞数少于 10 的聚类:adata = adata[adata.obs.groupby('leiden').filter(lambda x: len(x)>=10).index]

随附资源

此技能包含用于深入查询的参考文件。当主 SKILL.md 需要更多详情时,按需读取这些文件。

references/api_reference.md

按模块(sc.pp.、sc.tl.、sc.pl.*)整理的所有 scanpy 函数速查表,包含完整签名、常用参数和 AnnData 结构参考。适用场景:需要查找特定操作的确切函数名称或参数时。

references/plotting_guide.md

全面的可视化指南:质控图、UMAP 样式设置、标记基因图(点图、热图、堆叠小提琴图)、轨迹图、多面板图、出版级自定义设置和配色方案建议。适用场景:为出版物或演示文稿制作图表时。

references/standard_workflow.md

针对各个流程阶段的详细分步参考,包含决策点、参数选择依据和替代方法(基于 MAD 的过滤、scran 归一化、自动注释)。适用场景:从头开始执行完整分析,且需要比上方“工作流”部分更深入的指导时。

参考资料

评论

支持 Markdown