二、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 放第一列,有的放第二列——导致后续合并时样本数据全部错位。
常用格式字段一览:
| 字段 | 全称 | 典型值 | 解释 |
|---|---|---|---|
GT | Genotype | 0/1, 1|1 | 基因型,分隔符表示有无phasing |
AD | Allele Depth | 10,5 | 参考等位和替代等位的测序深度 |
DP | Read Depth | 15 | 总覆盖度 |
GQ | Genotype Quality | 99 | 基因型质量值(Phred scale) |
PL | Phred-scaled Likelihood | 0,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头部完整的结构关系。下面这张图帮你一目了然。
嗯,这张图把头部三部分的关系画得很清楚:元数据行定义了所有字段的“出生证明”;#CHROM 行是列名;样本数据则按格式拆解显示。你想想看,如果没有元数据行里的 Number 和 Type,解析器根本不知道 AD 的值有几个逗号、该按什么类型读取,所以头部信息是VCF的灵魂。
有些压缩工具或脚本会把
##FORMAT 行的 ID=GT 写成小写 id=gt。虽然VCF规范对 ## 行内的属性名大小写不敏感,但很多解析器(比如GATK的VCFCodec)仍会按严格匹配来检查。我曾因为这个bug,导致一批分析结果里的 GT 字段全部丢失。所以,尽量保持大写格式,别耍小聪明。