4. VCF格式详解:头信息、样本列、INFO、FORMAT、过滤等

VCF(Variant Call Format)是生物信息学里最常用的变异格式。说实话,我刚开始接触VCF时也被那密密麻麻的##行搞得头大。但用久了你会发现,这个结构其实很清晰——就像一份带注释的表格,每一块都有明确用途。

核心要点:VCF文件分为三大部分——文件头(##行)、列名行(#CHROM开头)和数据行(每一行一个变异)。理解这三层,整个格式就通了。

4.1 文件头(Header)——元信息大本营

文件头以##开头,记录描述性信息。一个合法的VCF文件必须有##fileformat=VCFv4.2这行。其他常见条目包括:

头信息键作用示例
##fileformat格式版本(必选)VCFv4.2
##source产生VCF的软件GATK4.3.0.0
##reference参考基因组文件路径file:///ref/hg38.fa
##contig染色体信息(长度等)<ID=chr1,length=248956422>
##INFOINFO字段的详细定义<ID=DP,Number=1,Type=Integer,Description="Total Depth">
##FORMATFORMAT字段的详细定义<ID=GT,Number=1,Type=String,Description="Genotype">
##FILTER过滤条件说明<ID=LowQual,Description="Low quality">
##ALT替代等位基因描述(非SNP/INDEL时用)<ID=DEL,Description="Deletion">

嗯,这里要注意:头信息里的##INFO和##FORMAT必须与实际数据行中的标签一一对应。我曾在项目中遇到一个情况:下游工具解析时因为缺少##INFO定义行直接报错退出。排查了半天才发现是头信息不完整。所以写头信息别偷懒,一个都不能少。

我个人习惯是在生成VCF时,先用bcftools head或grep "^##"快速检查头信息完整性。尤其是做pipeline时,头信息的标准化能节省大量debug时间。

4.2 列名行与固定列——表格的标题栏

文件头之后,紧接着是列名行,以#CHROM开头。格式固定:

#CHROM POS ID REF ALT QUAL FILTER INFO FORMAT 样本1 样本2 样本3 ...

每一列的含义如下:

  • CHROM / POS:染色体和位置(1-based)。注意不同参考基因组坐标可能不同。
  • ID:dbSNP rs号或其他唯一标识,若无则用.。
  • REF / ALT:参考等位基因和替代等位基因。ALT可以多个(逗号分隔)。
  • QUAL:变异质量值(Phred scale)。值越高越可靠。
  • FILTER:过滤状态。PASS表示通过,其他值对应头信息中定义的FILTER。
  • INFO:变异级别的聚合信息(键值对,分号分隔)。
  • FORMAT:该行样本字段的格式(冒号分隔的标签名,如GT:DP:GQ)。
  • 样本列:每个样本一个列,值按FORMAT定义顺序用冒号分隔。

你看,这里FORMAT列名本身也是列名行的一部分。我曾经犯过糊涂,以为FORMAT列是数据行的内容,其实是列名。搞清楚这点后,解析样本数据就顺畅多了。

4.3 INFO字段——变异级别的“聚合信息”

INFO列存放的是整个变异位点的统计量,不区分样本。常见的INFO键包括:

  • DP:该位点的总覆盖深度(所有样本合并)。
  • AF:等位基因频率(通常基于等位基因计数)。
  • AC:该ALT等位基因在样本中的出现次数。
  • AN:所有样本在该位点的等位基因总数。
  • MQ:Mapping质量均值。
  • QD:质量值除以深度。

举个例子:

##INFO=<ID=AF,Number=A,Type=Float,Description="Allele Frequency">
...
chr1 100 . A G 1200.5 PASS DP=200;AF=0.15;AC=30;AN=200 GT:DP:GQ 0/1:10:99 0/0:8:95 ...

我在做群体遗传分析时常用INFO中的AF来过滤低频位点。但要注意:不同caller产生的INFO字段名称可能不同,比如GATK用AF,freebayes用AF但含义相同。标准化时最好用bcftools annotate --rename-annots统一命名。

避坑:INFO中的Number项至关重要。Number=A表示每个ALT等位基因对应一个值;Number=R表示每个REF+ALT对应一个值;Number=.表示可变数量。解析时如果忽略Number类型,很容易取错索引。我因为这事debug过整整一天,后来才发现是Number类型搞混了。

4.4 FORMAT与样本列——每个样本的“基因型详情”

FORMAT列定义了每个样本字段的格式,样本列则按此格式写入具体数值。例如:

FORMAT 样本A 样本B
GT:DP:GQ 0/1:15:99 1/1:8:45

常用FORMAT键:

  • GT:基因型(0/0、0/1、1/1等)。0表示REF,1表示第一个ALT,2表示第二个ALT(多等位基因时)。分离符号/表示未phased,|表示phased。
  • DP:该样本在该位点的覆盖深度。
  • GQ:基因型质量(Phred scale)。值高说明基因型可信。
  • PL(或GL):基因型似然值(Phred-scaled),通常三个值对应三种基因型。
  • AD:等位基因深度(REF深度, ALT深度1, ...)。

注意:若样本数据缺失,通常用.或.填充。比如FORMAT是GT:DP,但某样本的DP未知:./.:.。

我建议在解析样本列时,先通过头信息中##FORMAT定义了解每个字段的类型和Number,再按FORAMT顺序分割样本列字符串。用Python的vcfpy或Java的htsjdk能省去很多手工解析的麻烦。

4.5 FILTER列——通过还是淘汰

FILTER列只有两个常见状态:PASS或者具体的过滤原因。如果FILTER列为.或者空,表示未经过滤(很多工具默认不过滤时写.)。

常见过滤原因:

  • LowQual:质量低于阈值。
  • QD:QD值过低(质量/深度)。
  • FS:Fisher检验p值过高。
  • BaseQRankSum:碱基质量秩和检验异常。

我个人习惯是保留所有变异(即使FILTER不是PASS),在后续分析时再按需过滤。因为有些过滤条件在不同项目中并不通用。比如我做过一个病毒变异检测项目,默认的GATK硬过滤会把很多真实低频变异误杀。当时我调整了FILTER阈值,才找回关键位点。

4.6 VCF文件结构总览

下面这张SVG图梳理了VCF文件的整体结构,以及各部分之间的关系:

VCF文件结构总览 文件头(Header) 每行以 ## 开头,包含 fileformat / source / reference / contig / INFO定义 / FORMAT定义 / FILTER定义等 列名行(#CHROM 开头) #CHROM POS ID REF ALT QUAL FILTER INFO FORMAT 样本1 样本2 ... 数据行(每行一个变异) 固定列(9列) CHROM POS ID REF ALT QUAL FILTER INFO FORMAT GT:DP:GQ:PL 样本列(可变数量) 0/1:10:99:100 注意:FORMAT列本身是列名行的一部分, 数据行的样本列按FORMAT顺序填写

4.7 实战要点与常见问题

  1. 强制校验头信息:使用bcftools view -H可以只输出头信息,检查##INFO和##FORMAT是否完整。
  2. 样本顺序:列名行中样本列的顺序必须与##SAMPLE头信息或实际样本列表一致,否则下游工具会乱序。
  3. 缺失值处理:样本列中的.表示缺失,但不同工具对缺失值的容忍度不同。我在合并多个VCF时遇到过因缺失值格式不一致导致合并失败的情况。
  4. 多等位基因:ALT列可包含多个用逗号分隔的等位基因,GT中用0/1、1/2等表示。解析时需注意等位基因索引从1开始。
  5. FILTER=.:很多caller默认输出.而不是PASS。如果后续用bcftools filter请先确认过滤状态。

我最常用的调试命令:bcftools view -h input.vcf | grep -c "^##"统计头信息行数;bcftools query -l input.vcf查看样本列表。这两个命令能快速定位VCF常见问题。

VCF格式看似繁琐,但一旦理解了“头信息→列名→数据行”的层级,以及INFO/FORMAT/样本列的职责分工,你就能轻松驾驭它。在实际项目中,标准化VCF的关键就是确保头信息完整、字段命名一致、样本排列有序。后面章节我将带你亲手做格式转换和清洗,把理论变成可重复的代码。