source: trunk/python/selector.py@ 2874

Last change on this file since 2874 was 2874, checked in by Takeshi Nakazato, 11 years ago

New Development: No

JIRA Issue: Yes CAS-5858

Ready for Test: Yes

Interface Changes: No

What Interface Changed: Please list interface changes

Test Programs: List test programs

Put in Release Notes: Yes/No

Module(s): Module Names change impacts.

Description: Describe your changes here...

Import math module.


File size: 13.8 KB
Line 
1import re
2import math
3import string
4from asap._asap import selector as _selector, srctype
5from asap.utils import unique, _to_list
6
7class selector(_selector):
8 """
9 A selection object to be applied to scantables to restrict the
10 scantables to specific rows.
11 """
12 fields = ["pols", "ifs", "beams", "scans", "cycles", "name", "query", "types", "rows"]
13
14 def __init__(self, *args, **kw):
15 if len(args) == 1:
16 if isinstance(args[0], self.__class__) \
17 or isinstance(args[0], _selector):
18 _selector.__init__(self, args[0])
19 else:
20 raise TypeError("Argument can only be a selector object")
21 else:
22 _selector.__init__(self)
23 for k,v in kw.items():
24 if k in self.fields:
25 func = getattr(self, "set_%s" % k)
26 func(v)
27
28 def reset(self):
29 """
30 Unset all selections.
31 """
32 self._reset()
33
34 def is_empty(self):
35 """
36 Has anything been set?
37 """
38 return self._empty()
39
40 def set_polarisations(self, pols=[]):
41 """
42 Set the polarisations to be selected in the scantable.
43 Parameters:
44 pols: a list of integers of 0-3, or strings, e.g ["I","Q"].
45 Default [] is no selection
46 Example:
47 sel = selector()
48 # These are equivalent if data is 'linear'
49 sel.set_polarisations(["XX","Re(XY)"])
50 sel.set_polarisations([0,2])
51 # reset the polarisation selection
52 sel.set_polarisations()
53
54 """
55 vec = _to_list(pols, str) or _to_list(pols, int)
56 if isinstance(vec, list): # is an empty and/or valid vector
57 if len(vec) and isinstance(vec[-1],str):
58 self._setpolstrings(vec)
59 return
60 self._setpols(vec)
61 else:
62 raise TypeError('Unknown pol type. Please use [0,1...] or ["XX","YY"...]')
63
64 # for the americans
65 set_polarizations = set_polarisations
66 # for the lazy
67 set_pols = set_polarisations
68
69 def set_ifs(self, ifs=[]):
70 """
71 Set a sequence of IF numbers (0-based).
72 Parameters:
73 ifs: a list of integers. Default [] is to unset the selection.
74 """
75 vec = _to_list(ifs, int)
76 if isinstance(vec,list):
77 self._setifs(vec)
78 else:
79 raise TypeError('Unknown IFno type. Use lists of integers.')
80
81 def set_scans(self, scans=[]):
82 """
83 Set a sequence of Scan numbers (0-based).
84 Parameters:
85 scans: a list of integers. Default [] is to unset the selection.
86 """
87 vec = _to_list(scans, int)
88 if isinstance(vec,list):
89 self._setscans(vec)
90 else:
91 raise TypeError('Unknown Scan number type. Use lists of integers.')
92
93 def set_beams(self, beams=[]):
94 """
95 Set a sequence of Beam numbers (0-based).
96 Parameters:
97 beams: a list of integers. Default [] is to unset the selection.
98 """
99 vec = _to_list(beams, int)
100 if isinstance(vec,list):
101 self._setbeams(vec)
102 else:
103 raise TypeError('Unknown Beam number type. Use lists of integers.')
104
105 def set_cycles(self, cycles=[]):
106 """
107 Set a sequence of IF numbers (0-based).
108 Parameters:
109 cycless: a list of integers. Default [] is to unset the selection.
110 """
111 vec = _to_list(cycles, int)
112 if isinstance(vec,list):
113 self._setcycles(vec)
114 else:
115 raise TypeError('Unknown Cycle number type. Use lists of integers.')
116
117
118 def set_name(self, name):
119 """
120 Set a selection based on a name. This can be a unix pattern , e.g. "*_R"
121 Parameters:
122 name: a string containing a source name or pattern
123 Examples:
124 # select all reference scans which start with "Orion"
125 selection.set_name("Orion*_R")
126 """
127 if isinstance(name, str):
128 self._setname(name)
129 else:
130 raise TypeError('name must be a string')
131
132 def set_tsys(self, tsysmin=0.0, tsysmax=None):
133 """
134 Select by Tsys range.
135 Parameters:
136 tsysmin: the lower threshold. Default 0.0
137 tsysmax: the upper threshold. Default None.
138 Examples:
139 # select all spectra with Tsys <= 500.0
140 selection.set_tsys(tsysmax=500.0)
141
142 """
143 taql = "SELECT FROM $1 WHERE TSYS[0] >= %f" % (tsysmin)
144 if isinstance(tsysmax, float):
145 taql = taql + " AND TSYS[0] <= %f" % ( tsysmax)
146 self._settaql(taql)
147
148 def set_query(self, query):
149 """
150 Select by Column query. Power users only!
151 Example:
152 # select all off scans with integration times over 60 seconds.
153 selection.set_query("SRCTYPE == PSOFF AND INTERVAL > 60.0")
154 """
155 rx = re.compile("((SRCTYPE *[!=][=] *)([a-zA-Z.]+))", re.I)
156 for r in rx.findall(query):
157 sval = None
158 stype = r[-1].lower()
159 if stype.find('srctype.') == -1:
160 stype = ".".join(["srctype", stype])
161 try:
162 sval = eval(stype)
163 sval = "%s%d" % (r[1], sval)
164 except:
165 continue
166 query = query.replace(r[0], sval)
167 taql = "SELECT FROM $1 WHERE " + query
168 self._settaql(taql)
169
170 def set_order(self, order):
171 """
172 Set the order the scantable should be sorted by.
173 Parameters:
174 order: The list of column names to sort by in order
175 """
176 self._setorder(order)
177
178 def set_rows(self, rows=[]):
179 """
180 Set a sequence of row numbers (0-based). Power users Only!
181 NOTICE row numbers can be changed easily by sorting,
182 prior selection, etc.
183 Parameters:
184 rows: a list of integers. Default [] is to unset the selection.
185 """
186 vec = _to_list(rows, int)
187 if isinstance(vec,list):
188 self._setrows(vec)
189 else:
190 raise TypeError('Unknown row number type. Use lists of integers.')
191
192 def set_types(self, types=[]):
193 """
194 Set a sequence of source types.
195 Parameters:
196 types: a list of integers. Default [] is to unset the selection.
197 """
198 vec = _to_list(types, int)
199 if isinstance(vec,list):
200 self._settypes(vec)
201 else:
202 raise TypeError('Unknown row number type. Use lists of integers.')
203
204 def set_msselection_field(self, selection):
205 """
206 """
207 selection_list = map(string.strip, selection.split(','))
208 query_list = list(self.generate_query(selection_list))
209 query = 'SELECT FROM $1 WHERE ' + ' || '.join(query_list)
210 self._settaql(query)
211
212 def generate_query(self, selection_list):
213 for s in selection_list:
214 if s.isdigit() or re.match('^[<>]=?[0-9]*$', s) or \
215 re.match('^[0-9]+~[0-9]+$', s):
216 #print '"%s" is ID selection using < or <='%(s)
217 a = FieldIdRegexGenerator(s)
218 yield '(%s)'%(a.get_regex())
219 else:
220 #print '"%s" is UNIX style pattern match'%(s)
221 yield '(SRCNAME == pattern(\'%s\'))'%(s)
222
223 def get_scans(self):
224 return list(self._getscans())
225 def get_cycles(self):
226 return list(self._getcycles())
227 def get_beams(self):
228 return list(self._getbeams())
229 def get_ifs(self):
230 return list(self._getifs())
231 def get_pols(self):
232 return list(self._getpols())
233 def get_poltypes(self):
234 return list(self._getpoltypes())
235 def get_order(self):
236 return list(self._getorder())
237 def get_types(self):
238 return list(self._gettypes())
239 def get_rows(self):
240 return list(self._getrows())
241 def get_query(self):
242 prefix = "SELECT FROM $1 WHERE "
243 return self._gettaql().replace(prefix, "")
244
245 def get_name(self):
246 print "NYI"
247 s = self._gettaql()
248 return
249 def __str__(self):
250 out = ""
251 d = {"SCANNO": self.get_scans(),
252 "CYCLENO": self.get_cycles(),
253 "BEAMNO": self.get_beams(),
254 "IFNO": self.get_ifs(),
255 "Pol Type": self.get_poltypes(),
256 "POLNO": self.get_pols(),
257 "QUERY": self.get_query(),
258 "SRCTYPE": self.get_types(),
259 "ROWS": self.get_rows(),
260 "Sort Order": self.get_order()
261 }
262 for k,v in d.iteritems():
263 if v:
264 out += "%s: %s\n" % (k, v)
265 if len(out):
266 return out[:-1]
267 else:
268 return out
269
270 def __add__(self, other):
271 """
272 Merge two selections.
273 """
274 if self.is_empty():
275 return selector(other)
276 elif other.is_empty():
277 return selector(self)
278 union = selector()
279 gets = [[self._getscans(), other._getscans(), union._setscans],
280 [self._getcycles(), other._getcycles(),union._setcycles],
281 [self._getbeams(), other._getbeams(), union._setbeams],
282 [self._getifs(), other._getifs(), union._setifs],
283 [self._getpols(), other._getpols(), union._setpols]]
284 for v in gets:
285 vec = list(v[0]+v[1])
286 vec.sort()
287 v[2](unique(vec))
288 q = other.get_query()
289 qs = self.get_query()
290 if len(q) and len(qs):
291 union.set_query(qs +" AND " + q)
292 else:
293 if len(q):
294 union.set_query(q)
295 elif len(qs):
296 union.set_query(qs)
297 return union
298
299class FieldIdRegexGenerator(object):
300 def __init__(self, pattern):
301 if pattern.isdigit():
302 self.regex = 'FIELDNAME == regex(\'.+__%s$\')'%(pattern)
303 else:
304 self.regex = None
305 ineq = None
306 if pattern.find('<') >= 0:
307 ineq = '<'
308 s = pattern.strip().lstrip(ineq).lstrip('=')
309 if not s.isdigit():
310 raise RuntimeError('Invalid syntax: %s'%(pattern))
311 self.id = int(s) + (-1 if pattern.find('=') < 0 else 0)
312 self.template = string.Template('FIELDNAME == regex(\'.+__${reg}$\')')
313 elif pattern.find('>') >= 0:
314 ineq = '>'
315 s = pattern.strip().lstrip(ineq).lstrip('=')
316 if not s.isdigit():
317 raise RuntimeError('Invalid syntax: %s'%(pattern))
318 self.id = int(s) + (-1 if pattern.find('=') >= 0 else 0)
319 self.template = string.Template('FIELDNAME == regex(\'.+__[0-9]+$\') && FIELDNAME != regex(\'.+__${reg}$\')')
320 elif pattern.find('~') >= 0:
321 s = map(string.strip, pattern.split('~'))
322 if len(s) == 2 and s[0].isdigit() and s[1].isdigit():
323 self.id = [int(s[0])-1,int(s[1])]
324 else:
325 raise RuntimeError('Invalid syntax: %s'%(pattern))
326 self.template = string.Template('FIELDNAME == regex(\'.+__${reg}$\') && FIELDNAME != regex(\'.+__${optreg}$\')')
327 else:
328 raise RuntimeError('Invalid syntax: %s'%(pattern))
329 #print 'self.id=',self.id
330
331 def get_regex(self):
332 if self.regex is not None:
333 # 'X'
334 return self.regex
335 elif isinstance(self.id, list):
336 # 'X~Y'
337 return self.template.safe_substitute(reg=self.__compile(self.id[1]),
338 optreg=self.__compile(self.id[0]))
339 else:
340 # '<(=)X' or '>(=)X'
341 return self.template.safe_substitute(reg=self.__compile(self.id))
342
343 def __compile(self, idx):
344 pattern = ''
345 if idx >= 0:
346 if idx < 10:
347 num_digits = 1
348 else:
349 num_digits = int(math.log10(idx)) + 1
350 numerics = []
351 modulo = 10
352 divider = 1
353 for i in xrange(num_digits):
354 numerics.append(int((idx % modulo) / divider))
355 modulo *= 10
356 divider *= 10
357 #print 'numerics=',numerics
358 if num_digits == 1:
359 if numerics[0] == 0:
360 pattern = '0'
361 else:
362 pattern = '[0-%s]'%(numerics[0])
363 elif num_digits == 2:
364 pattern_list = ['[0-9]']
365 pattern_list.append('[1-%s][0-9]'%(numerics[1]-1))
366 if numerics[0] == 0:
367 pattern_list.append('%s%s'%(numerics[1],numerics[0]))
368 else:
369 pattern_list.append('%s[0-%s]'%(numerics[1],numerics[0]))
370 pattern = '(%s)'%('|'.join(pattern_list))
371 elif num_digits == 3:
372 pattern_list = ['[0-9]','[1-9][0-9]']
373 if numerics[2] == 2:
374 pattern_list.append('1[0-9][0-9]')
375 elif numerics[2] > 2:
376 pattern_list.append('[1-%s][0-9][0-9]'%(numerics[2]-1))
377 if numerics[1] == 0:
378 if numerics[0] == 0:
379 pattern_list.append('%s00'%(numerics[2]))
380 else:
381 pattern_list.append('%s0[0-%s]'%(numerics[2],numerics[0]))
382 else:
383 pattern_list.append('%s[0-%s][0-9]'%(numerics[2],numerics[1]-1))
384 if numerics[0] == 0:
385 pattern_list.append('%s%s%s'%(numerics[2],numerics[1],numerics[0]))
386 else:
387 pattern_list.append('%s%s[0-%s]'%(numerics[2],numerics[1],numerics[0]))
388 pattern = '(%s)'%('|'.join(pattern_list))
389 else:
390 raise RuntimeError('ID > 999 is not supported')
391 pattern = ''
392 else:
393 raise RuntimeError('ID must be >= 0')
394 return pattern
Note: See TracBrowser for help on using the repository browser.