VCF文件组成:三部分结构,一个都不能少
拿到一个VCF文件,第一件事是什么?别急着看数据。我每次都会先扫一眼文件头,确认它是不是“完整”的。
一个标准的VCF文件,说白了就是三块内容拼起来的:元信息行、头部列定义、以及数据行。这三者缺一不可,顺序也不能乱。我见过不少新手把元信息行放错位置,结果工具直接报错退出了。
元信息行:文件的“身份证”
元信息行,也叫注释行。每行以 ## 开头。它们描述了文件的格式版本、数据来源、过滤条件、样本信息等等。你想想看,如果没有这些信息,其他人拿到文件,根本不知道数据是怎么生成的。
常见的元信息行长这样:
##fileformat=VCFv4.2
##fileDate=20250315
##source=myVarScan_v1.0
##reference=GRCh38
##INFO=<ID=DP,Number=1,Type=Integer,Description="Total Depth">
##FILTER=<ID=PASS,Description="All filters passed">
##FORMAT=<ID=GT,Number=1,Type=String,Description="Genotype">
##FORMAT=<ID=AD,Number=R,Type=Integer,Description="Allelic depths">
##FORMAT=<ID=DP,Number=1,Type=Integer,Description="Read Depth">
##ALT=<ID=DEL:ME:ALU,Description="Deletion of ALU element">
为什么这个重要?因为很多下游工具在解析VCF时,会先读元信息行。比如 ##INFO 定义了后面数据行里 INFO 字段的键值对含义。如果定义和实际数据对不上,轻则解析失败,重则基因型调用错误。
##fileformat 和 ##reference。确认版本号和参考基因组是否和项目一致。很多匹配问题,根源就在这里。
头部列定义:数据的“表头”
元信息行之后,紧跟着一行以 # 开头(注意只有一个 #)的头部列定义。这行定义了后面数据行每一列的含义。
#CHROM POS ID REF ALT QUAL FILTER INFO FORMAT sample1 sample2
固定列有8个:CHROM、POS、ID、REF、ALT、QUAL、FILTER、INFO。然后是可选的 FORMAT 列,再后面是样本列。我刚开始做生信时,总记不住 FORMAT 和 INFO 的区别——后来想通了,INFO 是关于这个位点的,FORMAT 是描述样本数据的格式。
这里有一个容易踩的坑:样本列的顺序。不同VCF文件可能有不同的样本顺序。如果你写脚本按列号硬编码,遇到样本顺序不同的文件就会出错。我建议用样本名来索引,而不是列号。比如用Python的 samples = header['sample1'] 而不是 sample_col = 9。
数据行:真正的变异信息
头部列定义之后,就是数据行。每行代表一个位点上的变异信息。大家看个例子:
chr1 123456 . A G 100 PASS DP=50;AF=0.5 GT:AD:DP 0/1:25,25:50
chr1 123789 rs12345 T C 99.5 q10 DP=30;AF=0.3 GT:AD:DP 1/1:0,30:30
数据行有9个固定字段,后面跟着样本数据。每个字段用制表符分隔。我一开始写解析脚本时,以为用空格也能凑合——结果被教训了。VCF的字段分隔符必须是制表符,这是硬性规定,没得商量。
重点说说 INFO 和 FORMAT 字段。很多人搞混:
INFO:位点级别的信息,用分号分隔的键值对。比如DP=50表示这个位点的总深度是50。FORMAT:样本级别的格式描述,用冒号分隔。比如GT:AD:DP,表示样本数据第一个字段是基因型,第二个是等位基因深度,第三个是样本深度。
我举个例子:
##FORMAT=<ID=GT,Number=1,Type=String,Description="Genotype">
##FORMAT=<ID=AD,Number=R,Type=Integer,Description="Allelic depths">
##FORMAT=<ID=DP,Number=1,Type=Integer,Description="Read Depth">
#CHROM POS ID REF ALT QUAL FILTER INFO FORMAT sample1
chr1 123456 . A G 100 PASS DP=50 GT:AD:DP 0/1:25,25:50
你看,FORMAT 定义了 GT:AD:DP,所以样本数据 0/1:25,25:50 就对应:基因型是杂合,参考等位基因深度25,变异等位基因深度25,样本总深度50。这是一个很典型的配对结构。
三部分结构的校验要点
我在项目中遇到过不少文件,元信息行和头部列定义都有,但数据行格式不对。比如 FORMAT 定义了3个字段,但样本数据只给了2个值——这类错误很隐蔽,常规解析器不一定报错。所以我做校验时,会重点检查以下几点:
我曾经踩过的坑: 有一次,一个VCF文件里 ##FORMAT 定义了 GT:AD:DP,但数据行样本数据只写了 0/1:25,漏了 DP。结果下游分析把 AD 当成了 AD:DP 拼接的字符串,后续计算全部混乱。
建立一个检查清单:
| 检查项目 | 预期值 | 常见问题 |
|---|---|---|
##fileformat |
VCFv4.2 或更高 | 旧版本字段解析可能不一致 |
| 固定列数 | 至少8列 | 缺少 FORMAT 或样本列时,列数可能少于9 |
| 字段分隔符 | 制表符 | 误用空格或逗号 |
FORMAT 字段数 |
等于样本数据冒号分隔数 | 字段数不匹配,或顺序与定义不一致 |
INFO 键值对 |
与 ##INFO 定义一致 |
未定义的键、类型不匹配 |
样本列数与 FORMAT 后的列数 |
一个样本对应一列 | 列数不等于样本数+9 |
说白了,VCF文件的三部分结构,就像房子的地基、框架和墙体——少一层都立不住。校验时别只看数据行,元信息行和头部列定义才是决定文件可用的关键。
一句话总结: 元信息行做声明,头部列做锚定,数据行做承载。这三块对上了,文件才能拿来用。
记住,校验VCF文件,不是简单地检查文件后缀名。我建议写一个快速脚本,逐行检查元信息行数量、头部列定义格式、数据行字段数。只要这三块结构没有问题,文件就成功了一半。