二、VCF头部信息详解:元数据行、格式定义、样本列

拿到一个VCF文件,第一眼看到的就是那一堆以 ## 打头的行。说白了,头部就是VCF的“身份证”和“说明书”。没有它,下游工具根本不知道你在讲什么。我个人习惯是先扫头部,再看数据,因为很多坑都埋在这里。

2.1 元数据行(##开头)

每个元数据行都是一个键值对,格式固定。最常见的几个,我一个个说。

元数据行标准格式示例作用
##fileformat##fileformat=VCFv4.2版本声明,必须第一行
##INFO##INFO=<ID=AF,Number=A,Type=Float,Description="Allele Frequency">INFO字段的定义
##FILTER##FILTER=<ID=PASS,Description="All filters passed">过滤标签说明
##FORMAT##FORMAT=<ID=GT,Number=1,Type=String,Description="Genotype">每个样本的格式字段定义
##SAMPLE有时出现,指定样本名或分组样本元信息

你可能会问,为什么同一个字段定义要写这么长?举个例子,Number 可以取 A、R、G、. 等值,解析器会据此决定字段长度。我在一个项目中遇到过,有人把 Number=1 写成了 Number=.,结果下游工具以为长度可变,直接抛出异常——排查了快半天。

曾经踩过的坑:
我曾见过一个公共数据库的VCF,把 ##fileformat=VCFv4.1 写成了 ##fileformat=VCF4.1(少了个v),导致多个解析器直接跳过文件。后来还是我手动改了一行才跑通。所以,请严格对照规范,尤其是版本声明那一行。

2.2 格式定义(##FORMAT与FORMAT列)

样本数据的格式定义全在 ##FORMAT 元数据行里。每个样本在 FORMAT 列中的字段顺序必须与元数据行定义的顺序一致。我记得有一次做批次关联校正,因为不同工具生成的 FORMAT 顺序不同——比如有的把 GT 放第一列,有的放第二列——导致后续合并时样本数据全部错位。

常用格式字段一览:

字段全称典型值解释
GTGenotype0/1, 1|1基因型,分隔符表示有无phasing
ADAllele Depth10,5参考等位和替代等位的测序深度
DPRead Depth15总覆盖度
GQGenotype Quality99基因型质量值(Phred scale)
PLPhred-scaled Likelihood0,255,255三种基因型的似然值,常用于Bayesian过滤

大家注意,AD 的个数是 Number=R 还是 Number=A 取决于你用的定义。我曾手写过一个解析脚本,因为没仔细看 Number,直接导致深度统计差了整整一倍。所以,写代码前一定先检查 ##FORMAT 行里的 Number 参数。

实用技巧:
用 bcftools query 可以快速验证样本字段内容。比如查看前5个样本的 GT 和 DP:
bcftools query -f '%CHROM\t%POS[\t%GT\t%DP]\n' input.vcf | head -5

2.3 样本列(SAMPLE)

样本列是VCF中最“活”的部分。从 #CHROM 行开始,第10列及之后全是样本名。每列对应一个样本,列名通常就是样本ID。我经常看到有人把样本名写错一个字母,或者大小写不一致,导致下游关联分析时样本匹配不上。

样本列的结构很简洁:

#CHROM POS ID REF ALT QUAL FILTER INFO FORMAT 样本A 样本B 样本C

FORMAT 列告诉解析器,这些管道分隔的值应该怎么拆。比如 FORMAT 是 GT:AD:DP,那么样本A列的内容可能是 0/1:12,8:20,按冒号拆开后第一个是基因型,第二个是等位深度,第三个是总深度。

个人经验:
有一次我需要把多个VCF按样本合并,结果因为样本列排序不一致,合并后出现了样本与数据错位的灾难。后来我养成了一个习惯:在任何合并操作前,先用 bcftools query -l 列出所有样本,核对顺序。强烈推荐你也这么做。
bcftools query -l input.vcf

最后,来看看VCF头部完整的结构关系。下面这张图帮你一目了然。

VCF头部结构全景 元数据行 (##) ##fileformat=VCFv4.2 ##INFO=<ID=AF,Number=A,Type=Float,Description="Allele Frequency"> ##FILTER=<ID=PASS,Description="All filters passed"> ##FORMAT=<ID=GT,Number=1,Type=String,Description="Genotype"> 列定义行 (#CHROM) #CHROM POS ID REF ALT QUAL FILTER INFO FORMAT 样本A 样本B 样本C 样本数据(每列一个样本) FORMAT列定义:GT:AD:DP 样本A:0/1:12,8:20 样本B:0/0:20,0:20 重要提醒 FORMAT字段顺序 与样本列数据必须一致

嗯,这张图把头部三部分的关系画得很清楚:元数据行定义了所有字段的“出生证明”;#CHROM 行是列名;样本数据则按格式拆解显示。你想想看,如果没有元数据行里的 Number 和 Type,解析器根本不知道 AD 的值有几个逗号、该按什么类型读取,所以头部信息是VCF的灵魂。

再提一个坑:
有些压缩工具或脚本会把 ##FORMAT 行的 ID=GT 写成小写 id=gt。虽然VCF规范对 ## 行内的属性名大小写不敏感,但很多解析器(比如GATK的VCFCodec)仍会按严格匹配来检查。我曾因为这个bug,导致一批分析结果里的 GT 字段全部丢失。所以,尽量保持大写格式,别耍小聪明。