跳转至

379_单细胞代谢通路活性评分


一句话说明

代谢通路活性评分就是把"一组代谢酶基因"的表达情况汇成一个分数,判断这条代谢通路在这个细胞里是否活跃。


核心知识点

要点1:为什么需要通路评分

  • 单个代谢基因表达波动大(单细胞dropout),不稳定
  • 通路级别的集体信号更稳健、更有生物学意义
  • 可以比较不同细胞类型之间的代谢状态差异

要点2:主流评分方法

方法思路工具
ssGSEA单样本基因集富集,排序后面积GSVA包/escape包
AUCell基因集在Top表达基因中的AUCAUCell/pySCENIC
Seurat AddModuleScore目标基因均值 - 背景基因均值Seurat内置
scMetabolism整合KEGG/REACTOME代谢数据库scMetabolism包
VISION元数据不添加混杂的评分VISION包

要点3:Seurat AddModuleScore

  • 最简单、最快的方法
  • 计算公式:目标基因集平均表达 - 匹配大小的随机背景基因集平均表达
  • 背景基因:按表达水平分箱,从同一表达箱随机选背景基因(消除表达量偏差)
  • 局限:只考虑基因的平均表达,不考虑基因集内部的协调性

要点4:scMetabolism 代谢专属工具

  • 专门整合 KEGG 代谢通路数据库
  • 内置人类和小鼠代谢通路基因集
  • 基于 IOBR 中的 ssGSEA 计算代谢通路评分

要点5:评分解读注意事项

  • 评分是相对的,不是绝对值(不同数据集之间不可直接比较)
  • 批次效应会影响评分,建议批次校正后再评分
  • 细胞周期也会影响代谢(S期细胞合成代谢旺盛)

实战代码

Seurat AddModuleScore(最快入门)

library(Seurat)     # 单细胞分析
library(ggplot2)

# 定义代谢通路基因集(KEGG糖酵解通路)
glycolysis_genes <- list(
  Glycolysis = c("HK1", "HK2", "GPI", "PFKL", "PFKM", "ALDOA",
                 "ALDOC", "GAPDH", "PGK1", "PGAM1", "ENO1",
                 "PKM", "LDHA", "LDHB")   # 糖酵解关键酶基因
)

# 氧化磷酸化通路基因集
oxphos_genes <- list(
  OXPHOS = c("NDUFA1", "NDUFB1", "SDHB", "UQCRB",
             "COX5A", "ATP5A1", "ATP5B", "ATP5D")  # 线粒体呼吸链复合物基因
)

# 计算糖酵解评分
seurat_obj <- AddModuleScore(
  seurat_obj,
  features = glycolysis_genes,    # 目标基因集列表
  name = "Glycolysis_Score",      # 评分列名前缀
  ctrl = 100,                     # 背景基因数(与目标基因集大小相匹配)
  seed = 42                       # 随机种子(保证可重现)
)
# 注意:实际列名会是 "Glycolysis_Score1"(自动加数字后缀)

# 计算氧化磷酸化评分
seurat_obj <- AddModuleScore(
  seurat_obj,
  features = oxphos_genes,
  name = "OXPHOS_Score",
  ctrl = 100,
  seed = 42
)

# 在 UMAP 上可视化代谢评分
p1 <- FeaturePlot(
  seurat_obj,
  features = "Glycolysis_Score1",    # 注意加数字后缀
  reduction = "umap",
  order = TRUE,                       # 高分细胞显示在前景
  pt.size = 0.5
) + scale_color_gradient2(
  low = "blue", mid = "white", high = "red",
  midpoint = 0   # 0为中点(正值=高于背景,负值=低于背景)
) + ggtitle("糖酵解活性")

p2 <- FeaturePlot(
  seurat_obj,
  features = "OXPHOS_Score1",
  reduction = "umap",
  order = TRUE
) + scale_color_gradient2(low = "blue", mid = "white", high = "red",
                           midpoint = 0) + ggtitle("氧化磷酸化活性")

