VCF 格式基础:定义、版本历史与基本结构

大家好,我是老李。在基因组分析这行混了七八年,每天打交道最多的文件格式之一就是 VCF。说实话,刚入行那会儿我以为 VCF 就是个简单的变异表格,直到有一次用错了版本解析器,把 4.2 的文件当 4.1 读,结果 INFO 字段全乱套了——嗯,从那以后我就老老实实把 VCF 的基础啃透了。今天咱们就从头捋一遍:VCF 到底是什么,怎么来的,长什么样。

1. VCF 格式定义

VCF,全称 Variant Call Format,是存储变异位点信息的标准文本格式。它由一千人基因组计划(1000 Genomes Project)提出,现在由 GA4GH 维护。你想想看,一个 VCF 文件就是一张「变异清单」:每个位点一行,包含参考基因组上的位置、碱基变化、质量分数、过滤状态,以及样本的基因型信息。

我个人的习惯是把它理解成「三联单」:元信息说明数据怎么来的,表头定义每一列的含义,数据行就是实际的变异记录。缺了任何一块,你都没法正确解释一个 VCF 文件。

核心要点:VCF 既是存储格式,也是交换格式。你在不同的分析流程之间传递变异信息,通常就靠它。

2. 版本历史与重要里程碑

VCF 版本迭代不算快,但每次升级都挺关键。我整理了一个简表,方便你对照:

版本 发布时间 主要变化
v4.0 2010 初始版本,定义核心字段(CHROM、POS、ID、REF、ALT、QUAL、FILTER、INFO、FORMAT、样本列)
v4.1 2011 增加 FILTER 行必须大写的约定,规范 ALT 中 、 等符号表示
v4.2 2014 引入 、 等高级替代符号,FILTER 支持 ';' 分隔的多个值
v4.3 2019 增加 #CHROM 前允许空行,规范 contig 元信息,支持更灵活的 FORMAT 字段

一个小提醒:现在 99% 的公共数据库和工具都兼容 v4.2 和 v4.3。但如果你手头还有旧版 4.0 的文件,记得用 bcftools 升级一下,千万别直接用老解析器去读。

为什么会分这么多版本?说白了,随着测序技术和分析算法发展,单核苷酸变异(SNV)已经不够用了,结构变异、复杂替换都需要在 VCF 里表达清楚。我曾经在项目里遇到过用 v4.1 的 标记大片段删除,结果入数据库时才发现新版规范已经要求用 加上 INFO/SVLEN 和 INFO/END 字段……那一次花了两天重写转换脚本,教训深刻。

3. 基本结构:从元信息到数据行

一个标准的 VCF 文件分三大部分:元信息行(Meta-information lines)、表头行(Header line)和数据行(Data lines)。我们来看一个实际片段:

##fileformat=VCFv4.2
##FILTER=<ID=PASS,Description="All filters passed">
##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">
#CHROM POS ID REF ALT QUAL FILTER INFO FORMAT sampleA sampleB
chr1  100 . A G 30.5 PASS DP=100 GT:AD 0/0:50,0 0/1:30,20

注意这里的 ## 开头的行就是元信息,用来声明版本号、定义 FILTER、INFO、FORMAT 等字段的元数据。紧接着 #CHROM 开头的行是表头,固定 9 列(CHROM 到 FORMAT),之后每多一个样本就多一列。下面的每一行就是一条变异记录。

3.1 元信息区

元信息行基本遵循 ##key=value 格式。常用的有:

  • ##fileformat= 版本声明,绝对不可缺少
  • ##FILTER= 定义过滤条件
  • ##INFO= 定义 INFO 标签的类型和含义
  • ##FORMAT= 定义每个样本列的格式
  • ##contig= 染色体长度等参考信息

避坑指南:我曾经遇到过一个同事导出的 VCF 没有写 ##fileformat 行,结果 bcftools 默认按 v4.0 解析,导致 FORMAT 字段的 Number=R 被当成固定个数处理,基因型表全部错位。从那以后,我定了个规矩:自己写的生成脚本第一行必须输出版本元信息。

3.2 固定数据列(前9列)

前 9 列是必需的,顺序不可改:

列名 含义 常见问题
CHROM 染色体名称(chr1 或 1) 命名不统一会导致合并 VCF 时出错
POS 1-based 起始位置 与 0-based 的 BED 对比时容易搞混
ID 已知的 dbSNP ID 或自定义标识,无则用 . 很多工具不接受空值,必须用 .
REF 参考等位基因 多碱基替换时大写中间不要有空格
ALT 替代等位基因,多个用逗号分隔 空值表示该位点无变异
QUAL Phred 质量分数 缺失时用 .
FILTER 过滤状态,PASS 或具体过滤名 多个过滤用 ; 连接
INFO 键值对格式,如 DP=100;AF=0.5 Key 需在元信息中定义
FORMAT 样本字段标签,如 GT:AD:DP 顺序与后续样本列对应

3.3 样本列

FORMAT 列之后,每一列对应一个样本,数据格式按照 FORMAT 定义的顺序,用 : 分隔。比如 GT:AD 对应的样本数据可能是 0/1:30,20。

关键:样本索引从第 10 列开始,个数可变。校验一个 VCF 文件是否完整,第一件事就是数表头的列数是否等于 9 + 样本数。

4. 整张图看清 VCF 结构

下面我手画了一张结构图,把上面说的三大部分串起来。你把它当成一张地图,以后看 VCF 就能快速定位。

VCF 文件结构全景 元信息区 (Meta-information) ##fileformat=VCFv4.2 ##INFO=<ID=DP,...> ##FORMAT=<ID=GT,...> ##FILTER=<...> (以 ## 开头,定义字段、版本、过滤条件等) 表头行 (Header Line) #CHROM POS ID REF ALT QUAL FILTER INFO FORMAT 样本1 样本2 ... 数据区 (Data Lines) chr1 100 . A G 30.5 PASS DP=100 GT:AD 0/0:50,0 0/1:30,20 chr1 200 rs123 C T 99 PASS DP=200 GT:AD 0/1:100,50 1/1:0,80 每一行一条变异记录,固定9列 + N个样本列 结构检查要点 ① 元信息行必须以 ## 开头,且第一行必须是 ##fileformat ② 表头行有且仅有一条,以 #CHROM 开头,固定9列 + 样本列 ③ 数据行中每个字段用 TAB 分隔,缺失值用 . 表示 ④ INFO 和 FORMAT 中的 Key 必须已在元信息中定义

5. 接下来你该做什么

了解了基本结构,我建议你动手做三件事:

  1. 拿一个真实的 VCF 文件(比如 1000 Genomes 的公开 VCF),打开看看元信息行、表头、数据行是不是都齐全。
  2. 数一数列数:用 head -1 your.vcf | awk '{print NF}' 看表头有几个字段。
  3. 验证一下:用 bcftools view -H your.vcf | head -3 看看前几条数据行有没有异常(比如行列不对齐、TAB 被空格替换)。

个人小习惯:我每次拿到新的 VCF 文件,第一时间先用 bcftools stats 跑个统计,既能校验格式,又能快速了解数据质量。