一个完整的WGS变异检测snakemake流程详解


写在前边的话

  在此要感谢碱基矿工,第一次做全外显子测序的变异检测时便是参考了他写的教程,当时的GATK还是3.8版本,而现在已经是4.1了。使用GATK来做变异检测非常好用,但是在有些步骤是非常耗时的,如果不能用多线程去处理的话,尤其是有多个样本时,就会效率很差。所以才打算写一个snakemake流程化处理,最后会附上一个完整脚本供大家参考。


第一步:建立index以及下载数据库



  Index 使用UCSC数据库的hg38作为reference,使用bwa进行比对。下载hg38数据存为hg38.chrom.fasta, 建立bwa比对index:

$ bwa index -a bwtsw -p hg38.chrom.fasta hg38.chrom.fasta

参数说明:
-p 指定输出index文件名字
第二个hg38.chrom.fasta为当前文件夹下的reference

  另外,还需要一个hg38.chrom.dict文件和一个hg38.chrom.fasta.fai文件, 命令如下:

$ gatk CreateSequenceDictionary -R hg38.chrom.fasta -O hg38.chrom.dict
$ samtools faidx hg38.chrom.fasta

参数说明:
-R 基因组reference文件
-O 输出文件路径
生成文件和输入文件都是在当前目录下

  index基本建立完成,然后是已知突变数据库下载,gatk提供了相关网站bundle可下载相关数据。下载hg38的所有数据到本地:

wget -r ftp://gsapubftp-anonymous@ftp.broadinstitute.org/bundle/hg38/*


第二步:数据处理


1. 去接头

  使用trim_galore进行去接头,自动检测接头序列:

input:
    R1 = '/Volumes/RawData1/WGS/Fastq/{sample}_R1.fastq.gz',
    R2 = '/Volumes/RawData1/WGS/Fastq/{sample}_R2.fastq.gz'
output:
    'Trim/{sample}_R1_val_1.fq.gz',
    'Trim/{sample}_R2_val_2.fq.gz'
shell:
    '''
        trim_galore \
        -q 20 \         
        --length 25 \ 
        --e 0.1 \
        --paired \
        {input.R1} \
        {input.R2} \
        --gzip \
        -o Trim
    '''

2. 序列比对

  BWA 的比对速度实在是太慢了,而hisat2速度相对快一些,而且一样可以用于dna测序比对;比对之后利用samtools进行转换为bam并排序:

input:
    r1 = 'Trim/{sample}_R1_val_1.fq.gz',
    r2 = 'Trim/{sample}_R2_val_2.fq.gz',
    index = index
output:
    bam = 'Mapping/{sample}.sorted.bam',
    sum = 'Mapping/{sample}_aln_sum.txt'
shell:
    '''
        hisat2 \
        -p {threads} \
        -x {input.index}/genome \
        -1 {input.r1} \
        -2 {input.r2} \
        --summary-file {output.sum} | \
        samtools view -Sb -q 30 - | \
        samtools sort -@ {threads} -m 2G -O bam \
        -T Mapping/{wildcards.sample}.tmp -o {output.bam}
    '''

3. 添加头信息

  由于不是用bwa比对,所以没办法在一开始就为reads加上头信息,所以这一步使用gatk去添加:

input:
    'Mapping/{sample}.sorted.bam'
output:
    'Mapping/{sample}.addhead.bam'
shell:
    '''
        gatk AddOrReplaceReadGroups \
        -I {input} \
        -O {output} \
        -LB {wildcards.sample} \
        -PL illumina \ # 测序平台不能乱写,其他随意
        -PU {wildcards.sample} \
        -SM {wildcards.sample} \
        -SO coordinate
    '''

4.去掉pcr重复

  这一步只是把重复reads标记了出来,并没有删除,可以通过更改参数去删除reads:

input:
    'Mapping/{sample}.addhead.bam'
output:
    bam = 'MarkDup/{sample}.markdup.bam',
    met = 'MarkDup/{sample}.metrics.txt'
shell:
    '''
        gatk MarkDuplicates \
        -I {input} \
        -O {output.bam} \
        -M {output.met}
    '''

  做完这一步需要建立一个index,方便后续调用bam文件:

input:
    'MarkDup/{sample}.markdup.bam'
output:
    'MarkDup/{sample}.markdup.bam.bai'
shell:
    'samtools index {input}'

5. 碱基质量校正

  由于比对到SNP或INDEL上的reads附近会有很多错配,为了避免出现过多假阳性,需要对这部分reads进行局部重新比对;这个过程用到了很多已知变异集,即已知的可靠的变异位点,重比对将主要围绕这些位点进行:

# SNP and INDEL datasets
omni='/Volumes/RawData1/WGS/Reference/hg38_snp_datasets/1000G_omni2.5.hg38.vcf.gz'
thg='/Volumes/RawData1/WGS/Reference/hg38_snp_datasets/1000G_phase1.snps.high_confidence.hg38.vcf.gz'
mill='/Volumes/RawData1/WGS/Reference/hg38_snp_datasets/Mills_and_1000G_gold_standard.indels.hg38.vcf.gz'
db146='/Volumes/RawData1/WGS/Reference/hg38_snp_datasets/dbsnp_146.hg38.vcf.gz'
hap='/Volumes/RawData1/WGS/Reference/hg38_snp_datasets/hapmap_3.3.hg38.vcf.gz'
dbsnp='/Volumes/RawData1/WGS/Reference/hg38_snp_datasets/beta/Homo_sapiens_assembly38.dbsnp.vcf.gz'
kn_indel='/Volumes/RawData1/WGS/Reference/hg38_snp_datasets/beta/Homo_sapiens_assembly38.known_indels.vcf.gz'
gold='/Volumes/RawData1/WGS/Reference/hg38_snp_datasets/beta/Homo_sapiens_assembly38.variantEvalGoldStandard.vcf.gz'
  • 第一步是重比对的过程:
input:
    bam = 'MarkDup/{sample}.markdup.bam',
    ref = ref,
    db146 = db146,
    mill = mill,
    thg = thg,
    hap = hap,
    omni = omni,
    kn_indel = kn_indel,
    gold = gold,
    dbsnp = dbsnp
output:
    'BQSR/{sample}.markdup.recal.table'
shell:
    '''
        gatk BaseRecalibrator \
        -R {input.ref} \
        -I {input.bam} \
        --known-sites {input.db146} \
        --known-sites {input.mill} \
        --known-sites {input.thg} \
        --known-sites {input.hap} \
        --known-sites {input.omni} \
        --known-sites {input.kn_indel} \
        --known-sites {input.gold} \
        --known-sites {input.dbsnp} \
        -O {output}
    '''
  • 第二步是把重比对的结果再写入到bam文件中:
input:
    bam = 'MarkDup/{sample}.markdup.bam',
    table = 'BQSR/{sample}.markdup.recal.table',
    ref = ref
output:
    'BQSR/{sample}.BQSR.bam'
shell:
    '''
        gatk ApplyBQSR \
        --bqsr-recal-file {input.table} \
        -R {input.ref} \
        -I {input.bam} \
        -O {output}
    '''
  • 第三步依然是为bam文件建立index:
input:
    'BQSR/{sample}.BQSR.bam'
output:
    'BQSR/{sample}.BQSR.bam.bai'
shell:
    'samtools index {input}''

6. 开始真正的变异calling

  在对reads做了校正之后,就可以拿来做Variant calling 了,这一步只是拿到初始的变异位点,因为后续还有对这些变异位点进行过滤和校正:

input:
    bam = 'BQSR/{sample}.BQSR.bam',
    ref = ref
output:
    'HC/{sample}.HC.vcf.gz'
shell:
    '''
        gatk HaplotypeCaller \
        -R {input.ref} \
        -I {input.bam} \
        -O {output}
    '''

7. 对上一步得到的变异进行过滤

  该过程也分为两步,第一步是根据已知变异集的变异位点信息,利用自己的测序数据建立一个高斯模型,用来区分好的变异位点和坏的变异位点;好的变异位点即为已知变异集相同或相似的位点,坏的变异位点则相反:

  • 首先是SNP筛选


  • 第一步建立模型
input:
    vcf = 'HC/{sample}.HC.vcf.gz',
    ref = ref,
    hap = hap,
    omi = omni,
    thg = thg,
    dbs = db146,
    dbsnp = dbsnp,
    gold = gold
output:
    R = 'VQSR/{sample}.snps.plots.R',
    tr = 'VQSR/{sample}.snps.tranches',
    recal = 'VQSR/${sample}.snps.recal'
shell:
    '''
        gatk VariantRecalibrator \
        -R {input.ref} \
        -V {input.vcf} \
        --resource hapmap,known=false,training=true,truth=true,prior=15.0:{input.hap} \
        --resource omini,known=false,training=true,truth=true,prior=12.0:{input.omi} \
        --resource 1000G,known=false,training=true,truth=false,prior=10.0:{input.thg} \
        --resource dbsnp,known=true,training=false,truth=false,prior=2.0:{input.dbs} \
        --resource dbsnp38,known=true,training=false,truth=false,prior=2.0:{input.dbsnp} \
        --resource gold,known=true,training=false,truth=false,prior=2.0:{input.gold} \
        -an MQ -an MQRankSum -an ReadPosRankSum -an FS -an SOR -an DP \ # 全基因组测序还有一个参数是 -an QD ,外显子测序没有
        -mode SNP \
        -tranche 100.0 -tranche 99.9 -tranche 99.0 -tranche 95.0 -tranche 90.0 \
        --rscript-file {output.R} \
        --tranches-file {output.tr} \
        -O {output.recal}
    '''
  • 第二步筛选变异位点:
input:
    vcf = 'HC/{sample}.HC.vcf.gz',
    ref = ref,
    tr = 'VQSR/{sample}.snps.tranches',
    recal = 'VQSR/${sample}.snps.recal'