p1 | p2   # 并排显示

# 按细胞类型比较代谢活性
VlnPlot(
  seurat_obj,
  features = c("Glycolysis_Score1", "OXPHOS_Score1"),
  group.by = "cell_type",    # 按细胞类型分组
  pt.size = 0,               # 不显示点(细胞太多时)
  ncol = 2
)

AUCell 评分(Python)

import scanpy as sc
import pandas as pd
import numpy as np
from pyscenic.aucell import aucell
from pyscenic.utils import load_motifs

# 准备基因集(字典格式)
gene_sets = {
    "Glycolysis": ["HK1", "HK2", "GPI", "PFKL", "PFKM", "ALDOA",
                   "ALDOC", "GAPDH", "PGK1", "PGAM1", "ENO1", "PKM"],
    "TCA_Cycle":  ["CS", "ACON", "IDH1", "IDH2", "OGDH", "SUCLA2",
                   "SDH", "FH", "MDH1", "MDH2"],
    "Fatty_Acid_Oxidation": ["CPT1A", "CPT2", "ACADL", "HADH",
                              "HADHA", "HADHB", "ACAA2"]
}

# 加载已预处理的 AnnData
adata = sc.read_h5ad("preprocessed.h5ad")

# 只保留目标基因在数据中存在的
for pathway, genes in gene_sets.items():
    available = [g for g in genes if g in adata.var_names]
    print(f"{pathway}: {len(available)}/{len(genes)} 个基因在数据中")
    gene_sets[pathway] = available   # 更新为可用基因

# 转换为 AUCell 所需的 Regulon 格式
from ctypes import Union
from pyscenic.transform import df2regulons
import collections

# 简单方法:直接用 decoupler 包(更现代的替代品)
import decoupler as dc   # 安装:pip install decoupler

# 从 PROGENy 或自定义基因集获取通路网络
# 自定义通路基因集(转为DataFrame格式)
net_data = []
for pathway, genes in gene_sets.items():
    for gene in genes:
        net_data.append({"source": pathway, "target": gene, "weight": 1})

net_df = pd.DataFrame(net_data)   # 源(通路)-目标(基因)-权重

# 用 ULM(单变量线性模型)评分
dc.run_ulm(
    mat=adata,              # AnnData 对象
    net=net_df,             # 通路-基因网络
    source="source",        # 通路列名
    target="target",        # 基因列名
    weight="weight",        # 权重列名
    use_raw=False
)
# 结果存储在 adata.obsm["ulm_estimate"]

# 提取评分矩阵
scores_df = pd.DataFrame(
    adata.obsm["ulm_estimate"],
    index=adata.obs_names,
    columns=adata.uns["ulm_estimate_names"]
)
print(scores_df.head())

# 将评分添加到 adata.obs 供可视化
for col in scores_df.columns:
    adata.obs[f"pathway_{col}"] = scores_df[col].values

# 可视化
sc.pl.umap(adata, color=["pathway_Glycolysis", "pathway_TCA_Cycle"],
           cmap="RdBu_r",    # 红蓝配色(正=红/活跃,负=蓝/抑制)
           vmin=-2, vmax=2,
           save="_metabolism_scores.pdf")

面试常问点

  1. AddModuleScore和ssGSEA有什么区别? AddModuleScore用均值差,ssGSEA用排序AUC,ssGSEA统计更严格但计算慢
  2. 为什么代谢评分是负数? 负数表示该基因集的表达低于随机背景,即该通路被抑制(相对于其他基因的平均表达水平)
  3. 如何获取KEGG代谢通路基因集? R中用msigdbr(species="Homo sapiens", category="C2", subcategory="CP:KEGG") 直接下载

速查表

工具方法速度适用场景
AddModuleScore均值差很快快速探索,自定义基因集
AUCellAUC稀疏数据,准确性好
ssGSEA/GSVA排序富集发表级别分析
decoupler多种方法Python流程,现代替代品