肿瘤克隆演化分析¶
一句话概述:肿瘤不是单一细胞群体,而是由多个基因不同的"克隆"组成。通过 PyClone/SciClone 分析突变的等位基因频率来推断克隆结构和演化历史,揭示耐药和转移机制。
核心知识点速查表¶
| 概念 | 白话解释 |
|---|---|
| 克隆(Clone) | 肿瘤中基因组相同的一群细胞 |
| 亚克隆(Subclone) | 在主克隆基础上又发生了新突变的分支 |
| ITH | 肿瘤内异质性(IntraTumor Heterogeneity) |
| VAF | 变异等位基因频率 |
| CCF | 癌细胞比例(Cancer Cell Fraction) |
| 克隆突变(Clonal) | 所有肿瘤细胞都有的突变(CCF≈1) |
| 亚克隆突变(Subclonal) | 只有部分肿瘤细胞有的突变(CCF<1) |
| PyClone-VI | 主流的克隆结构推断工具 |
| 鱼骨图(Fish plot) | 克隆演化的经典可视化 |
一、克隆演化概念(白话版)¶
比喻:肿瘤就像一棵不断分叉的"进化树"。最初一个细胞"变坏"(创始突变),然后这个"坏细胞"的后代又有一些获得了新的突变(亚克隆)。就像达尔文进化——不同的亚克隆在"生存竞争"中,有的被化疗杀死,有的因为耐药突变而"胜出"。
二、分析流程¶
第 1 步:准备输入数据¶
# PyClone-VI 需要的输入:每个突变的 VAF、深度、拷贝数
# ========== Python脚本:准备PyClone输入 ==========
import pandas as pd
# 从 Mutect2 VCF 提取突变信息
def prepare_pyclone_input(vcf_file, cnv_file, sample_name, purity=0.8):
"""
准备 PyClone-VI 输入文件
vcf_file: 过滤后的体细胞突变VCF
cnv_file: CNV分段结果(如CNVkit的.cns文件)
purity: 肿瘤纯度(0~1)
"""
import pysam
vcf = pysam.VariantFile(vcf_file) # 打开VCF
cnv = pd.read_csv(cnv_file, sep="\t") # 读取CNV
records = []
for variant in vcf:
if 'PASS' not in variant.filter: # 跳过未通过过滤的
continue
# 获取肿瘤样本的AD(等位基因深度)
sample = variant.samples[0] # 肿瘤样本
ad = sample['AD'] # 等位基因深度
ref_count = ad[0] # 参考等位基因reads数
alt_count = ad[1] # 变异等位基因reads数
total_depth = ref_count + alt_count
if total_depth < 20: # 深度太低则跳过
continue
# 查找该位点的拷贝数
chrom = variant.chrom
pos = variant.pos
cn_row = cnv[(cnv['chromosome'] == chrom) &
(cnv['start'] <= pos) &
(cnv['end'] >= pos)]
if len(cn_row) > 0:
total_cn = int(round(2 * 2**cn_row.iloc[0]['log2'])) # 总拷贝数
minor_cn = max(0, total_cn // 2 - 1) # 次要等位基因拷贝数
else:
total_cn = 2 # 默认二倍体
minor_cn = 0
records.append({
'mutation_id': f"{chrom}_{pos}", # 突变ID
'sample_id': sample_name, # 样本名
'ref_counts': ref_count, # ref reads
'alt_counts': alt_count, # alt reads
'major_cn': total_cn - minor_cn, # 主要拷贝数
'minor_cn': minor_cn, # 次要拷贝数
'normal_cn': 2, # 正常拷贝数
'tumour_content': purity, # 肿瘤纯度
})
df = pd.DataFrame(records)
df.to_csv(f"pyclone_input_{sample_name}.tsv",
sep="\t", index=False)
print(f"总突变数: {len(df)}")
return df
# 运行
prepare_pyclone_input("filtered_somatic.vcf", "tumor.cns",
"tumor_sample", purity=0.75)
第 2 步:运行 PyClone-VI¶
# 安装 PyClone-VI
pip install pyclone-vi # pip安装
# 运行克隆推断
pyclone-vi fit \
-i pyclone_input_tumor_sample.tsv \ # 输入文件
-o pyclone_results.h5 \ # 输出HDF5文件
-d beta-binomial \ # 分布模型(推荐beta-binomial)
-c 40 \ # 最大聚类数
-r 10 \ # 随机重启次数
--seed 42 # 随机种子
# 提取结果
pyclone-vi write-results-file \
-i pyclone_results.h5 \ # 输入HDF5
-o pyclone_clusters.tsv # 输出聚类结果
# 结果格式:
# mutation_id sample_id cluster_id cellular_prevalence ...
# cluster_id: 克隆编号
# cellular_prevalence: 该突变在肿瘤细胞中的比例(CCF)
第 3 步:多区域/多时间点分析¶
# 如果有多个样本(多区域活检或治疗前后),合并输入
cat pyclone_input_region1.tsv > pyclone_multi.tsv
tail -n +2 pyclone_input_region2.tsv >> pyclone_multi.tsv
tail -n +2 pyclone_input_region3.tsv >> pyclone_multi.tsv
# 运行多样本分析
pyclone-vi fit \
-i pyclone_multi.tsv \
-o pyclone_multi_results.h5 \
-d beta-binomial \
-c 40 -r 10 --seed 42
pyclone-vi write-results-file \
-i pyclone_multi_results.h5 \
-o pyclone_multi_clusters.tsv
第 4 步:克隆演化树推断¶
# ========== R脚本:用 ClonEvol 推断演化树 ==========
# install.packages("clonevol")
# install.packages("fishplot")
library(clonevol) # 加载ClonEvol
# 读取 PyClone 结果
clusters <- read.table("pyclone_clusters.tsv",
header=TRUE, sep="\t") # 读取聚类结果
# 整理为 ClonEvol 格式
# 需要每个克隆在每个样本中的CCF
clone_data <- aggregate(cellular_prevalence ~ cluster_id + sample_id,
data=clusters, FUN=mean) # 每个克隆的平均CCF
# 转为宽格式
library(reshape2)
wide_data <- dcast(clone_data, cluster_id ~ sample_id,
value.var="cellular_prevalence") # 宽格式
# 推断克隆演化树
results <- infer.clonal.models(
variants=wide_data, # 克隆数据
cluster.col.name="cluster_id", # 克隆ID列
sample.names=c("primary", "metastasis"), # 样本名
cancer.initiation.model="monoclonal", # 单克隆起源
subclonal.test="bootstrap", # Bootstrap检验
num.boots=1000 # Bootstrap次数
)
# 画克隆演化树
pdf("clonal_evolution_tree.pdf", width=10, height=8)
plot.clonal.models(results,
box.plot=TRUE, # 箱线图
variants=wide_data,
cluster.col.name="cluster_id",
sample.names=c("primary", "metastasis"))
dev.off()
第 5 步:Fish Plot(鱼骨图)¶
# Fish plot 展示克隆随时间的演化
library(fishplot) # 加载fishplot
# 定义时间点
timepoints <- c(0, 30, 100, 150) # 天数
# 定义克隆的频率变化(每行一个克隆,每列一个时间点)
frac.table <- matrix(
c(100, 80, 30, 60, # 克隆1(创始克隆)
0, 20, 50, 30, # 克隆2(亚克隆A)
0, 0, 20, 40), # 克隆3(亚克隆B,耐药)
ncol=length(timepoints),
byrow=TRUE
)
# 定义克隆的父子关系
parents <- c(0, 1, 1) # 克隆2和3都是克隆1的子代
# 创建 fish 对象
fish <- createFishObject(frac.table, parents,
timepoints=timepoints,
fix.missing.clones=TRUE)
# 设置颜色
fish <- setCol(fish, c("#E41A1C", "#377EB8", "#4DAF4A"))
# 画图
pdf("fish_plot.pdf", width=10, height=5)
fishPlot(fish,
shape="spline", # 平滑曲线
title.btm="Clonal Evolution", # 底部标题
vlines=c(30), # 垂直线(如治疗开始)
vlab=c("Treatment")) # 标签
dev.off()
三、常见报错与解决¶
| 问题 | 原因 | 解决方法 |
|---|---|---|
| PyClone 推断出太多克隆 | 突变数多或参数宽松 | 调低最大聚类数(-c 10~20) |
| CCF > 1 | 拷贝数估计不准 | 检查CNV数据和肿瘤纯度 |
| 克隆树不合理 | CCF估计有误差 | 增加突变数,改用多区域数据 |
| 多区域数据克隆不匹配 | 不同区域突变集不同 | 只使用在所有区域都检测到的突变 |
四、面试高频问题¶
Q1:什么是肿瘤内异质性(ITH),为什么重要?
ITH指肿瘤内部存在基因组不同的亚克隆。重要性:(1)耐药——化疗杀死敏感克隆,耐药亚克隆扩张;(2)转移——某些亚克隆有转移能力;(3)活检偏差——单点活检可能遗漏重要亚克隆。
Q2:PyClone 的原理?
PyClone 使用贝叶斯聚类模型,根据突变的VAF、拷贝数和肿瘤纯度推断每个突变所属的克隆及其CCF。同一克隆的突变应该有相似的CCF。PyClone-VI使用变分推断加速计算。
Q3:克隆突变和亚克隆突变对免疫治疗的影响?
克隆突变(所有肿瘤细胞都有)产生的新抗原是更好的免疫治疗靶点,因为靶向它们可以杀死所有肿瘤细胞。亚克隆突变只存在于部分细胞,靶向它们只能杀死一部分肿瘤。
五、速查表¶
# === 克隆演化分析速查 ===
# PyClone-VI
pyclone-vi fit -i input.tsv -o results.h5 -d beta-binomial -c 40
pyclone-vi write-results-file -i results.h5 -o clusters.tsv
# SciClone(R包替代方案)
# library(sciClone); sc <- sciClone(vafs, copyNumberData, sampleNames)
# ClonEvol(演化树推断)
# library(clonevol); infer.clonal.models(variants, ...)
# fishplot(鱼骨图可视化)
# library(fishplot); fishPlot(fish, shape="spline")
# CCF 解读:
# CCF ≈ 1.0 → 克隆突变(所有肿瘤细胞都有)
# CCF < 0.5 → 亚克隆突变(只有部分细胞有)
# CCF 的分布模式揭示克隆结构
参考资料:PyClone-VI (Gillis et al. 2020, BMC Bioinformatics)、RETCHER (2024, Briefings in Bioinformatics)、ClonEvol、fishplot