bam文件转fq.gz文件

1、fq文件格式

  fastq格式是一种包含质量值的序列文件,一般用来存储原始测序数据。下面是fastq格式常见的序列格式。

@FCD056DACXX:3:1101:2163:1959#TCGCCGTG/1
TCCGATAACGCTCAACCAGAGGGCTGCCAGCTCCGATCGGCAGTTGCAACCCATTGGCCGTCTGAGCCAGCAACCCCGGA
+
gggiiiiiiiiiiiiiiiiiiiiiiiiiigggggeeecccccc^bcbcccccccbccccc]aaccbbccc^R^^acccc_
@FCD056DACXX:3:1101:2194:1984#TCGCCGTG/1
AGACGACGACTTCGTTTCCCGCCGCGAGTTGCGCCATGATCGCGGTGTGCAGATTCGTTACGCCCTGGGCCACGGAGACG
+
gggiihiiiiiiiihiiiiiiiiiigeccccccccccccccccccaccccdcccccccccccacc_accccccccccV^^

  第一行:以‘@’开头,是这一条read的名字,这个字符串是根据测序时的状态信息转换过来的,中间不会有空格,它是每一条read的唯一标识符,同一份FASTQ文件中不会重复出现,甚至不同的FASTQ文件里也不会有重复;
  第二行:测序read的序列,由A,C,G,T和N这五种字母构成,这也是我们真正关心的DNA序列,N代表的是测序时那些无法被识别出来的碱基;
  第三行:以‘+’开头,在旧版的FASTQ文件中会直接重复第一行的信息,但现在一般什么也不加(节省存储空间);
  第四行:测序read的质量值,这个和第二行的碱基信息一样重要,它描述的是每个测序碱基的可靠程度,用ASCII码表示。
 

2、bam文件格式

  bam、sam文件内容其实是一样的,只是bam是二进制的压缩文件,需要通过特定的软件来进行查看。
  bam文件通常分为header section(头部分,注释信息,以@开头,可有可无)和alignment section(比对结果)两个部分。比对结果由12个字段组成。
示例如下:

F300000235L1C002R0530799573_TGCTTCCTACCA    1187    chr13   32968914    60  82M9D11M    =   32969045    224 GCCTCATATGTTAATTGCTGCAAGCAACCTCCAGTGGCGACCAGAATCCAAATCAGGCCTTCTTACTTTATTTGCTGGAGATTTTTCTGCTAG   A/B7>FDCFDEFDBFEBDFCAEFEAAACAECEDE@B@=B?5BDF1D;:EFCE5ADE>BEE;CCDF=FF=FEBEBEEE=EE@D@AC=<DCACDB   X0:i:1  X1:i:0  MD:Z:82^TTTTCTGTG11 PG:Z:MarkDuplicates RG:Z:case   XG:i:9  DI:i:6783411    AM:i:37 NM:i:9  SM:i:37 XM:i:2  XO:i:1  MQ:i:60 DS:i:2  XT:A:U

其中:
  (1)F3*_TGCTTCCTACCA:序列的名字,也就是reads的名称
  (2)118: FLAG值,是一个标记的数字,均可以转换为2的n次方。这种特定能保证它们数字加和是唯一的。
  可以使用 samtools flags 1147 了解flag值由哪些数字组成,或者到网站了解详细含义:http://broadinstitute.github.io/picard/explain-flags.html。

1:序列是一对序列中的一个;
2:比对结果是一个pair-end比对的末端;
4:没有找到位点;
8:这个序列是pair中的一个但是没有找到位点;
16:在这个比对上的位点,序列与参考序列反向互补;
32: 这个序列在pair-end中的的mate序列与参考序列反响互补
64:序列是 mate 1;
128:序列是 mate 2;

  假如说标记不是以上列举出的数字,比如说83=(64+16+2+1),其实就是这几种情况值和,由二进制混合来表示。

  (3) chr13:参考序列的名字
  (4)32968914:在参考序列上的位置
  (5) 60:mapping qulity 越高则位点越独特,比对的质量值。
      比对的质量值 0-60 依次表示 未比对上-最高比对值,255表示结果不可用。
  (6)85M9D8M:CIGAR 值
  表示reads的比对状态,M表示匹配,I插入,D缺失,N可变剪切,S表示替换,H表示切除,例如这里就是这条reads前面85个碱基比对上了,然后中间9个缺失,后面8个又比对上了;
  更详细的可以见生物信息学100个基础问题 —— 第19题 SAM/BAM中的CIGAR值

  Soft Clip,是指虽然比对不到基因组,但是还是存在于SEQ (segment SEQuence)中的序列,此时CIGAR列对应的S(Soft)的符号。直白点说,就是虽然比对不上参考基因组,但是在BAM/SAM文件中的reads上还是存在的序列(并没有被截断扔掉的序列)

  Hard Clip,同样的,就表示比对不上并且不会存在于SAM/BAM文件中的序列(被截断扔掉了的序列,此时CIGAR列会留下H(Hard)的符号,但是序列的那一列却没有对应的序列了)。

  只要一条序列两端有比对不上的序列部分,就是Soft Clip,这个一条序列上有比对不上的部分的现象是必然存在的(比如结构变异的断点的部分),这种两端比对不上的read的特殊的表示方法,就是Soft Clip。Soft Clip是可以独立存在的。

  Soft Clip不一定有Hard Clip,而有Hard Clip则一定有Soft Clip。Hard Clip存在的本意,是减少BAM文件序列的冗余度,比如有一条read,它能比对到A,B两个地方,在A地方,是60M90S,在B地方是60H90M,此时一条read其实已经在A位置有了完整的序列信息,在B位置的信息其实是冗余的,所以在B位置可以引入Hard Clip这样一个标记形式,就能把B位置的序列标记为secondary。

  (7)=(是否存在多重比对):mate 序列所在参考序列的名称,mate一般指大的片段序列
  reads二次比对结果,=表示没有二次结果;如果3和7都是*,表示没有结果;如果只有7是*,表示只比对到3的结果,如下:

