第四讲:深度对拷贝数变异分析的影响
拷贝数变异(CNV)分析,说白了就是找基因组上多了几份或少了几份的区域。这东西对深度特别敏感——深度不够,信号被噪声淹掉;深度过高,成本直线上升,还得处理一堆假阳性。我刚开始做CNV时,总以为深度越高越好,结果跑一个60x的全基因组,花了三天,最后发现一半的候选CNV都是GC偏好搞出来的鬼。嗯,这件事让我重新思考:到底多深才算够?
深度如何影响CNV的检测能力
CNV检测的核心逻辑很简单:某个区域的read count显著偏离期望值,就是拷贝数异常。但“显著”这个词很关键,它依赖两个因素——区域的绝对深度和背景噪声。我个人的习惯是把深度的影响分成三个维度来讲:
- 灵敏度:深度越高,低比例嵌合体CNV、小片段CNV(< 10kb)越容易被发现。你在30x的数据里,要找到一个2kb的杂合缺失,几乎不可能;但换到100x,信号就冒出来了。
- 特异性:深度不足时,零覆盖区域和低覆盖区域容易被误判为纯合缺失。我曾经接手过一个项目,全基因组平均深度只有8x,结果跑出来的“缺失”位点里,有一半其实只是测序死区。
- 断点精度:深度越均匀,CNV起始和结束的边界就越清晰。当深度低于15x时,大部分工具的断点分辨率会退化到50kb以上,这对临床级分析来说基本是不可用的。
来看一张我常用的对比表,直观感受一下不同深度下的CNV分析表现:
| 平均深度 | 可检测最小片段(杂合缺失) | 断点精度中位数 | 常见工具推荐值 |
|---|---|---|---|
| 10x | ~50 kb | ±200 kb | CNVnator 默认不支持 <15x |
| 30x | ~10 kb | ±50 kb | Control-FREEC 要求 ≥20x |
| 60x | ~3 kb | ±20 kb | GATK-gCNV 推荐 30-60x |
深度不均带来的麻烦
光看平均深度是不够的,你想想看,一个区域50x,旁边区域5x,这种不均匀程度会把CNV分析搞得一塌糊涂。为什么会这样?因为几乎所有的read count类工具都假设覆盖度在基因组上是平滑变化的——如果这个假设不成立,你就得疯狂做GC校正和重复区域屏蔽。
我记得有一次,一个客户送来的数据平均深度40x,我顺手就跑了。结果在着丝粒和端粒附近爆出几百个“CNV”,仔细一看,全是GC含量异常导致的人为信号。后来我加了这两行预处理:
# 用samtools统计每个窗口的GC含量
samtools depth -a sample.bam | awk 'BEGIN{OFS="\t"}{
chr=$1; pos=$2; depth=$3
# 模拟GC校正(实际会用更复杂的模型)
gc = getGC(chr, pos)
corrected_depth = depth / (gc / 0.41)
print chr, pos, corrected_depth
}' > corrected_depth.txt
# 过滤极端GC区域(GC<0.2 或 GC>0.65)
awk '$2>=0.2 && $2<=0.65' gc_bias_regions.bed > valid_regions.bed
这行代码虽简,但在实际项目中至少帮我砍掉了70%的假阳性。记住,深度的均匀度比深度绝对值更重要。如果数据不均匀,就算你堆到100x,CNV结果也是垃圾进垃圾出。
不同工具对深度的要求
我在项目里常用三个工具,它们对深度的脾气完全不同,我总结了一套选择策略:
- CNVnator:基于read depth + GC校正,适合WGS全基因组分析。它要求每个窗口(默认100bp)至少有几条read,低于10x的数据基本跑不动。优点是对芯片和非标准库兼容性好,缺点是慢——60x的30x人基因组要跑8小时。
- Control-FREEC:可以处理WES和WGS,内置了GC和重复区域校正。它对深度的容忍度比CNVnator高一些,最低能到5x,但此时必须开启
-breakPointThreshold参数,否则结果全是碎片。我曾在15x的数据上用过它,效果还行,但要把窗口调到500bp。 - GATK-gCNV:最新版GATK4的CNV模块,本质是隐马尔可夫模型。它要求数据深度不低于20x,且最好做到批次内样本深度一致。如果你有100个样本,深度在25x到35x之间,gCNV的灵敏度是三个里面最高的。但它有个坑——对极端GC区域和重复序列完全放弃,所以结果里会漏掉一些真正的CNV。
来看一个典型的工作流配置,我通常这样写配置文件:
# Control-FREEC 配置文件示例,用于30x WGS
[general]
samtools = /usr/bin/samtools
outputDir = ./freec_output
chrFiles = /ref/hg38/chromosomes
chrLenFile = /ref/hg38/hg38.len
[sample]
mateFile = sample.bam
inputFormat = BAM
mateOrientation = FR
[control]
mateFile = normal.bam
inputFormat = BAM
mateOrientation = FR
[BAF]
makePileup = 1
fastaFile = /ref/hg38/hg38.fa
minCoverage = 20
[CNV]
window = 500
step = 100
breakPointThreshold = 0.8
minCNAlength = 3
maxThreads = 8
缺失值处理的特殊挑战
深度不足直接导致缺失值——那些被标记为“0覆盖”或“低于阈值”的窗口。在CNV分析里,缺失值不是简单的丢弃,它可能意味着真正的纯合缺失(拷贝数为0),也可能是测序死角。我处理缺失值分三步走:
- 区分技术性缺失与生物学缺失:如果某个区域在100个样本里都显示0覆盖,那大概率是测序盲区,直接屏蔽掉。如果一个样本独有的0覆盖,而且临近窗口深度正常,才考虑真缺失。
- 基于邻近窗口插补:用两侧各5个窗口的中位数深度去填补单窗口缺失。这个方法对长度 < 10kb的缺失非常有效,但会丢失小CNV的边界信息。我只有在做群体频率统计时才用这个。
- 硬性过滤:如果缺失窗口连续超过5个(默认窗口500bp,即2.5kb),我就把这个区域标记为“不可靠CNV”,直接排除。因为连续缺失很可能是比对失败或结构性重复导致的。
下面这个bedtools命令是我日常用来屏蔽低深度区域的:
# 屏蔽所有连续10个窗口深度 < 5的区域,窗口200bp
bedtools makewindows -g hg38.genome -w 200 > windows.bed
samtools bedcov windows.bed sample.bam > window_depth.txt
awk '$4 < 5' window_depth.txt | bedtools merge -d 2000 -i - > low_depth_regions.bed
# 在CNV分析前用这个bed文件过滤掉这些区域
结构图:深度对CNV分析的核心影响路径
我把上面的逻辑整理成一张图,方便你快速回顾。这张图重点标出了深度如何通过均匀度和窗口深度两个通道,最终影响CNV结果的可靠性。
最后说个老生常谈的教训——不要一上来就调高深度期望。先拿一个小panel定量跑一下,看看GC偏倚和缺失值的分布,摸清数据脾气,再决定要不要加测。我个人做CNV分析超过五年,踩了最深的坑就是“深度越高越好”这句话。现在每一次项目启动,我都会在终端先跑一句samtools depth -a | awk '{print $3}' | sort | uniq -c,看一眼深度直方图,心里才有底。