2. 多批次VCF数据准备:收集、版本检查、压缩与索引

好,我们正式开始讲故事。

做多批次VCF一致性分析,最怕什么?最怕数据还没看,工具就开始报错。我见过太多同学,兴致勃勃跑流程,结果第一步就被卡住——VCF格式不对,版本不兼容,甚至文件都没压缩。嗯,这些坑我都踩过,而且不止一次。

这一章,我们就来聊聊怎么把原始数据整理成“可以上战场”的状态。说白了,就是数据准备四步走:收集、版本检查、压缩、索引。每一步我都给你掰开揉碎讲清楚。

多批次VCF数据准备流程 原始VCF收集 版本检查 压缩(bgzip) 索引(tabix) 输出:标准化、可随机访问的 VCF(.gz) + .tbi 索引 关键检查点 • 检查点1:VCF格式版本(v4.1/v4.2/v4.3)— 必需一致 • 检查点2:参考基因组版本(hg19/hg38)— 必需一致 • 检查点3:样本ID是否存在重复 — 必需唯一 • 检查点4:固定ID字段格式校验(CHROM/POS/REF/ALT) • 检查点5:压缩完整性(bgzip vs gzip)— 必须使用bgzip

2.1 收集数据,别做无头苍蝇

很多人觉得收集数据嘛,扔到一个文件夹就行了。我建议你不要这样。我个人的习惯是,在项目启动时就定好一套命名规则。比如:

# 我常用的命名格式
project_batch1_20250301.vcf
project_batch2_20250315.vcf
project_batch3_20250328.vcf

为什么要这么干?你想想看,三个月后你可能手头有30个批次,没有命名规则,光靠文件名猜内容,能把你搞疯。我在一个项目中就看到过同事用“final_v1.vcf”、“final_v2.vcf”、“real_final.vcf”这种命名,结果根本分不清哪个是哪个版本。

我的小技巧: 准备一个文件清单(file manifest),用表格记录下来。这样你不仅知道自己有什么文件,还能一眼看出有没有漏批次。
批次编号 文件名 生成日期 样本数 备注
Batch 1 proj_b1_20250301.vcf 2025-03-01 48 基准批次
Batch 2 proj_b2_20250315.vcf 2025-03-15 52 新增样本
Batch 3 proj_b3_20250328.vcf 2025-03-28 50 重测数据

2.2 版本检查,别掉进格式陷阱

VCF格式有v4.1、v4.2、v4.3三个主要版本。为什么非要检查这个?因为不同版本的关键字段定义不一样啊!我曾经遇到过,两个VCF文件看起来数据都对,结果一合并,QUAL字段全乱了——因为一个是v4.1,一个是v4.2,QUAL的精度定义不同。

怎么检查版本?用grep看文件头就行:

# 查看VCF版本信息
head -5 sample.vcf | grep "##fileformat"

# 输出示例
##fileformat=VCFv4.2
##fileDate=20250301
##source=GATK4.3.0.0
##reference=GRCh38
#CHROM  POS     ID      REF     ALT     QUAL    FILTER  INFO    FORMAT  SAMPLE1
核心检查项:
  • fileformat — 格式版本(必须一致)
  • reference — 参考基因组(hg19 vs hg38混用会出大事)
  • 样本ID — 列名重复?不要有同名样本
  • FORMAT字段 — 定义是否一致(GT、DP、GQ等)

我个人建议写一个脚本自动检查所有待分析VCF的文件头。拿Python写也不复杂:

import gzip

def check_vcf_header(filepath):
    with gzip.open(filepath, 'rt') if filepath.endswith('.gz') else open(filepath, 'r') as f:
        for line in f:
            if line.startswith('##fileformat'):
                print(f"版本: {line.strip()}")
            if line.startswith('##reference'):
                print(f"参考基因组: {line.strip()}")
            if line.startswith('#CHROM'):
                samples = line.strip().split('\t')[9:]
                print(f"样本ID: {samples}")
                break
注意: 不同版本的FORMAT字段定义可能不同。比如v4.2引入的“AD”字段在v4.1中可能不兼容。强制合并之前,一定要先统一版本。

2.3 压缩必须是bgzip,别用错

好,版本确认没问题了,接下来就是压缩。很多同学觉得,压缩嘛,gzip不就行了?嗯,还真不行。

为什么?因为我们要做索引。bgzip和gzip的区别在于,bgzip可以按块压缩,支持随机访问。你在tabix索引的时候,需要能在不拆开整个文件的情况下快速定位到某个区域。gzip做不到这一点,所以VCF工具链(bcftools、GATK)都要求用bgzip。