chr13	32968934	37	62M9D31M	chr12	52009363	0 

  (8)32969045:mate 序列在参考序列上的位置,即多重比对时另一条reads位置;
  (9)224:文库插入片段长度,当mate 序列位于本序列上游时该值为负值。
  (10)read的序列:

GCCTCATATGTTAATTGCTGCAAGCAACCTCCAGTGGCGACCAGAATCCAAATCAGGCCTTCTTACTTTATTTGCTGGAGATTTTTCTGCTAG

  (11)read序列对应的ASCII码格式的碱基质量值:

A/B7>FDCFDEFDBFEBDFCAEFEAAACAECEDE@B@=B5BDF1D;:EFCE5ADE>BEE;CCDF=FF=FEBEBEEE=EE@D@AC=<DCACDB

  (12)可选的区域 header section:

X0:i:1  X1:i:0  MD:Z:82^TTTTCTGTG11 PG:Z:MarkDuplicates RG:Z:case   XG:i:9  DI:i:6783411    AM:i:37 NM:i:9  SM:i:37 XM:i:2  XO:i:1  MQ:i:60 DS:i:2  XT:A:U

  其中header section用不同的tag表示不同的信息,主要有@HD,说明符合标准的版本、对比序列的排列顺序;@SQ,参考序列说明;@RG,比对上的序列(read)说明;@PG,使用的程序说明;@CO,任意的说明信息。Tag以键值对的形式存在。

	AS:i 匹配的得分
	XS:i 第二好的匹配的得分
	YS:i mate 序列匹配的得分
	XN:i 在参考序列上模糊碱基的个数
	XM:i 错配的个数
	XO:i gap open的个数
	XG:i gap 延伸的个数
	NM:i 经过编辑的序列
	YF:i 说明为什么这个序列被过滤的字符串
	YT:Z
	MD:Z? 代表序列和参考序列错配的字符串

 

3、转换思路

3.1 软件bedtools自带功能

主要是以参考资料中的信息为主,大致步骤如下:
(1)对bam文件重排序
  流程跑出来的input.bam是以染色体位置排序的,重新sort成按reads名称排序的形式;如果本来就是,这步可跳过。

#sort 
samtools sort -n input.bam -o input.name.bam

(2)转换成fastq文件
  使用bamtofastq功能把reads还原成fq文件。需注意,仅有bam文件中read_1、read_2都存在(两条reads排序后挨着)的reads才会被保留,仅有1条的会被舍弃。

#convert  
bedtools bamtofastq -i input.name.bam \
-fq out.R1.fq \
-fq2 out.R2.fq

(3)压缩fastq文件
  gzip压缩成fq.gz形式。

#gzip fq
gzip out.R1.fq
gzip out.R2.fq

3.2 自己写代码

(1)找bam => fq转化规律
  通过flag值得到两组指标READ1 or READ2, MREVERSE or REVERSE;
  其中,READ1-READ2来决定放到哪个fastq文件中,MREVERSE-REVERSE看测序序列是否与参考序列相同(MREVERSE=相同)。MREVERSE时bam中信息与fq一致, 无需改变; REVERSE时需要质量值反向、序列反向互补;

(2)脚本实现逻辑
  模拟后的bam(sort_by_pos) => 读入pysam => 保留flag值<2048的reads(去掉补充序列) => 按1中规则转换,同时修改reads名称格式,dict记录 => 仅保留reads有read1+read2信息的那种 => 写入并压缩fq文件;

3.3 代码示例

# !D:\00_software\13.Python\python.exe
# -*- coding: UTF-8 -*-
# @File    : bam_to_fq.py

import argparse
import os
import sys
import pysam
from collections import defaultdict


