comparison flanking_features.py @ 1:8307665c4b6c draft

planemo upload for repository https://github.com/galaxyproject/tools-devteam/tree/master/tool_collections/gops/flanking_features commit a1517c9d22029095120643bbe2c8fa53754dd2b7
author devteam
date Wed, 11 Nov 2015 12:48:18 -0500
parents 90100b587723
children a09d13b108fd
comparison
equal deleted inserted replaced
0:90100b587723 1:8307665c4b6c
8 -2, --cols2=N,N,N,N: Columns for start, end, strand in second file 8 -2, --cols2=N,N,N,N: Columns for start, end, strand in second file
9 -G, --gff1: input 1 is GFF format, meaning start and end coordinates are 1-based, closed interval 9 -G, --gff1: input 1 is GFF format, meaning start and end coordinates are 1-based, closed interval
10 -H, --gff2: input 2 is GFF format, meaning start and end coordinates are 1-based, closed interval 10 -H, --gff2: input 2 is GFF format, meaning start and end coordinates are 1-based, closed interval
11 """ 11 """
12 12
13 import sys, traceback, fileinput 13 import fileinput
14 from warnings import warn 14 import sys
15 from bx.cookbook import doc_optparse 15 from bx.cookbook import doc_optparse
16 from galaxy.tools.util.galaxyops import * 16 from bx.intervals.io import Comment, GenomicInterval, Header, NiceReaderWrapper
17 from bx.intervals.io import *
18 from bx.intervals.operations import quicksect 17 from bx.intervals.operations import quicksect
19 from utils.gff_util import * 18 from bx.tabular.io import ParseError
19 from galaxy.tools.util.galaxyops import fail, parse_cols_arg, skipped
20 from utils.gff_util import convert_bed_coords_to_gff, GFFIntervalToBEDReaderWrapper
20 21
21 assert sys.version_info[:2] >= ( 2, 4 ) 22 assert sys.version_info[:2] >= ( 2, 4 )
22 23
23 def get_closest_feature (node, direction, threshold_up, threshold_down, report_func_up, report_func_down): 24
25 def get_closest_feature(node, direction, threshold_up, threshold_down, report_func_up, report_func_down):
24 #direction=1 for +ve strand upstream and -ve strand downstream cases; and it is 0 for +ve strand downstream and -ve strand upstream cases 26 #direction=1 for +ve strand upstream and -ve strand downstream cases; and it is 0 for +ve strand downstream and -ve strand upstream cases
25 #threhold_Up is equal to the interval start for +ve strand, and interval end for -ve strand 27 #threhold_Up is equal to the interval start for +ve strand, and interval end for -ve strand
26 #threhold_down is equal to the interval end for +ve strand, and interval start for -ve strand 28 #threhold_down is equal to the interval end for +ve strand, and interval start for -ve strand
27 if direction == 1: 29 if direction == 1:
28 if node.maxend <= threshold_up: 30 if node.maxend <= threshold_up:
29 if node.end == node.maxend: 31 if node.end == node.maxend:
30 report_func_up(node) 32 report_func_up(node)
31 elif node.right and node.left: 33 elif node.right and node.left:
32 if node.right.maxend == node.maxend: 34 if node.right.maxend == node.maxend:
58 get_closest_feature(node.left, direction, threshold_up, threshold_down, report_func_up, report_func_down) 60 get_closest_feature(node.left, direction, threshold_up, threshold_down, report_func_up, report_func_down)
59 else: 61 else:
60 if node.right: 62 if node.right:
61 get_closest_feature(node.right, direction, threshold_up, threshold_down, report_func_up, report_func_down) 63 get_closest_feature(node.right, direction, threshold_up, threshold_down, report_func_up, report_func_down)
62 64
65
63 def proximal_region_finder(readers, region, comments=True): 66 def proximal_region_finder(readers, region, comments=True):
64 """ 67 """
65 Returns an iterator that yields elements of the form [ <original_interval>, <closest_feature> ]. 68 Returns an iterator that yields elements of the form [ <original_interval>, <closest_feature> ].
66 Intervals are GenomicInterval objects. 69 Intervals are GenomicInterval objects.
67 """ 70 """
68 primary = readers[0] 71 primary = readers[0]
69 features = readers[1] 72 features = readers[1]
70 either = False 73 either = False
71 if region == 'Upstream': 74 if region == 'Upstream':
74 up, down = False, True 77 up, down = False, True
75 else: 78 else:
76 up, down = True, True 79 up, down = True, True
77 if region == 'Either': 80 if region == 'Either':
78 either = True 81 either = True
79 82
80 # Read features into memory: 83 # Read features into memory:
81 rightTree = quicksect.IntervalTree() 84 rightTree = quicksect.IntervalTree()
82 for item in features: 85 for item in features:
83 if type( item ) is GenomicInterval: 86 if type( item ) is GenomicInterval:
84 rightTree.insert( item, features.linenum, item ) 87 rightTree.insert( item, features.linenum, item )
85 88
86 for interval in primary: 89 for interval in primary:
87 if type( interval ) is Header: 90 if type( interval ) is Header:
88 yield interval 91 yield interval
89 if type( interval ) is Comment and comments: 92 if type( interval ) is Comment and comments:
90 yield interval 93 yield interval
94 end = int(interval.end) 97 end = int(interval.end)
95 strand = interval.strand 98 strand = interval.strand
96 if chrom not in rightTree.chroms: 99 if chrom not in rightTree.chroms:
97 continue 100 continue
98 else: 101 else:
99 root = rightTree.chroms[chrom] #root node for the chrom tree 102 root = rightTree.chroms[chrom] # root node for the chrom tree
100 result_up = [] 103 result_up = []
101 result_down = [] 104 result_down = []
102 if (strand == '+' and up) or (strand == '-' and down): 105 if (strand == '+' and up) or (strand == '-' and down):
103 #upstream +ve strand and downstream -ve strand cases 106 #upstream +ve strand and downstream -ve strand cases
104 get_closest_feature (root, 1, start, None, lambda node: result_up.append( node ), None) 107 get_closest_feature(root, 1, start, None, lambda node: result_up.append( node ), None)
105 108
106 if (strand == '+' and down) or (strand == '-' and up): 109 if (strand == '+' and down) or (strand == '-' and up):
107 #downstream +ve strand and upstream -ve strand case 110 #downstream +ve strand and upstream -ve strand case
108 get_closest_feature (root, 0, None, end-1, None, lambda node: result_down.append( node )) 111 get_closest_feature(root, 0, None, end - 1, None, lambda node: result_down.append( node ))
109 112
110 if result_up: 113 if result_up:
111 if len(result_up) > 1: #The results_up list has a list of intervals upstream to the given interval. 114 if len(result_up) > 1: # The results_up list has a list of intervals upstream to the given interval.
112 ends = [] 115 ends = []
113 for n in result_up: 116 for n in result_up:
114 ends.append(n.end) 117 ends.append(n.end)
115 res_ind = ends.index(max(ends)) #fetch the index of the closest interval i.e. the interval with the max end from the results_up list 118 res_ind = ends.index(max(ends)) # fetch the index of the closest interval i.e. the interval with the max end from the results_up list
116 else: 119 else:
117 res_ind = 0 120 res_ind = 0
118 if not(either): 121 if not(either):
119 yield [ interval, result_up[res_ind].other ] 122 yield [ interval, result_up[res_ind].other ]
120 123
121 if result_down: 124 if result_down:
122 if not(either): 125 if not(either):
123 #The last element of result_down will be the closest element to the given interval 126 #The last element of result_down will be the closest element to the given interval
124 yield [ interval, result_down[-1].other ] 127 yield [ interval, result_down[-1].other ]
125 128
126 if either and (result_up or result_down): 129 if either and (result_up or result_down):
127 iter_val = [] 130 iter_val = []
128 if result_up and result_down: 131 if result_up and result_down:
129 if abs(start - int(result_up[res_ind].end)) <= abs(end - int(result_down[-1].start)): 132 if abs(start - int(result_up[res_ind].end)) <= abs(end - int(result_down[-1].start)):
130 iter_val = [ interval, result_up[res_ind].other ] 133 iter_val = [ interval, result_up[res_ind].other ]
135 iter_val = [ interval, result_up[res_ind].other ] 138 iter_val = [ interval, result_up[res_ind].other ]
136 elif result_down: 139 elif result_down:
137 #The last element of result_down will be the closest element to the given interval 140 #The last element of result_down will be the closest element to the given interval
138 iter_val = [ interval, result_down[-1].other ] 141 iter_val = [ interval, result_down[-1].other ]
139 yield iter_val 142 yield iter_val
140 143
144
141 def main(): 145 def main():
142 options, args = doc_optparse.parse( __doc__ ) 146 options, args = doc_optparse.parse( __doc__ )
143 try: 147 try:
144 chr_col_1, start_col_1, end_col_1, strand_col_1 = parse_cols_arg( options.cols1 ) 148 chr_col_1, start_col_1, end_col_1, strand_col_1 = parse_cols_arg( options.cols1 )
145 chr_col_2, start_col_2, end_col_2, strand_col_2 = parse_cols_arg( options.cols2 ) 149 chr_col_2, start_col_2, end_col_2, strand_col_2 = parse_cols_arg( options.cols2 )
146 in1_gff_format = bool( options.gff1 ) 150 in1_gff_format = bool( options.gff1 )
147 in2_gff_format = bool( options.gff2 ) 151 in2_gff_format = bool( options.gff2 )
148 in_fname, in2_fname, out_fname, direction = args 152 in_fname, in2_fname, out_fname, direction = args
149 except: 153 except:
150 doc_optparse.exception() 154 doc_optparse.exception()
151 155
152 # Set readers to handle either GFF or default format. 156 # Set readers to handle either GFF or default format.
153 if in1_gff_format: 157 if in1_gff_format:
154 in1_reader_wrapper = GFFIntervalToBEDReaderWrapper 158 in1_reader_wrapper = GFFIntervalToBEDReaderWrapper
155 else: 159 else:
156 in1_reader_wrapper = NiceReaderWrapper 160 in1_reader_wrapper = NiceReaderWrapper
158 in2_reader_wrapper = GFFIntervalToBEDReaderWrapper 162 in2_reader_wrapper = GFFIntervalToBEDReaderWrapper
159 else: 163 else:
160 in2_reader_wrapper = NiceReaderWrapper 164 in2_reader_wrapper = NiceReaderWrapper
161 165
162 g1 = in1_reader_wrapper( fileinput.FileInput( in_fname ), 166 g1 = in1_reader_wrapper( fileinput.FileInput( in_fname ),
163 chrom_col=chr_col_1, 167 chrom_col=chr_col_1,
164 start_col=start_col_1, 168 start_col=start_col_1,
165 end_col=end_col_1, 169 end_col=end_col_1,
166 strand_col=strand_col_1, 170 strand_col=strand_col_1,
167 fix_strand=True ) 171 fix_strand=True )
168 g2 = in2_reader_wrapper( fileinput.FileInput( in2_fname ), 172 g2 = in2_reader_wrapper( fileinput.FileInput( in2_fname ),
169 chrom_col=chr_col_2, 173 chrom_col=chr_col_2,
170 start_col=start_col_2, 174 start_col=start_col_2,
171 end_col=end_col_2, 175 end_col=end_col_2,
172 strand_col=strand_col_2, 176 strand_col=strand_col_2,
173 fix_strand=True ) 177 fix_strand=True )
174 178
175 # Find flanking features. 179 # Find flanking features.
176 out_file = open( out_fname, "w" ) 180 out_file = open( out_fname, "w" )
177 try: 181 try:
178 for result in proximal_region_finder([g1,g2], direction): 182 for result in proximal_region_finder([g1, g2], direction):
179 if type( result ) is list: 183 if type( result ) is list:
180 line, closest_feature = result 184 line, closest_feature = result
181 # Need to join outputs differently depending on file types. 185 # Need to join outputs differently depending on file types.
182 if in1_gff_format: 186 if in1_gff_format:
183 # Output is GFF with added attribute 'closest feature.' 187 # Output is GFF with added attribute 'closest feature.'
184 188
185 # Invervals are in BED coordinates; need to convert to GFF. 189 # Invervals are in BED coordinates; need to convert to GFF.
186 line = convert_bed_coords_to_gff( line ) 190 line = convert_bed_coords_to_gff( line )
187 closest_feature = convert_bed_coords_to_gff( closest_feature ) 191 closest_feature = convert_bed_coords_to_gff( closest_feature )
188 192
189 # Replace double quotes with single quotes in closest feature's attributes. 193 # Replace double quotes with single quotes in closest feature's attributes.
190 out_file.write( "%s closest_feature \"%s\" \n" % 194 out_file.write( "%s closest_feature \"%s\" \n" %
191 ( "\t".join( line.fields ), \ 195 ( "\t".join( line.fields ),
192 "\t".join( closest_feature.fields ).replace( "\"", "\\\"" ) 196 "\t".join( closest_feature.fields ).replace( "\"", "\\\"" )
193 ) ) 197 ) )
194 else: 198 else:
195 # Output is BED + closest feature fields. 199 # Output is BED + closest feature fields.
196 output_line_fields = [] 200 output_line_fields = []
197 output_line_fields.extend( line.fields ) 201 output_line_fields.extend( line.fields )
198 output_line_fields.extend( closest_feature.fields ) 202 output_line_fields.extend( closest_feature.fields )
200 else: 204 else:
201 out_file.write( "%s\n" % result ) 205 out_file.write( "%s\n" % result )
202 except ParseError, exc: 206 except ParseError, exc:
203 fail( "Invalid file format: %s" % str( exc ) ) 207 fail( "Invalid file format: %s" % str( exc ) )
204 208
205 print "Direction: %s" %(direction) 209 print "Direction: %s" % (direction)
206 if g1.skipped > 0: 210 if g1.skipped > 0:
207 print skipped( g1, filedesc=" of 1st dataset" ) 211 print skipped( g1, filedesc=" of 1st dataset" )
208 if g2.skipped > 0: 212 if g2.skipped > 0:
209 print skipped( g2, filedesc=" of 2nd dataset" ) 213 print skipped( g2, filedesc=" of 2nd dataset" )
210 214