跳转至

肿瘤克隆演化分析

一句话概述:肿瘤不是单一细胞群体,而是由多个基因不同的"克隆"组成。通过 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