output:
    'VQSR/{sample}.snp.vcf'
shell:
    '''
        gatk ApplyVQSR \
        -R {input.ref} \
        -V {input.vcf} \
        --ts-filter-level 99.0 \
        --tranches-file {input.tr} \
        --recal-file {input.recal} \
        -mode SNP \
        -O {output}
    '''

  • 然后是INDEL筛选


  INDEL的筛选是建立在snp筛选基础上的,所以snp筛选用过的变异集在这一步就不再用了。

  • 第一步建立模型
input:
    vcf = 'VQSR/{sample}.snp.vcf',
    ref = ref,
    mill = mill,
    kn_indel = kn_indel
output:
    tr = 'VQSR/{sample}.indel.tranches',
    R = 'VQSR/{sample}.indel.plots.R',
    recal = 'VQSR/${sample}.indel.recal'
shell:
    '''
        gatk VariantRecalibrator \
        -R {input.ref} \
        -V {input.vcf} \
        --resource mills,known=true,training=true,truth=true,prior=12.0:{input.mill} \
        --resource kn_indel,known=true,training=true,truth=true,prior=10.0:{input.kn_indel} \
        -an MQ -an MQRankSum -an ReadPosRankSum -an FS -an SOR -an DP \ # 全基因组测序还有一个参数是 -an QD ,外显子测序没有
        -mode INDEL \
        --max-gaussians 6 \
        --rscript-file {output.R} \
        --tranches-file {output.tr} \
        -O {output.recal}
    '''
  • 第二步筛选变异位点
input:
    vcf = 'VQSR/{sample}.snp.vcf',
    ref = ref,
    tr = 'VQSR/{sample}.indel.tranches',
    recal = 'VQSR/${sample}.indel.recal'
output:
    'VQSR/{sample}.snp.indel.vcf'
shell:
    '''
        gatk ApplyVQSR \
        -R {input.ref} \
        -V {input.vcf} \
        --ts-filter-level 99.0 \
        --tranches-file {input.tr} \
        --recal-file {input.recal} \
        -mode INDEL \
        -O {output}
    '''

8.拆分SNP和INDEL结果

  由于前两步产生的结果都在同一个文件里,所以需要从中把SNP和INDEL分别拆分出来:

input:
    vcf = 'VQSR/{sample}.snp.indel.vcf',
    ref = ref
output:
    snp = 'SNP_INDEL/{sample}.snp.vcf.gz',
    indel = 'SNP_INDEL/{sample}.indel.vcf.gz'
shell:
    '''
        gatk SelectVariants -R {input.ref} -select-type SNP --variant {input.vcf} -O {output.snp}
        gatk SelectVariants -R {input.ref} -select-type INDEL --variant {input.vcf} -O {output.indel}
    '''

9. 选择通过筛选的变异

  在前两步的结果中,通过筛选的变异会加上一个'PASS'的标签,我们通过这个标签可以选择出对应的变异位点:

input:
    snp = 'SNP_INDEL/{sample}.snp.vcf.gz',
    indel = 'SNP_INDEL/{sample}.indel.vcf.gz'
output:
    snp = 'PASS/{sample}.filtered.snp.vcf',
    indel = 'PASS/{sample}.filtered.indel.vcf'
shell:
    '''
        zgrep 'PASS' {input.snp} > {output.snp}
        zgrep 'PASS' {input.indel} > {output.indel}
    '''

10. 对变异位点进行注释

  最后得到的变异位点我们并不能直接看出它的功能等信息,因此需要利用已知的变异库去注释,然后去观察它是否和一些疾病等有关。注释软件我们选择ANNOVAR,因为它下载即可使用,而且提供常用的变异库,使用起来非常方便:

input:
    snp = 'PASS/{sample}.filtered.snp.vcf',
    indel = 'PASS/{sample}.filtered.indel.vcf',
    humandb = humandb,
    xref = xref
output:
    snp = 'ANNOVAR/{sample}.filtered.snp',
    indel = 'ANNOVAR/{sample}.filtered.indel'
shell:
    '''
        table_annovar.pl {input.snp} {input.humandb} -buildver hg38 -out {output.snp} \
        -remove -protocol refGene,cytoBand,exac03,dbnsfp33a,avsnp150,cosmic70,dbscsnv11 \
        -operation gx,r,f,f,f,f,f -nastring . \
        -polish -xref {input.xref}
    

        table_annovar.pl {input.indel} {input.humandb} -buildver hg38 -out {output.indel} \
        -remove -protocol refGene,cytoBand,exac03,dbnsfp33a,avsnp150,cosmic70,dbscsnv11 \
        -operation gx,r,f,f,f,f,f -nastring . \
        -polish -xref {input.xref}
    '''

最后,我们得到的是Tab分割的表格,分别储存着变异位点的位置、序列、相关基因等信息,可以利用这些信息去进行可视化,例如利用IGV去观察或者用circos或cirlize去作图。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容