第1章:VCF格式详解——结构、字段与规范
做生物信息学的人,每天都得跟VCF打交道。
VCF,全称是Variant Call Format,变异检测的标准输出格式。说白了,它就是描述样本在基因组上“哪里不一样、怎么不一样”的文本文件。
我刚开始接触NGS数据分析那会儿,觉得这就是个普通的表格。直到有一次项目里,因为我忽略了VCF头部的几个关键字段,导致下游注释全乱套……嗯,从那以后,我再也不敢小看这个格式了。这一章,我把我这些年踩过的坑和沉淀下来的经验,一起给你理清楚。
1.1 VCF文件的三段式结构
一个标准的VCF文件,由三部分组成:元数据行(以##开头)、列标题行(以#CHROM开头)、数据行(紧跟在标题行后面)。
我的习惯是,先把头部过一遍,再看数据。为什么呢?因为元数据里藏着你对数据的“解释规则”。
| 区段 | 标记 | 作用 |
|---|---|---|
| 元数据行 | ## | 定义文件版本、INFO/FORMAT字段含义、过滤条件、参考基因组等 |
| 列标题行 | #CHROM | 固定10列核心字段 + 若干样本列 |
| 数据行 | 无 | 逐行记录每个变异的具体信息 |
给你看个完整的VCF头部示例:
##fileformat=VCFv4.2
##fileDate=20250315
##source=GATK4.2.0.0
##reference=hg38
##contig=<ID=chr1,length=248956422>
##INFO=<ID=DP,Number=1,Type=Integer,Description="Total Depth">
##FORMAT=<ID=GT,Number=1,Type=String,Description="Genotype">
##FORMAT=<ID=AD,Number=R,Type=Integer,Description="Allelic depths">
##FILTER=<ID=q30,Description="Quality below 30">
#CHROM POS ID REF ALT QUAL FILTER INFO FORMAT sample_01
核心记忆点:VCF头部中的##INFO和##FORMAT行,定义了数据行里INFO列和FORMAT列到底能解析出哪些子字段。没有这些定义,数据就是一堆数字和字母,毫无意义。
1.2 必修的10列核心字段
每一行数据的前8列是强制信息,加上FORMAT和样本列,组成了VCF的骨架。我挨个解释一下,尤其是那些容易搞混的。
| 列号 | 字段名 | 类型 | 说明 |
|---|---|---|---|
| 1 | CHROM | String | 染色体/contig名称,必须与头部##contig一致 |
| 2 | POS | Integer | 变异在染色体上的起始位置(1-based) |
| 3 | ID | String | 变异标识符,常见的是dbSNP的rs号;无则填. |
| 4 | REF | String | 参考基因组上的等位碱基 |
| 5 | ALT | String | 替代等位碱基,多个用逗号分隔;无变异填. |
| 6 | QUAL | Float | 变异质量评分(Phred scale),越高越可靠 |
| 7 | FILTER | String | 过滤状态:PASS或头部定义的过滤器名称;未过滤填. |
| 8 | INFO | String | 以键值对形式存储变异的附加信息,如DP、AF等 |
| 9 | FORMAT | String | 定义样本列各子字段的分隔顺序,如GT:AD:DP |
| 10+ | 样本列 | String | 每个样本一个列,值与FORMAT定义的字段一一对应 |
这里提一个我踩过的坑。POS是1-based,而很多其他工具用0-based。我曾经在做坐标转换时,忘了这个偏移,结果变异位置全串位了。所以我的习惯是:拿到VCF第一件事,确认所有工具链是否统一使用1-based坐标。
PASS当成过滤好的数据。不对,FILTER列里可能有些行是.qc_fail之类的,筛选的时候必须用bcftools filter -i 'FILTER="PASS"'这类命令。这个细节,我吃过亏。1.3 INFO vs FORMAT:你真的分清了吗?
这是VCF最让人纠结的地方。好多同行问过我,INFO和FORMAT到底啥区别?
INFO:存储的是“这个变异本身的属性”。不管你有几个样本,这个变异的信息是共享的。比如总体深度DP、等位基因频率AF。
FORMAT:存储的是“每个样本在这个位点上的个性化信息”。每个样本的基因型GT、等位基因深度AD、基因型质量GQ,都是按样本单独计算的。
我整理了一个对比表,你一看就明白:
| 特性 | INFO | FORMAT |
|---|---|---|
| 范围 | 位点级别,所有样本共享 | 样本级别,每个样本独自有值 |
| 常见字段 | DP、AF、AC、AN、MQ、MQRankSum | GT、AD、DP、GQ、PL、GL |
| 存储位置 | 第8列(INFO列) | 第9列定义,第10+列填充 |
| 用途 | 全局质量评估、人群频率、注释 | 个体基因型、测序深度、样本质控 |
举个例子,一行典型的VCF数据可能是这样的:
chr1 100 rs12345 A G 50.5 PASS DP=100;AF=0.45 GT:AD:GQ 0/1:30,70:99
这里DP=100;AF=0.45在INFO里,说明该位点整体覆盖深度100,人群频率0.45。而GT:AD:GQ是FORMAT头,对应样本列的值0/1:30,70:99,代表杂合突变,参考等位基因深度30,替代等位基因深度70,基因型质量99。
pysam或者cyvcf2这类库,都内置了这个能力。1.4 常见规范与避坑指南
VCF规范在实际使用中,有不少“潜规则”。我挑几个最要命的跟你唠唠。
- REF和ALT的大小写:必须用大写字母。我见过samtools mpileup输出的vcf给了小写,结果下游注释工具直接报错。你想想看,这种低级错误,排查起来多费劲。
- 多等位基因位点:ALT可以用逗号分隔写多个,比如
A,G。但要注意,样本的GT字段里,如果ALT有两个以上,数字编号1、2分别对应ALT列的顺序。 - ID字段的规范:原则上保持唯一且稳定。有些变异缺失dbSNP ID,就用
.占位。但我在合并多个VCF时,经常遇到相同位点ID不一致的情况。这时候,我个人建议使用bcftools annotate --set-id重新赋予统一的ID。 - VCF必须压缩和索引:正规用法是
bgzip压缩+.tbi或.csi索引。我曾经图省事用gzip压缩,结果很多工具不认,白白浪费半天时间。
1,有的用chr1。做合并或对比时,不改就会匹配不上。我的建议是:在生成VCF阶段,明确指定参考基因组版本,比如--reference hg38.fa,就基本能避免这个问题。1.5 实战验证:读懂一个完整的VCF行
来,咱们一起拆一个真实常见的VCF示例。假设文件内容如下:
##fileformat=VCFv4.2
##FILTER=<ID=PASS,Description="All filters passed">
##INFO=<ID=DP,Number=1,Type=Integer,Description="Total depth">
##INFO=<ID=AF,Number=A,Type=Float,Description="Allele frequency">
##FORMAT=<ID=GT,Number=1,Type=String,Description="Genotype">
##FORMAT=<ID=AD,Number=R,Type=Integer,Description="Allelic depths">
##FORMAT=<ID=GQ,Number=1,Type=Integer,Description="Genotype quality">
#CHROM POS ID REF ALT QUAL FILTER INFO FORMAT sample_01 sample_02
chr1 123456 . T C 60 PASS DP=150;AF=0.33 GT:AD:GQ 0/1:40,110:99 1/1:5,95:85
我来一句句解释:
- 位置:chr1上第123456个碱基,T→C的单核苷酸变异。
- 质量:Phred评分60,错误概率仅为百万分之一。这个值很靠谱。
- FILTER:通过所有过滤。
- INFO:整体深度150,等位基因频率0.33。
- FORMAT定义:每个样本提供GT、AD、GQ三列。
- sample_01:杂合突变(0/1),参考等位基因深度40,突变等位基因深度110,GQ=99。
- sample_02:纯合突变(1/1),参考深度5,突变深度95,GQ=85。
好了,关于VCF格式的基本功,就讲到这里。你记住这三点就行:三段式结构、10列强制字段、INFO和FORMAT各司其职。这些是后续章节所有知识图谱构建的基石。