746. 单细胞CNV推断InferCNV¶
一句话概述:从单细胞RNA-seq数据中推断每个细胞的拷贝数变异(染色体上某些区域的基因多了或少了)——就像从"表达量的异常"反推"DNA数量的异常",用来区分肿瘤细胞和正常细胞。
核心知识点速查表¶
| 概念 | 白话解释 | 关键工具 |
|---|---|---|
| CNV | 拷贝数变异,DNA某段区域多拷贝或缺失 | InferCNV, CopyKAT |
| 扩增(Gain) | 某段染色体多了 → 上面的基因表达升高 | 红色区域 |
| 缺失(Loss) | 某段染色体少了 → 上面的基因表达降低 | 蓝色区域 |
| 参考细胞 | 已知的正常细胞,作为基线对比 | 必须提供 |
| 移动平均 | 对染色体上连续基因的表达取滑动窗口平均 | 消除噪声 |
| CopyKAT | 替代工具,自动区分肿瘤/正常 | R包 |
一、原理(白话版)¶
1.1 核心思路¶
正常的CNV检测:用DNA测序 → 直接看覆盖度 → 覆盖度高=扩增,低=缺失
InferCNV的思路:没有DNA数据 → 用RNA表达间接推断
逻辑链:
如果染色体7上的一大段区域的基因表达都偏高
→ 不太可能是所有基因碰巧都上调
→ 更可能是这段DNA本身多了一份(扩增)
→ 推断:这个细胞在chr7上有CNV gain
关键前提:
需要"正常细胞"作为参考(baseline)
异常 = 目标细胞 - 正常细胞的平均值
1.2 InferCNV的工作流程¶
1. 将基因按染色体位置排序
2. 对每个细胞,计算每个基因相对于参考细胞的表达偏差
3. 沿染色体滑动窗口平均(消除单基因噪声)
4. 用HMM或BayesNet进一步降噪
5. 输出CNV热图和分类结果
二、InferCNV分析流程¶
2.1 安装¶
# 安装InferCNV
if (!requireNamespace("BiocManager", quietly=TRUE))
install.packages("BiocManager")
BiocManager::install("infercnv") # 安装InferCNV
# 验证
library(infercnv)
packageVersion("infercnv")
2.2 准备输入文件¶
# ===== InferCNV需要三个输入文件 =====
# 1. 表达矩阵(原始counts,不是归一化后的)
# 从Seurat提取:
library(Seurat)
seurat_obj <- readRDS("seurat.rds")
counts <- GetAssayData(seurat_obj, layer="counts") # 原始计数矩阵
write.table(as.matrix(counts), "counts_matrix.tsv",
sep="\t", quote=FALSE) # 保存为TSV
# 2. 细胞注释文件(每个细胞属于什么类型)
# 格式:细胞barcode\t分类
annotations <- data.frame(
row.names = colnames(seurat_obj), # 细胞barcode
cell_type = seurat_obj$cell_type # 细胞类型
)
write.table(annotations, "cell_annotations.tsv",
sep="\t", col.names=FALSE, quote=FALSE)
# 3. 基因坐标文件(每个基因在染色体上的位置)
# 格式:基因名\t染色体\t起始位置\t结束位置
# InferCNV提供了预制文件,也可以从GTF生成
# 下载预制文件:
# https://data.broadinstitute.org/Trinity/CTAT/cnv/
# 从GTF文件生成基因坐标文件
# 提取基因坐标(简化版)
grep -P "^chr" genes.gtf | \
awk '$3=="gene"' | \
grep 'gene_name' | \
sed 's/.*gene_name "\([^"]*\)".*/\1/' | \
paste - <(grep -P "^chr" genes.gtf | awk '$3=="gene" {print $1, $4, $5}') | \
sort -k2,2V -k3,3n > gene_ordering_file.tsv
2.3 运行InferCNV¶
# ===== 创建InferCNV对象并运行 =====
library(infercnv)
# 创建InferCNV对象
infercnv_obj <- CreateInfercnvObject(
raw_counts_matrix = "counts_matrix.tsv", # 原始计数矩阵
annotations_file = "cell_annotations.tsv", # 细胞注释
gene_order_file = "gene_ordering_file.tsv", # 基因坐标
ref_group_names = c("Normal_Epithelial", "Fibroblast", "Endothelial")
# 参考细胞类型(已知的正常细胞!)
)
# 运行InferCNV分析
infercnv_obj <- infercnv::run(
infercnv_obj,
cutoff = 0.1, # 基因过滤:至少在cutoff比例的参考细胞中表达
out_dir = "infercnv_output/", # 输出目录
cluster_by_groups = TRUE, # 按注释分组聚类
denoise = TRUE, # 去噪(推荐开启)
HMM = TRUE, # 使用隐马尔可夫模型推断CNV状态
num_threads = 8, # 线程数
no_prelim_plot = FALSE, # 生成中间结果图
analysis_mode = "subclusters" # 分析模式:subclusters更敏感
)
# 输出文件
# infercnv_output/
# ├── infercnv.png # CNV热图(核心结果!)
# ├── infercnv.observations.txt # 观测细胞的CNV值
# ├── infercnv.references.txt # 参考细胞的CNV值
# ├── HMM_CNV_predictions.*.txt # HMM预测的CNV区域
# └── run.final.infercnv_obj # R对象(可继续分析)
2.4 结果解读与可视化¶
# ===== 结果解读 =====
# 读取HMM预测结果
hmm_results <- read.table(
"infercnv_output/HMM_CNV_predictions.HMMi6.leiden.hmm_mode-subclusters.Pnorm_0.5.repr_intensities.observations.txt",
header=TRUE, sep="\t"
)
# CNV状态编码:
# 0.5 = 纯合缺失 (complete loss)
# 1 = 杂合缺失 (loss of one copy)
# 1.5 = 正常 (neutral)
# 2 = 单倍增 (gain of one copy)
# 3 = 扩增 (amplification)
# 统计每个细胞的CNV负荷
cnv_score <- apply(hmm_results, 1, function(x) {
sum(abs(x - 1.5)) # 偏离正常值(1.5)的总量
})
# 将CNV score添加回Seurat对象
seurat_obj$cnv_score <- cnv_score[match(
colnames(seurat_obj), names(cnv_score)
)]
# 绘制CNV score在UMAP上的分布
library(ggplot2)
FeaturePlot(seurat_obj, features="cnv_score",
cols=c("gray90", "red")) +
ggtitle("CNV Burden Score")
ggsave("cnv_score_umap.png", width=8, height=6, dpi=300)
# 区分肿瘤vs正常细胞(基于CNV score阈值)
seurat_obj$is_tumor <- ifelse(
seurat_obj$cnv_score > quantile(cnv_score, 0.75, na.rm=TRUE),
"Tumor", "Normal"
)
三、替代工具:CopyKAT¶
# ===== CopyKAT:自动区分肿瘤/正常 =====
# CopyKAT的优势:不需要预先指定参考细胞
# 安装
devtools::install_github("navinlabcode/copykat")
library(copykat)
# 运行CopyKAT
copykat_result <- copykat(
rawmat = as.matrix(counts), # 原始计数矩阵
id.type = "S", # S=symbol, E=ensemble
ngene.chr = 5, # 每条染色体至少5个基因
win.size = 25, # 滑动窗口大小
KS.cut = 0.1, # KS检验阈值
sam.name = "sample1", # 样本名
n.cores = 8 # CPU数
)
# 结果
head(copykat_result$prediction)
# cell.names copykat.pred
# AAACCTGA aneuploid ← 肿瘤(异倍体)
# AAACCTGC diploid ← 正常(二倍体)
# AAACCTGG not.defined ← 不确定
# 将预测结果添加到Seurat
seurat_obj$copykat_pred <- copykat_result$prediction$copykat.pred[
match(colnames(seurat_obj), copykat_result$prediction$cell.names)
]
四、常见报错与解决¶
| 报错信息 | 原因 | 解决方案 |
|---|---|---|
Gene ordering file error | 基因名不匹配 | 确保基因名格式一致(HUGO symbols) |
No reference cells | 参考组名字拼写错误 | 检查ref_group_names与注释文件的一致性 |
HMM: convergence error | 数据太稀疏 | 降低cutoff值或增加min.cells |
Memory error | 细胞数太多 | 下采样到1万细胞以内 |
CopyKAT: all diploid | CNV太轻微检测不到 | 降低KS.cut阈值或用InferCNV的HMM模式 |
Empty chromosome | 该染色体上基因太少 | 过滤掉基因数<5的染色体 |
五、面试高频问题¶
Q1: InferCNV推断CNV的原理?¶
A: 将基因按染色体位置排列,计算目标细胞相对于正常参考细胞的表达偏差。如果染色体某个区域的基因系统性偏高/偏低,推断该区域发生了CNV扩增/缺失。使用滑动窗口平均和HMM去噪。
Q2: 参考细胞的选择很重要吗?¶
A: 非常重要。参考细胞应该是已知的正常细胞(如成纤维细胞、内皮细胞)。如果参考细胞本身有CNV,会导致假阴性。如果参考细胞选错(混入肿瘤细胞),结果会严重偏差。
Q3: InferCNV和基于DNA的CNV检测比较?¶
A: InferCNV从RNA间接推断,分辨率低于DNA方法(只能看到大片段CNV,>5Mb),假阳性率更高。但优势是不需要额外的DNA测序,可以在单细胞水平区分肿瘤亚群。
六、速查表¶
# ===== InferCNV速查 =====
# 安装
BiocManager::install("infercnv")
# 三个输入文件
# 1. counts_matrix.tsv(原始counts)
# 2. cell_annotations.tsv(细胞类型)
# 3. gene_ordering_file.tsv(基因染色体坐标)
# 运行
obj <- CreateInfercnvObject(raw_counts, annotations, gene_order,
ref_group_names=c("Normal"))
obj <- infercnv::run(obj, cutoff=0.1, out_dir="output/",
denoise=TRUE, HMM=TRUE, num_threads=8)
# CNV状态
# 0.5=纯合缺失 1=杂合缺失 1.5=正常 2=单倍增 3=扩增
# CopyKAT(无需参考细胞)
# devtools::install_github("navinlabcode/copykat")
# result <- copykat(rawmat, id.type="S", n.cores=8)
# result$prediction → aneuploid/diploid