4
4
5
5
from functools import reduce , total_ordering
6
6
from pyparsing import (Literal , nums , Word , Forward , Optional , Regex ,
7
- infixNotation , delimitedList , opAssoc , alphas , ParseException )
8
- from pyvdrm .drm import AsiExpr , AsiBinaryExpr , AsiUnaryExpr , DRMParser
7
+ infixNotation , delimitedList , opAssoc , ParseException )
8
+
9
+ from pyvdrm .drm import MissingPositionError
10
+ from pyvdrm .drm import AsiExpr , AsiBinaryExpr , DRMParser
9
11
from pyvdrm .vcf import MutationSet
10
12
13
+
11
14
def update_flags (fst , snd ):
12
15
for k in snd :
13
16
if k in fst :
14
17
fst [k ].append (snd [k ])
15
18
else :
16
- fst [k ] = snd [k ] # this chould be achieved with a defaultdict
19
+ fst [k ] = snd [k ] # this could be achieved with a defaultdict
17
20
return fst
18
21
19
22
20
- def maybe_foldl (func , noneable ):
21
- """Safely fold a function over a potentially empty list of
22
- potentially null values"""
23
- if noneable is None :
24
- return None
25
- clean = [x for x in noneable if x is not None ]
26
- if not clean :
27
- return None
28
- return reduce (func , clean )
29
-
30
-
31
- def maybe_map (func , noneable ):
32
- if noneable is None :
33
- return None
34
- r_list = []
35
- for x in noneable :
36
- if x is None :
37
- continue
38
- result = func (x )
39
- if result is None :
40
- continue
41
- r_list .append (result )
42
- if not r_list :
43
- return None
44
- return r_list
45
-
46
-
47
23
@total_ordering
48
24
class Score (object ):
49
25
"""Encapsulate a score and the residues that support it"""
@@ -65,13 +41,15 @@ def __init__(self, score, residues, flags=None):
65
41
66
42
def __add__ (self , other ):
67
43
flags = update_flags (self .flags , other .flags )
68
- return Score (self .score + other .score , self .residues | other .residues ,
69
- flags )
44
+ return Score (self .score + other .score ,
45
+ self .residues | other .residues ,
46
+ flags )
70
47
71
48
def __sub__ (self , other ):
72
49
flags = update_flags (self .flags , other .flags )
73
- return Score (self .score - other .score , self .residues | other .residues ,
74
- flags )
50
+ return Score (self .score - other .score ,
51
+ self .residues | other .residues ,
52
+ flags )
75
53
76
54
def __repr__ (self ):
77
55
return "Score({!r}, {!r})" .format (self .score , self .residues )
@@ -166,11 +144,11 @@ def __call__(self, mutations):
166
144
if len (self .children ) == 4 :
167
145
operation , _ , flag , _ = self .children
168
146
flags [flag ] = []
169
- score = 0 # should be None
147
+ score = 0 # should be None
170
148
171
149
elif len (self .children ) == 3 :
172
150
operation , minus , score = self .children
173
- if minus != '-' : # this is parsing the expression twice, refactor
151
+ if minus != '-' : # this is parsing the expression twice, refactor
174
152
raise ValueError
175
153
score = - 1 * int (score )
176
154
@@ -197,10 +175,22 @@ class ScoreList(AsiExpr):
197
175
def __call__ (self , mutations ):
198
176
operation , * rest = self .children
199
177
if operation == 'MAX' :
200
- return maybe_foldl (max , [f (mutations ) for f in rest ])
201
-
202
- # the default operation is sum
203
- return maybe_foldl (lambda x , y : x + y , [f (mutations ) for f in self .children ])
178
+ terms = rest
179
+ func = max
180
+ else :
181
+ # the default operation is sum
182
+ terms = self .children
183
+ func = sum
184
+ scores = [f (mutations ) for f in terms ]
185
+ matched_scores = [score .score for score in scores if score .score ]
186
+ residues = reduce (lambda x , y : x | y ,
187
+ (score .residues for score in scores ))
188
+ flags = {}
189
+ for score in scores :
190
+ flags .update (score .flags )
191
+ return Score (bool (matched_scores ) and func (matched_scores ),
192
+ residues ,
193
+ flags )
204
194
205
195
206
196
class SelectFrom (AsiExpr ):
@@ -215,14 +205,12 @@ def __call__(self, mutations):
215
205
operation , * rest = self .children
216
206
# the head of the arg list must be an equality expression
217
207
218
- scored = list ( maybe_map ( lambda f : f (mutations ), rest ))
219
- passing = len ( scored )
208
+ scored = [ f (mutations ) for f in rest ]
209
+ passing = sum ( bool ( score . score ) for score in scored )
220
210
221
- if operation (passing ):
222
- return Score (True , maybe_foldl (
223
- lambda x , y : x .residues .union (y .residues ), scored ))
224
- else :
225
- return None
211
+ return Score (operation (passing ),
212
+ reduce (lambda x , y : x | y ,
213
+ (item .residues for item in scored )))
226
214
227
215
228
216
class AsiScoreCond (AsiExpr ):
@@ -232,7 +220,7 @@ class AsiScoreCond(AsiExpr):
232
220
233
221
def __call__ (self , args ):
234
222
"""Score conditions evaluate a list of expressions and sum scores"""
235
- return maybe_foldl ( lambda x , y : x + y , map ( lambda x : x ( args ), self . children ))
223
+ return sum (( f ( args ) for f in self . children ), Score ( False , set () ))
236
224
237
225
238
226
class AsiMutations (object ):
@@ -241,21 +229,26 @@ class AsiMutations(object):
241
229
def __init__ (self , _label = None , _pos = None , args = None ):
242
230
"""Initialize set of mutations from a potentially ambiguous residue
243
231
"""
244
- self .mutations = args and MutationSet ('' .join (args ))
232
+ self .mutations = MutationSet ('' .join (args ))
245
233
246
234
def __repr__ (self ):
247
235
if self .mutations is None :
248
236
return "AsiMutations()"
249
237
return "AsiMutations(args={!r})" .format (str (self .mutations ))
250
238
251
239
def __call__ (self , env ):
240
+ is_found = False
252
241
for mutation_set in env :
242
+ is_found |= mutation_set .pos == self .mutations .pos
253
243
intersection = self .mutations .mutations & mutation_set .mutations
254
244
if len (intersection ) > 0 :
255
245
return Score (True , intersection )
256
246
257
- # the mutationset has no members in the environment
258
- return None
247
+ if not is_found :
248
+ # Some required positions were not found in the environment.
249
+ raise MissingPositionError ('Missing position {}.' .format (
250
+ self .mutations .pos ))
251
+ return Score (False , set ())
259
252
260
253
261
254
class HCVR (DRMParser ):
@@ -305,8 +298,8 @@ def parser(self, rule):
305
298
selectstatement = select + select_quantifier + from_ + residue_list
306
299
selectstatement .setParseAction (SelectFrom )
307
300
308
- bool_ = Literal ('TRUE' ).suppress ().setParseAction (BoolTrue ) | \
309
- Literal ('FALSE' ).suppress ().setParseAction (BoolFalse )
301
+ bool_ = ( Literal ('TRUE' ).suppress ().setParseAction (BoolTrue ) |
302
+ Literal ('FALSE' ).suppress ().setParseAction (BoolFalse ) )
310
303
311
304
booleancondition = Forward ()
312
305
condition = residue | excludestatement | selectstatement | bool_
0 commit comments