From 4567ef5f70e6ff7bf58ead56ebba82d478d05d2f Mon Sep 17 00:00:00 2001 From: weilongguo Date: Fri, 9 Dec 2016 18:02:29 +0800 Subject: [PATCH] v2.1.0 --- RELEASE_NOTES | 8 ++- bs_align/bs_align_utils.py | 106 ++++++++++++++++++++++----------- bs_align/bs_pair_end.py | 7 ++- bs_align/bs_rrbs.py | 20 ++++--- bs_align/bs_single_end.py | 7 ++- bs_seeker2-call_methylation.py | 73 +++++++++++++---------- bs_utils/utils.py | 2 +- 7 files changed, 139 insertions(+), 84 deletions(-) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 1d25e0b..95a3024 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -72,4 +72,10 @@ V2.0.9 - Dec 30, 2014 V2.0.10 - Nov 5, 2015 1. [bs_seeker2-align.py] : Fix bug with "-s" and "-e" -1. [bs_seeker2-call_methylation.py] : Fix error with pysam version higher than 0.8.0 +2. [bs_seeker2-call_methylation.py] : Fix error with pysam version higher than 0.8.0 + +------------------------------------------ + +V2.1.0 - Dec 9, 2016 +1. fixed bug related with pysam version +2. fixed bug related with output information diff --git a/bs_align/bs_align_utils.py b/bs_align/bs_align_utils.py index 20f5abd..d90f7c5 100755 --- a/bs_align/bs_align_utils.py +++ b/bs_align/bs_align_utils.py @@ -16,8 +16,11 @@ def N_MIS(r,g): for i in xrange(len(r)): if r[i] != g[i] and r[i] != "N" and g[i] != "N" and not(r[i] == 'T' and g[i] == 'C'): mismatches += 1 + # + # + # return mismatches - +# #---------------------------------------------------------------- @@ -64,7 +67,7 @@ def RemoveAdapter ( read, adapter, no_mismatch, rm_back=0) : # not including the 'A' base in front of the adapter. if adapter[2:] == read[0:(la-1)] : return "" - + # for i in xrange( lr - no_mismatch ) : read_pos = i adapter_pos = 0 @@ -80,6 +83,8 @@ def RemoveAdapter ( read, adapter, no_mismatch, rm_back=0) : else : read_pos = read_pos + 1 adapter_pos = adapter_pos + 1 + # + # # while_end # Cut the extra bases before the adapter @@ -90,6 +95,8 @@ def RemoveAdapter ( read, adapter, no_mismatch, rm_back=0) : return '' else : return read[:(i-rm_back)] + # + # # for_end return read @@ -99,6 +106,7 @@ def Remove_5end_Adapter ( read, adapter, no_mismatch) : la = len(adapter) if la == 0 : return read + # for i in xrange (la - no_mismatch) : read_pos = 0 adapter_pos = i @@ -114,11 +122,14 @@ def Remove_5end_Adapter ( read, adapter, no_mismatch) : else : read_pos = read_pos + 1 adapter_pos = adapter_pos + 1 + # + # # while_end if adapter_pos == la : return read[(la-i):] + # return read - +# def next_nuc(seq, pos, n): """ Returns the nucleotide that is n places from pos in seq. Skips gap symbols. @@ -133,7 +144,8 @@ def next_nuc(seq, pos, n): return seq[i] else : return 'N' - + # +# def methy_seq(read, genome): @@ -141,13 +153,10 @@ def methy_seq(read, genome): m_seq = [] xx = "-" for i in xrange(len(read)): - if genome[i] == '-': continue - elif read[i] != 'C' and read[i] != 'T': xx = "-" - elif read[i] == "T" and genome[i] == "C": #(unmethylated): nn1 = next_nuc(genome, i, 1) if nn1 == "G": @@ -158,7 +167,8 @@ def methy_seq(read, genome): xx = "y" elif nn2 in H : xx = "z" - + # + # elif read[i] == "C" and genome[i] == "C": #(methylated): nn1 = next_nuc(genome, i, 1) @@ -166,44 +176,51 @@ def methy_seq(read, genome): xx = "X" elif nn1 in H : nn2 = next_nuc(genome, i, 2) - + 2# if nn2 == "G": xx = "Y" elif nn2 in H: xx = "Z" + # + # else: xx = "-" + # m_seq.append(xx) - + # return ''.join(m_seq) +# def mcounts(mseq, mlst, ulst): out_mlst=[mlst[0]+mseq.count("X"), mlst[1]+mseq.count("Y"), mlst[2]+mseq.count("Z")] out_ulst=[ulst[0]+mseq.count("x"), ulst[1]+mseq.count("y"), ulst[2]+mseq.count("z")] return out_mlst, out_ulst - +# def process_aligner_output(filename, pair_end = False): - #m = re.search(r'-('+'|'.join(supported_aligners) +')-TMP', filename) m = re.search(r'-('+'|'.join(supported_aligners) +')-.*TMP', filename) if m is None: error('The temporary folder path should contain the name of one of the supported aligners: ' + filename) - + # format = m.group(1) try : input = open(filename) except IOError: print "[Error] Cannot open file %s" % filename exit(-1) - + # QNAME, FLAG, RNAME, POS, MAPQ, CIGAR, RNEXT, PNEXT, TLEN, SEQ, QUAL = range(11) def parse_SAM(line): - buf = line.split() - # print buf + # fix error when reading file with lots of \x00 # date on 2016-12-09 + line = line.replace('\x00', '').strip() + buf = line.split("\t") + if len(buf) < 11 : + sys.stderr.write("[warning] SAM input without enough columns\n") + return None, None, None, None, None, None + # flag = int(buf[FLAG]) - # skip reads that are not mapped # skip reads that have probability of being non-unique higher than 1/10 if flag & 0x4 : # or int(buf[MAPQ]) < 10: @@ -230,7 +247,7 @@ def parse_SAM(line): # chr16 75728107 75728147 read45 9 - # chr16 67934919 67934959 read45 9 - mismatches = buf[4] - + # return (buf[QNAME], # read ID buf[RNAME], # reference ID int(buf[POS]) - 1, # position, 0 based (SAM is 1 based) @@ -238,6 +255,7 @@ def parse_SAM(line): parse_cigar(buf[CIGAR]), # the parsed cigar string flag & 0x40 # true if it is the first mate in a pair, false if it is the second mate ) + # SOAP_QNAME, SOAP_SEQ, SOAP_QUAL, SOAP_NHITS, SOAP_AB, SOAP_LEN, SOAP_STRAND, SOAP_CHR, SOAP_LOCATION, SOAP_MISMATCHES = range(10) def parse_SOAP(line): @@ -250,8 +268,9 @@ def parse_SOAP(line): buf[SOAP_STRAND], parse_cigar(buf[SOAP_LEN]+'M') ) + # - # chr16 75728107 75728147 read45 9 - + # chr16 75728107 75728147 read45 9 - RMAP_CHR, RMAP_START, RMAP_END, RMAP_QNAME, RMAP_MISMATCH, RMAP_STRAND = range(6) def parse_RMAP(line): buf = line.split() @@ -262,47 +281,55 @@ def parse_RMAP(line): int(buf[RMAP_MISMATCH]), buf[RMAP_STRAND] ) + # if format == BOWTIE or format == BOWTIE2: if pair_end: for line in input: header1, chr1, location1, no_mismatch1, cigar1, _ = parse_SAM(line) header2, _, location2, no_mismatch2, cigar2, mate_no2 = parse_SAM(input.next()) - - + # if header1 and header2: # flip the location info if the second mate comes first in the alignment file if mate_no2: location1, location2 = location2, location1 cigar1, cigar2 = cigar2, cigar1 - - + # yield header1, chr1, no_mismatch1 + no_mismatch2, location1, cigar1, location2, cigar2 + # + # else: for line in input: header, chr, location, no_mismatch, cigar, _ = parse_SAM(line) if header is not None: yield header, chr, location, no_mismatch, cigar + # + # + # elif format == SOAP: if pair_end: for line in input: header1, chr1, location1, no_mismatch1, mate1, strand1, cigar1 = parse_SOAP(line) header2, _ , location2, no_mismatch2, _, strand2, cigar2 = parse_SOAP(input.next()) - + # if mate1 == 'b': location1, location2 = location2, location1 strand1, strand2 = strand2, strand1 ciga1, cigar2 = cigar2, cigar1 - - + # if header1 and header2 and strand1 == '+' and strand2 == '-': yield header1, chr1, no_mismatch1 + no_mismatch2, location1, cigar1, location2, cigar2 - + # + # + # else: for line in input: header, chr, location, no_mismatch, _, strand, cigar = parse_SOAP(line) if header and strand == '+': yield header, chr, location, no_mismatch, cigar + # + # + # elif format == RMAP : if pair_end : todo = 0 @@ -312,9 +339,11 @@ def parse_RMAP(line): header, chr, location, read_len, no_mismatch, strand = parse_RMAP(line) cigar = str(read_len) + "M" yield header, chr, location, no_mismatch, cigar - + # + # + # input.close() - +# def parse_cigar(cigar_string): i = 0 @@ -339,11 +368,13 @@ def get_read_start_end_and_genome_length(cigar): r_end += count elif edit_op == BAM_DEL: g_len += count + # + # return r_start, r_end, g_len # return the start and end in the read and the length of the genomic sequence # r_start : start position on the read # r_end : end position on the read # g_len : length of the mapped region on genome - +# def cigar_to_alignment(cigar, read_seq, genome_seq): """ Reconstruct the pairwise alignment based on the CIGAR string and the two sequences @@ -368,9 +399,10 @@ def cigar_to_alignment(cigar, read_seq, genome_seq): r_aln += read_seq[r_pos : r_pos + count] g_aln += '-'*count r_pos += count - + # + # return r_aln, g_aln - +# # return sequence is [start, end), not include 'end' @@ -384,19 +416,21 @@ def get_genomic_sequence(genome, start, end, strand = '+'): prev = 'N'+genome[0] else: prev = 'NN' - + # if end < len(genome) - 1: next = genome[end: end + 2] elif end == len(genome) - 1: next = genome[end] + 'N' else: next = 'NN' + # origin_genome = genome[start:end] - + # if strand == '-': # reverse complement everything if strand is '-' revc = reverse_compl_seq('%s%s%s' % (prev, origin_genome, next)) prev, origin_genome, next = revc[:2], revc[2:-2], revc[-2:] - + # return origin_genome, next, '%s_%s_%s' % (prev, origin_genome, next) - # next : next two nucleotides \ No newline at end of file + # next : next two nucleotides +# diff --git a/bs_align/bs_pair_end.py b/bs_align/bs_pair_end.py index b1a675f..3bb4fab 100755 --- a/bs_align/bs_pair_end.py +++ b/bs_align/bs_pair_end.py @@ -989,7 +989,8 @@ def bs_pair_end(main_read_file_1, #print "AdapterA=", adapterA, "; AdapterB=", adapterB if adapterA != "" or adapterB != "" : logm("Number of reads having adapter removed: %d "% all_trimmed) - logm("Number of bases after trimming the adapters: %d (%1.3f)" % (all_base_after_trim, float(all_base_after_trim)/all_base_before_trim) ) + trim_percent = (float(all_base_after_trim) / all_base_before_trim) if all_base_before_trim>0 else 0 + logm("Number of bases after trimming the adapters: %d (%1.3f)" % (all_base_after_trim, trim_percent) ) if all_raw_reads >0: logm("Number of reads rejected because of multiple hits: %d" % len(Multiple_hits) ) @@ -1011,8 +1012,8 @@ def bs_pair_end(main_read_file_1, elif asktag=="N": logm(" %7d FW-RC pairs mapped to Watson strand" % (numbers_mapped_lst[0]) ) logm(" %7d FW-RC pairs mapped to Crick strand" % (numbers_mapped_lst[1]) ) - - logm("Mappability = %1.4f%%" % (100*float(all_mapped_passed)*2/all_raw_reads) ) + Mappability = (100*float(all_mapped_passed)*2/all_raw_reads) if all_raw_reads > 0 else 0 + logm("Mappability = %1.4f%%" % Mappability ) logm("Total bases of uniquely mapped reads : %7d" % all_base_mapped ) logm("Unmapped read pairs: %d" % all_unmapped+"\n") # diff --git a/bs_align/bs_rrbs.py b/bs_align/bs_rrbs.py index a915d9c..2b6742c 100755 --- a/bs_align/bs_rrbs.py +++ b/bs_align/bs_rrbs.py @@ -1012,13 +1012,13 @@ def bs_rrbs(main_read_file, asktag, adapter_file, cut_s, cut_e, no_small_lines, try_pos, FR) try_pos += 1 try_count += 1 - + # #if my_region_serial == 0 : # print "[For debug]: chr=", mapped_chr # print "[For debug]: RC_C2A read still cannot find fragment serial" - + # N_mismatch = N_MIS(r_aln, g_aln) -# if N_mismatch <= int(max_mismatch_no) : + #if N_mismatch <= int(max_mismatch_no) : mm_no=float(max_mismatch_no) if (mm_no>=1 and N_mismatch<=mm_no) or (mm_no<1 and N_mismatch<=(mm_no*len(r_aln)) ): all_mapped_passed += 1 @@ -1059,19 +1059,23 @@ def bs_rrbs(main_read_file, asktag, adapter_file, cut_s, cut_e, no_small_lines, logm("----------------------------------------------") logm("Number of raw reads: %d " % all_raw_reads) if all_raw_reads>0: - logm("Number of raw reads with CGG/TGG at 5' end: %d (%1.3f)" % (all_tagged, float(all_tagged)/all_raw_reads)) + tag_percent = (float(all_tagged)/all_raw_reads) if all_raw_reads>0 else 0 + logm("Number of raw reads with CGG/TGG at 5' end: %d (%1.3f)" % (all_tagged, tag_percent) ) for kk in range(len(n_cut_tag_lst)): - logm("Number of raw reads with tag %s: %d (%1.3f)" % (cut3_tag_lst[kk],n_cut_tag_lst[cut3_tag_lst[kk]],float(n_cut_tag_lst[cut3_tag_lst[kk]])/all_raw_reads)) + tag_percent = (float(n_cut_tag_lst[cut3_tag_lst[kk]])/all_raw_reads) if all_raw_reads>0 else 0 + logm("Number of raw reads with tag %s: %d (%1.3f)" % (cut3_tag_lst[kk],n_cut_tag_lst[cut3_tag_lst[kk]], tag_percent) ) logm("Number of bases in total: %d " % all_base_before_trim) if adapter!="" : logm("Number of reads having adapter removed: %d " % all_tagged_trimmed) - logm("Number of bases after trimming the adapters: %d (%1.3f)" % (all_base_after_trim, float(all_base_after_trim)/all_base_before_trim ) ) + trim_percent = (float(all_base_after_trim)/all_base_before_trim ) if all_base_before_trim>0 else 0 + logm("Number of bases after trimming the adapters: %d (%1.3f)" % (all_base_after_trim, trim_percent ) ) logm("Number of reads are rejected because of multiple hits: %d\n" % len(Multiple_hits) ) logm("Number of unique-hits reads (before post-filtering): %d" % all_mapped) logm(" %d uniquely aligned reads, passed fragment check, with mismatches <= %s"%(all_mapped_passed, max_mismatch_no)) - logm("Mappability = %1.4f%%" % (100*float(all_mapped_passed)/all_raw_reads)) + Mappability = (100*float(all_mapped_passed)/all_raw_reads) if all_raw_reads>0 else 0 + logm("Mappability = %1.4f%%" % Mappability ) #logm("Total bases of uniquely mapped reads %7d"% all_base_mapped ) - if asktag == "Y": # un-diretional + if asktag == "Y": # un-directional logm(" %7d FW reads mapped to Watson strand" % (num_mapped_FW_C2T) ) logm(" %7d RC reads mapped to Watson strand" % (num_mapped_FW_G2A) ) logm(" %7d FW reads mapped to Crick strand" % (num_mapped_RC_C2T) ) diff --git a/bs_align/bs_single_end.py b/bs_align/bs_single_end.py index 7996b7b..8a7a0d4 100755 --- a/bs_align/bs_single_end.py +++ b/bs_align/bs_single_end.py @@ -726,7 +726,9 @@ def bs_single_end(main_read_file, asktag, adapter_file, cut1, cut2, no_small_lin logm("Number of bases in total: %d " % all_base_before_trim) if (asktag == "N" and adapter != "") or (asktag == "Y" and adapter_fw != "") : logm("Number of reads having adapter removed: %d" % all_trimmed ) - logm("Number of bases after trimming the adapters: %d (%1.3f)" % (all_base_after_trim, float(all_base_after_trim)/all_base_before_trim) ) + trim_percent = (float(all_base_after_trim)/all_base_before_trim) if all_base_before_trim>0 else 0 + logm("Number of bases after trimming the adapters: %d (%1.3f)" % (all_base_after_trim, trim_percent) ) + # logm("Number of reads are rejected because of multiple hits: %d" % len(Multiple_hits) ) logm("Number of unique-hits reads (before post-filtering): %d" % all_mapped) if asktag == "Y": @@ -747,7 +749,8 @@ def bs_single_end(main_read_file, asktag, adapter_file, cut1, cut2, no_small_lin elif asktag == "N": logm(" %7d FW reads mapped to Watson strand" % (numbers_mapped_lst[0]) ) logm(" %7d FW reads mapped to Crick strand" % (numbers_mapped_lst[1]) ) - logm("Mappability = %1.4f%%" % (100*float(all_mapped_passed)/all_raw_reads) ) + Mappability = (100 * float(all_mapped_passed) / all_raw_reads) if all_raw_reads>0 else 0 + logm("Mappability = %1.4f%%" % Mappability ) logm("Total bases of uniquely mapped reads : %7d" % all_base_mapped ) # n_CG = mC_lst[0] + uC_lst[0] diff --git a/bs_seeker2-call_methylation.py b/bs_seeker2-call_methylation.py index 653f1f7..b9d967d 100755 --- a/bs_seeker2-call_methylation.py +++ b/bs_seeker2-call_methylation.py @@ -8,7 +8,7 @@ except ImportError : print "[Error] Cannot import \"pysam\" package. Have you installed it?" exit(-1) - +# import gzip @@ -52,7 +52,7 @@ def context_calling(seq, position): if __name__ == '__main__': - + # parser = OptionParser() parser.add_option("-i", "--input", type="string", dest="infilename",help="BAM output from bs_seeker2-align.py", metavar="INFILE") parser.add_option("-d", "--db", type="string", dest="dbpath",help="Path to the reference genome library (generated in preprocessing genome) [Default: %default]" , metavar="DBPATH", default = reference_genome_path) @@ -72,74 +72,78 @@ def context_calling(seq, position): parser.add_option("-v", "--version", action="store_true", dest="version",help="show version of BS-Seeker2", metavar="version", default = False) (options, args) = parser.parse_args() - + # # if no options were given by the user, print help and exit if len(sys.argv) == 1: parser.print_help() exit(0) - + # if options.version : show_version() exit (-1) else : show_version() - - + # + # if options.infilename is None: error('-i option is required') if not os.path.isfile(options.infilename): error('Cannot find input file: %s' % options.infilename) - + # open_log(options.infilename+'.call_methylation_log') db_d = lambda fname: os.path.join( os.path.expanduser(options.dbpath), fname) # bug fixed, weilong - + # if options.RM_OVERLAP : logm("The option \"--rm-overlap\" is specified, thus overlap regions of two mates would be discarded.") - + # if options.sorted : logm('The option \"--sorted\" is specified, thus sorting step is skipped') sorted_input_filename = options.infilename else : logm('sorting BS-Seeker alignments') sorted_input_filename = options.infilename+'_sorted' - pysam.sort(options.infilename, sorted_input_filename) + if pysam.__version__ > '0.7' : + pysam.sort(options.infilename, sorted_input_filename) + else : + pysam.sort("-o", sorted_input_filename + '.bam', "-T", sorted_input_filename, options.infilename) + # sorted_input_filename += '.bam' # end_of if logm('indexing sorted alignments') pysam.index(sorted_input_filename) - + # logm('calculating methylation levels') if options.text : ATCGmap_fname = options.ATCGmap_file or ((options.output_prefix or options.infilename) + '.ATCGmap') ATCGmap = open(ATCGmap_fname, 'w') - + # CGmap_fname = options.CGmap_file or ((options.output_prefix or options.infilename) + '.CGmap') CGmap = open(CGmap_fname, 'w') else : ATCGmap_fname = options.ATCGmap_file or ((options.output_prefix or options.infilename) + '.ATCGmap.gz') ATCGmap = gzip.open(ATCGmap_fname, 'wb') - + # CGmap_fname = options.CGmap_file or ((options.output_prefix or options.infilename) + '.CGmap.gz') CGmap = gzip.open(CGmap_fname, 'wb') - + # # to improve the performance options_RM_CCGG = options.RM_CCGG options_read_no = options.read_no options_RM_SX = options.RM_SX options_RM_OVERLAP = options.RM_OVERLAP - + # wiggle_fname = options.wig_file or ((options.output_prefix or options.infilename) + '.wig') wiggle = open(wiggle_fname, 'w') wiggle.write('type wiggle_0\n') - + # sorted_input = pysam.Samfile(sorted_input_filename, 'rb') - + # chrom = None nucs = ['A', 'T', 'C', 'G', 'N'] ATCG_fwd = dict((n, 0) for n in nucs) ATCG_rev = dict((n, 0) for n in nucs) - + # # Define the context and subcontext exchanging dictionary ContextTable={"CAA":"CHH", "CAC":"CHH", "CAG":"CHG", "CAT":"CHH", "CCA":"CHH", "CCC":"CHH", "CCG":"CHG", "CCT":"CHH", @@ -165,9 +169,9 @@ def context_calling(seq, position): # #PileUp = sorted_input.pileup() #for col in PileUp: - + # cnts = lambda d: '\t'.join(str(d[n]) for n in nucs) - + # for col in sorted_input.pileup(): col_chrom = sorted_input.getrname(col.tid) col_pos = col.pos @@ -183,7 +187,7 @@ def context_calling(seq, position): # #nuc, context, subcontext = context_calling(chrom_seq, col_pos) # Need to validate the following codes - + # if 1 < col_pos < len(chrom_seq) - 2 : FiveMer = chrom_seq[(col_pos-2):(col_pos+3)].upper() nuc = FiveMer[2] @@ -198,6 +202,7 @@ def context_calling(seq, position): else : context = "--" subcontext = "--" + # else : nuc = chrom_seq[col_pos].upper() context = "--" @@ -224,7 +229,7 @@ def context_calling(seq, position): if options_RM_OVERLAP : qname_pool = [] del qname_pool[:] - + # for pr in col.pileups: # print pr if pysam.__version__ > "0.8.0" : @@ -234,6 +239,7 @@ def context_calling(seq, position): # if (not pr.indel) : # skip indels pr_alignment = pr.alignment + #print pr.alignment #if ( (options_RM_SX) and (pr.alignment.tags[1][1] == 1) ): ##=== Fixed error reported by Roberto #print options_RM_SX, dict(pr.alignment.tags)["XS"] @@ -277,34 +283,34 @@ def context_calling(seq, position): if read_nuc != 'N': total_reads += 1 #print col_pos, qname_pool - + # #cnts = lambda d: '\t'.join(str(d[n]) for n in nucs) fwd_counts = cnts(ATCG_fwd) rev_counts = cnts(ATCG_rev) - + # meth_level = None meth_cytosines = 0 unmeth_cytosines = 0 - + # if nuc == 'C': # plus strand: take the ratio of C's to T's from reads that come from the forward strand meth_cytosines = ATCG_fwd['C'] unmeth_cytosines = ATCG_fwd['T'] - elif nuc == 'G': # minus strand: take the ratio of G's to A's from reads that come from the reverse strand meth_cytosines = ATCG_rev['G'] unmeth_cytosines = ATCG_rev['A'] - + #print("%s\t%d\t%d" % (nuc, ATCG_rev['G'], ATCG_rev['A'] ) ) + # all_cytosines = meth_cytosines + unmeth_cytosines if all_cytosines > 0: meth_level = float(meth_cytosines)/all_cytosines - + # pos = col_pos + 1 - + # meth_level_string = str(round(meth_level, 2)) if meth_level is not None else 'na' ATCGmap.write('%(chrom)s\t%(nuc)s\t%(pos)d\t%(context)s\t%(subcontext)s\t%(fwd_counts)s\t%(rev_counts)s\t%(meth_level_string)s\n' % locals()) - + # try : #if (meth_level is not None) and (all_cytosines >= options_read_no): if (all_cytosines >= options_read_no): @@ -318,15 +324,16 @@ def context_calling(seq, position): # CGmap file only show CG sites except TypeError : continue - + # + # ATCGmap.close() CGmap.close() wiggle.close() - + # logm('Call methylation is finished. ') logm('==============================') logm('Files are saved as:') logm(' Wiggle: %s'% wiggle_fname) logm(' ATCGMap: %s' % ATCGmap_fname) logm(' CGmap: %s' % CGmap_fname) - +# diff --git a/bs_utils/utils.py b/bs_utils/utils.py index 966340d..4123ef8 100644 --- a/bs_utils/utils.py +++ b/bs_utils/utils.py @@ -23,7 +23,7 @@ def reverse_compl_seq(strseq): def show_version() : print "" - print " BS-Seeker2 v2.0.10 - Nov 5, 2015" + print " BS-Seeker2 v2.1.0 - Dec 9, 2016" print ""