view pyPRADA_1.2/prada-guess-if @ 0:acc2ca1a3ba4

Uploaded
author siyuan
date Thu, 20 Feb 2014 00:44:58 -0500
parents
children
line wrap: on
line source

#!/usr/bin/env python

#GUESS-if is to find abnormal intragenic fusions. 
#It is an extension of the GUESS-ft method. 

import pysam
import subprocess
import os
import time
import sys
import bioclass
import ioprada

args=sys.argv

#Get all parameters
help_menu='''\nUsage: prada-guess-if Gene -conf xx.txt -inputbam X -mm 1 -minmapq 30 -junL X -outdir ./ -unmap X\n
    **Parameters**:
        -conf        the configure file. see prada-fusion -conf for details
        -inputbam   the input bam file
        -mm         number of mismatch allowed
        -minmapq    mininum mapping quality for reads to be used in fusion finding
        -junL       length of exons to be used for junctions. see prada-fusion
        -outdir     output directory
        -unmap      the numapped reads. useful if user need to run guess-ft multiple times
'''

if '-h' in args or '-help' in args or len(args)==1:
    print help_menu
    sys.exit(0)

#########################################################################
target=args[1]
if '-inputbam' not in args:
    sys.exit('Input BAM needed')
else:
    i=args.index('-inputbam')
    sourcefile=args[i+1]
if '-outdir' not in args:
    outdir='./'
else:
    i=args.index('-outdir')
    outdir=args[i+1]
    if not os.path.exists(outdir):
        os.mkdir(outdir)
if '-unmap' not in args:
    unmapbam='%s/one.end.unmapped.bam'%outdir
    extract_mask=1
else:
    i=args.index('-unmap')
    unmapbam=args[i+1]
    extract_mask=0

if '-mm' not in args:
    mm=1
else:
    i=args.index('-mm')
    mm=int(args[i+1])

#minimum mapping quality for reads as fusion evidences
if '-minmapq' not in args:
    minmapq=30
else:
    i=args.index('-minmapq')
    minmapq=int(args[i+1])

if '-junL' not in args:
    sys.exit('-junL needed')
else:
    i=args.index('-junL')
    junL=int(args[i+1])

prada_path=os.path.dirname(os.path.abspath(__file__))   ####
ref_search_path=[prada_path,os.getcwd()]                #search path for ref file if not specified in command line

if '-conf' in args:
    i=args.index('-conf')
    reffile=args[i+1]
    if os.path.exists(reffile):
        pass
    else:
        for pth in ref_search_path:
            new_reffile='%s/%s'%(pth, os.path.basename(reffile))
            if os.path.exists(new_reffile):
                reffile=new_reffile
                break
        else:
            sys.exit('ERROR: ref file %s not found'%reffile)
else:
    reffile='%s/conf.txt'%prada_path
    if not os.path.exists(reffile):
        sys.exit('ERROR: No default conf.txt found and none specified')

#reference files
refdict=ioprada.read_conf(reffile)
ref_anno=refdict['--REF--']['ref_anno']
ref_map=refdict['--REF--']['ref_map']
ref_fasta=refdict['--REF--']['ref_fasta']
featurefile=refdict['--REF--']['feature_file']

samtools='%s/tools/samtools-0.1.16/samtools'%prada_path
bwa='%s/tools/bwa-0.5.7-mh/bwa'%prada_path

#########################################################################
print 'GUESS-if start: %s'%time.ctime()
print 'CMD: %s'%('\t'.join(args))

##get gene position information
gobj=ioprada.read_feature_genes(featurefile,target)[0]
if gobj is None:
    sys.exit('%s not found'%target)

gchr=gobj.strand
gchr_rev=True if gchr=='-1' else False

#Generate unmapped reads.
if extract_mask:
    cmd='%s view -b -f 4 -F 8 %s > %s'%(samtools,sourcefile,unmapbam)
    cmdout=subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,shell=True)
    while True:
        if cmdout.poll() is None:
            print 'Extracting unmapped reads...'
            time.sleep(120)
            pass
        if cmdout.poll()==0:
            print 'Extracted unmapped reads'
            break
        if cmdout.poll() is not None and cmdout.poll() != 0:
            raise Exception('Error extracting unmapped reads')


#Generate junction db
juncfile='%s.junction.fasta'%target
cmd='perl %s/make_intragenic_junctions.pl %s %s %s %s %s %d > %s/%s'%(prada_path,target,target,ref_anno,ref_map,ref_fasta,junL,outdir,juncfile)
cmdout=subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,shell=True)
while True:
    if cmdout.poll() is None:
        print 'Generating junction db. Waiting...'
        time.sleep(20)
        pass
    if cmdout.poll()==0:
        print 'Generated Junction DB.'
        break
    if cmdout.poll() is not None and cmdout.poll() != 0:
        raise Exception('Error generated Junction DB.')

#scan BAM file for mapping reads.
samfile=pysam.Samfile(sourcefile,'rb')
reads_se=[]
reads_as=[]
for alignedread in samfile.fetch(gobj.chr,gobj.start-1,gobj.end):
    if alignedread.mapq >= minmapq:
        mmf=[x[1] for x in alignedread.tags if x[0]=='NM'][0]
        if mmf <= mm:
            if alignedread.is_reverse==gchr_rev:   #sense
                reads_se.append(alignedread)
            else:                                  #anti-sense
                reads_as.append(alignedread)