# 正确的做法 —— 用bgzip
bgzip -c input.vcf > output.vcf.gz

# 或者直接原地压缩(推荐)
bgzip input.vcf
# 会生成 input.vcf.gz
我的习惯: 压缩之后再解压检查一次,确保压缩没出错。用 gunzip -t output.vcf.gz 就能测完整性。

还有一个坑:有些同学用Python的 gzip.open() 来写压缩文件,这个方法默认会生成gzip格式,不是bgzip格式。虽然文件名都是 .gz,但是BCFtools是不认的!

所以,老老实实用 bgzip 命令行工具来完成压缩。如果你非要程序里处理,可以用 pysam.bgzip:

import pysam

# pysam自带的bgzip压缩
pysam.tabix_compress("input.vcf", "output.vcf.gz")

2.4 索引,让工具能“找到”数据

压缩之后必须做索引。索引的作用是什么?说白了,就是让工具能快速定位到基因组上的某个位置,而不需要把整个VCF都读到内存里。你需要同时查第1号染色体和第22号染色体?没有索引的话,工具就得从头扫两遍。有了索引,几毫秒就定位到了。

索引使用 tabix 工具:

# 对bgzip压缩后的VCF做索引
tabix -p vcf input.vcf.gz

# 完成后会生成 input.vcf.gz.tbi 文件
# 查看索引内容
tabix -l input.vcf.gz
索引命令详解:
  • -p vcf — 指定输入格式是VCF(也可以是bcf、gff、bed等)
  • 输出文件 .tbi 必须和 .vcf.gz 放在同一目录
  • 如果VCF文件非常大(比如超过500M),可以考虑用 --csi 参数生成CSI索引
  • 批量索引时,使用 for f in *.vcf.gz; do tabix -p vcf $f; done

我个人的经验:索引之后,一定要验证能不能正常读取。随便挑一个位置试一下:

# 验证索引有效
tabix input.vcf.gz chr1:1000000-2000000 | head -5

如果输出正常,说明索引生效了。如果报错说“index not found”或者“file is not bgzip compressed”,那就是前两步错了,回头检查。

2.5 统一数据管理的一站式方案

到现在为止,每个VCF都已经经过了收集、版本检查、压缩、索引四道工序。项目进行到这里,我建议你做一个数据管理脚本,把上述步骤全串起来。这是我当年做大项目时写的一个简化版脚本:

#!/bin/bash

# 多批次VCF数据准备一键脚本(简化版)
INPUT_DIR="./raw_vcf"
OUTPUT_DIR="./prepared_vcf"

mkdir -p $OUTPUT_DIR

for vcf in $INPUT_DIR/*.vcf; do
    echo "=== 处理文件: $vcf ==="

    # 1. 版本检查
    head -1 $vcf | grep -q "^##fileformat=VCFv4.2"
    if [ $? -ne 0 ]; then
        echo "警告: $vcf 不是VCFv4.2格式"
        continue
    fi

    # 2. 检查参照基因组
    head -5 $vcf | grep -q "GRCh38"
    if [ $? -ne 0 ]; then
        echo "警告: $vcf 不是GRCh38参考基因组"
        continue
    fi

    # 3. 检查样本ID
    SAMPLE_COUNT=$(head -1 $vcf | awk -F'\t' '{print NF-9}')
    echo "样本数: $SAMPLE_COUNT"

    # 4. bgzip压缩
    bgzip -c $vcf > $OUTPUT_DIR/$(basename $vcf).gz

    # 5. 创建索引
    tabix -p vcf $OUTPUT_DIR/$(basename $vcf).gz

    # 6. 验证
    tabix -l $OUTPUT_DIR/$(basename $vcf).gz | head -3
    echo "=== 完成: $vcf ==="
done
进阶技巧: 如果你的企业环境不允许装bgzip/tabix,可以用conda管理环境:conda install -c bioconda bcftools tabix。这样既能隔离环境,又能保证版本一致性。

2.6 一个真实的踩坑记录

最后分享一个我经历过的真实案例。有一次,我从合作方拿到了5个批次的VCF文件,对方保证说“格式都处理好啦”。我没多想,直接做了一致性分析。结果跑了两天才发现,第二个批次的样本ID里有空格,第四个批次的QUAL精度不对,第五个批次的索引文件是旧的。

从那次以后,我发誓:数据到手后,第一步必做版本检查,第二步必重新压缩和索引,绝不做“拿来主义”。

你想想看,一个一致性分析流程,如果数据本身都有问题,结果怎么可能可靠?所以,数据准备这一关,值得你花200%的精力去过。