def get_opt():
    group = argparse.ArgumentParser(description="this script is convert file format. bam(sort_by position) => fq.gz")
    group.add_argument("-i", "--input_file", help="input bam file", required=True)
    group.add_argument("-n", "--file_name", help="specify the result file(fq.gz) name", required=True)
    group.add_argument("-o", "--out_dir", help="output directory", default="./")

    return group.parse_args()


def sort_bam(bam_by_position):
    samtools = "/data/Apps/Production/samtools-1.7/bin/samtools"
    is_exists = os.path.exists(samtools)
    if not is_exists:
        print("%s 's abs_path is invalid, please check!!!" % samtools)
        sys.exit(1)

    bai_file = bam_by_position + ".bai"
    if not os.path.exists(bai_file):
        cmd_bai = samtools + " index " + bam_by_position
        os.system(cmd_bai)

    prefix_bam = bai_file.split(".bam")[0]
    sort_bam_file = prefix_bam + "_sort.bam"
    if os.path.exists(sort_bam_file):
        os.system("rm {}".format(sort_bam_file))
    temp_path_list = os.popen("ls {}*sort.bam.tmp*".format(prefix_bam))
    for path_list in temp_path_list:
        os.system("rm {0}".format(path_list))

    cmd_sort = samtools + " sort -n " + bam_by_position + " -o " + sort_bam_file
    os.system(cmd_sort)
    return sort_bam_file


def str_complement(sequence):
    # 构建互补字典
    comp_dict = {
        "A": "T",
        "T": "A",
        "G": "C",
        "C": "G",
        "a": "t",
        "t": "a",
        "g": "c",
        "c": "g",
        "N": "N"
    }
    #求互补序列
    sequence_list = list(sequence)
    sequence_list = [comp_dict[base] for base in sequence_list]
    string = ''.join(sequence_list)
    return string


def str_reverse(sequence):
    return sequence[::-1]  # 求反向序列


def reads_tag(input_file):
    samfile = pysam.AlignmentFile(input_file, "rb")
    # samfile = pysam.AlignmentFile("test.bam", "rb")
    all_reads_dict = defaultdict(dict)
    for read in samfile.fetch():
        # 过滤掉补充reads
        if read.is_supplementary:
            continue

        if read.is_read1:
            read_orientation = "read1"
            read_name = "@" + read.qname + " 1:N:0:AGCGATAG+ACGAATAA"
        else:
            read_orientation = "read2"
            read_name = "@" + read.qname + " 2:N:0:AGCGATAG+ACGAATAA"
        fixed = "+"
        raw_read_name = read.qname

        if read.is_reverse:
            temp_seq_1 = read.seq
            temp_seq_2 = str_reverse(temp_seq_1)
            read_seq = str_complement(temp_seq_2)

            temp_quality = read.qual
            read_quality = str_reverse(temp_quality)
        else:
            read_seq = read.seq
            read_quality = read.qual
        total_reads = "{name}\n{seq}\n{fix}\n{qual}\n".format(name=read_name, seq=read_seq, fix=fixed, qual=read_quality)
        # print(read.qname, read.is_reverse, read.seq, read.qual)
        # print("total_reads", total_reads)
        all_reads_dict[raw_read_name][read_orientation] = total_reads
    return all_reads_dict


def save_fq(all_reads_dict, file_name, out_dir):
    fq1 = out_dir + "/" + file_name + "_R1.fq"
    fq2 = out_dir + "/" + file_name + "_R2.fq"
    ff_1 = open(fq1, 'w')
    ff_2 = open(fq2, 'w')

    for read_name in all_reads_dict.keys():
        orient_num = len(all_reads_dict[read_name].keys())
        # 只保留read_name有两条信息的测序数据
        if orient_num != 2:
            continue
        else:
            # print(read_name, orient_num)
            read1_info = all_reads_dict[read_name]['read1']
            read2_info = all_reads_dict[read_name]['read2']
            ff_1.write(read1_info)
            ff_2.write(read2_info)
    ff_1.close()
    ff_2.close()

    fq_gz_1 = out_dir + "/" + file_name + "_R1.fq.gz"
    fq_gz_2 = out_dir + "/" + file_name + "_R2.fq.gz"
    if os.path.exists(fq_gz_1) or os.path.exists(fq_gz_2):
        os.system("rm {} {}".format(fq_gz_1, fq_gz_2))

    os.system("/usr/bin/gzip {}".format(fq1))
    os.system("/usr/bin/gzip {}".format(fq2))
    return fq_gz_1, fq_gz_2


def main():
    opts = get_opt()
    input_file = opts.input_file
    file_name = opts.file_name
    out_dir = opts.out_dir
    # print(input_file, file_name, out_dir)

    all_reads_dict = reads_tag(input_file)
    fq_gz_1, fq_gz_2 = save_fq(all_reads_dict, file_name, out_dir)
    print(fq_gz_1, fq_gz_2)


if __name__ == '__main__':
    main()

4、参考资料

(1)bamtofastq 将bam文件转成fastq文件
(2)bam格式转换为Fastq/Fasta格式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值