samfile.close()

seonly,asonly=[],[]
for rd in reads_se:
    if rd.mate_is_unmapped:
        seonly.append(rd)
for rd in reads_as:
    if rd.mate_is_unmapped:
        asonly.append(rd) 
seunmap=[x.qname for x in seonly]
asunmap=[x.qname for x in asonly]

#find read sequences
print 'Extracting unmapped read sequences.'
print 'mate unmapped reads for sense strand:',len(seonly)
print 'mate unmapped reads for antisense strand:',len(asonly)
samfile=pysam.Samfile(unmapbam,'rb')
resfq_a=open('%s/%s_se_unmap.fq'%(outdir,target),'w')
resfq_b=open('%s/%s_as_unmap.fq'%(outdir,target),'w')
for item in samfile:
    if item.qname in seunmap:
        resfq_a.write('@%s\n'%item.qname)
        resfq_a.write('%s\n'%item.seq)
        resfq_a.write('+\n')
        resfq_a.write('%s\n'%item.qual)
    if item.qname in asunmap:
        resfq_b.write('@%s\n'%item.qname)
        resfq_b.write('%s\n'%item.seq)
        resfq_b.write('+\n')
        resfq_b.write('%s\n'%item.qual)
resfq_a.close()
resfq_b.close()
samfile.close()

##indexing junction db
print 'Aligning reads to junction db'
cmd='%s index %s/%s'%(bwa,outdir,juncfile)
cmdout=subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,shell=True)
while True:
    if cmdout.poll() is None:
        time.sleep(3)
        pass
    if cmdout.poll()==0:
        print 'Junction DB indexed.'
        break
    if cmdout.poll() is not None and cmdout.poll() != 0:
        raise Exception('Error building junction db index')

taga='%s_se'%target
tagb='%s_as'%target
for rs in [taga,tagb]:
    cmd='%s aln -n %d -R 100 %s/%s %s/%s_unmap.fq > %s/%s_unmap.sai'%(bwa,mm,outdir,juncfile,outdir,rs,outdir,rs)
    cmdout=subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,shell=True)
    while True:
        if cmdout.poll() is None:
            time.sleep(5)
            pass
        if cmdout.poll()==0:
            print 'Aligned unmapped reads group %s'%rs
            break
        if cmdout.poll() is not None and cmdout.poll() != 0:
            raise Exception('Error aligning unmapped reads for group %s'%rs)

    cmd='%s samse -n 1000 %s/%s %s/%s_unmap.sai %s/%s_unmap.fq > %s/%s_unmap.sam'%(bwa,outdir,juncfile,outdir,rs,outdir,rs,outdir,rs)
    cmdout=subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,shell=True)
    while True:
        if cmdout.poll() is None:
            time.sleep(2)
            pass
        if cmdout.poll()==0:
            print 'Converting to sam for group %s'%rs
            break
        if cmdout.poll() is not None and cmdout.poll() != 0:
            raise Exception('Error converting to sam for group %s'%rs)

qualrd_a=[]
junc_a=[]
samfile=pysam.Samfile('%s/%s_unmap.sam'%(outdir,taga),'r')
for rd in samfile:
    if not rd.is_unmapped and rd.is_reverse:
        qualrd_a.append(rd)
        junc_a.append(samfile.getrname(rd.tid))
samfile.close()
qualrd_b=[]
junc_b=[]
samfile=pysam.Samfile('%s/%s_unmap.sam'%(outdir,tagb),'r')
for rd in samfile:
    if not rd.is_unmapped and not rd.is_reverse:
        qualrd_b.append(rd)
        junc_b.append(samfile.getrname(rd.tid))
samfile.close()

junc_span=[]
junc_span.extend(qualrd_a)
junc_span.extend(qualrd_b)

junc_name=[]
junc_name.extend(junc_a)
junc_name.extend(junc_b)

#Generate a summary report
sumfile=open('%s/%s.GUESS-IF.summary.txt'%(outdir,target),'w')
sumfile.write('%s\n'%(target))
sumfile.write('\n')
sumfile.write('>spanning\n')
for i in range(len(junc_span)):
    rd=junc_span[i]
    jname=junc_name[i]
    mm_j=[x[1] for x in rd.tags if x[0]=='NM'][0]
    ss='%s\t%s.mm%d'%(rd.qname,jname,mm_j)
    sumfile.write('%s\n'%ss)

sumfile.write('\n')
sumfile.write('>junction\n')
juncol=[]
for item in set(junc_name):
    nn=junc_name.count(item)
    juncol.append([item,nn])
juncol=sorted(juncol,key=lambda x:x[1],reverse=True)
for item in juncol:
    sumfile.write('%s\t%s\n'%(item[0],item[1]))

sumfile.write('\n')
sumfile.write('>summary\n')
sumfile.write('Number of Fusion Reads = %d\n'%len(junc_span))
sumfile.write('Number of Distinct Junctions = %d\n'%len(set(junc_name)))

sumfile.close()
print 'Done: %s'%time.ctime()