1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 """
26 organise, sort, and filter collection of dictionaries or similar objects
27 S{->} abstract base class (aka interface)
28 """
29
30 import numpy.oldnumeric as N
31 import types
32
33 import Biskit.tools as t
34 import ColorSpectrum as C
35 from Biskit import EHandler
36 from Errors import BiskitError
37
38 try:
39 import biggles
40 except:
41 bigges = 0
42
43
45 pass
46
48 pass
49
51 pass
52
54 pass
55
57 """
58 An *abstract* base class that lays out the basic interface for
59 collections of dictionary-like objects. To the outside, it behaves
60 like a list, albeit one with a second dimension addressed by
61 'keys'. You could think of BisList as a kind of two-dimensional
62 array or a dictionary of lists. However, no assumptions are yet
63 made as to the internal data structure. BisList provides uniform
64 methods for sorting, filtering, extraction and combination of
65 sub-lists (take, compress), and plotting.
66
67 Classes derived from BisList have to override several
68 methods to be functional (a NotImplementedError is raised otherwise):
69
70 * getValue, extend, append, take, keys,
71 * __len__, __setitem__, __getslice__
72
73 The latter 3 are not yet defined in BisList (no
74 NotImplementedError) but are nevertheless required. They can also
75 be provided from the python built-in list type via multiple
76 inheritence (see L{Biskit.DictList}, for an example).
77
78 That means there are two ways of implementing BisList.
79 1. via multiple inheritence from BisList (first!) and list
80 -> only L{getValue}, L{extend}, L{append} and L{take} need to be
81 overriden.
82 2. inheritence from BisList only
83 -> the __xxx__ methods have to be implemented, too.
84
85 See L{DictList} for an example of strategy 1.
86 """
87
89 """
90 Override but call.
91 """
92 self.initVersion = t.dateString() + ' ' + self.version()
93
95 """
96 @return: CVS version of this class (see also attribute initVersion)
97 @rtype: str
98 """
99 return 'BisList $Revision: 2.15 $'
100
101
102 - def getValue( self, i, key, default=None ):
103 """
104 Get the value of a dictionary entry of a list item.
105 B{Override!}
106
107 @param i: position in collection
108 @type i: int
109 @param key: attribute key
110 @type key: any
111 @param default: return value if key is not found [None]
112 @type default: any
113
114 @return: any
115 @rtype: any
116 """
117 raise NotImplementedError
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
145 """
146 c.__add__( other ) <==> c + other
147
148 @param other: other instance
149 @type other: instance
150
151 @return: new instance with one collection appended to the other
152 @rtype: any
153 """
154 r = self.__class__( self )
155 r.extend( other )
156 return r
157
158
160 """
161 c.__iadd__( other ) <==> c += other
162
163 @param other: other instance
164 @type other: instance
165
166 @return: this instance with other appended
167 @rtype: any
168 """
169 self.extend( other )
170 return self
171
172
174 """
175 Add all items of other to (the end of) this instance.
176 B{Override!}
177
178 @param other: other instance
179 @type other: instance
180 """
181 raise NotImplementedError
182
183
185 """
186 Append a single item to the end of this list.
187 B{Override!}
188
189 @param v: any (left to the implementing class)
190 @type v: any
191 """
192 raise NotImplementedError
193
194
196 """
197 @return: attribute keys used by the current items.
198 @rtype: [ any ]
199 """
200 raise NotImplementedError
201
202
203 - def argsort( self, sortKey, cmpfunc=cmp ):
204 """
205 Sort by values of a certain item attribute.
206
207 @param sortKey: attribute key
208 @type sortKey: any
209 @param cmpfunc: used for comparing values; cmpfunc(v1,v2)
210 -> -1,0,1 [built-in cmp]
211 @type cmpfunc: function
212
213 @return: indices after sorting (the collection itself is not sorted)
214 @rtype: [ int ]
215 """
216 pairs = [(self.getValue(i,sortKey),i) for i in range(0, len(self))]
217 pairs.sort( cmpfunc )
218 return [ x[1] for x in pairs ]
219
220
221 - def take( self, indices, deepcopy=0 ):
222 """
223 Extract certain items in a certain order.
224 B{Override!}
225
226 @param indices: positions
227 @type indices: [ int ]
228 @param deepcopy: deepcopy items (default: 0)
229 @type deepcopy: 0|1
230
231 @return: new instance (or sub-class) with specified items
232 @rtype: instance
233 """
234 raise NotImplementedError
235
236
237 - def compress( self, mask, deepcopy=0 ):
238 """
239 Extract certain items.
240
241 @param mask: mask of positions; len( mask ) == len( self )
242 @type mask: [ 1|0 ]
243 @param deepcopy: deepcopy items (default: 0)
244 @type deepcopy: 1|0
245
246 @return: new instance (or sub-class) with specified items
247 @rtype: instance
248 """
249 return self.take( N.nonzero( mask ), deepcopy=deepcopy )
250
251
252 - def sortBy( self, sortKey, cmpfunc=cmp ):
253 """
254 Use::
255 sortBy( sortKey ) -> new instance sorted by item[ sortKey ]
256
257 @param sortKey: key for item attribute
258 @type sortKey: any
259 @param cmpfunc: comparison function
260 @type cmpfunc: function
261
262 @return: new instance (or sub-class) sorted by item
263 @rtype: instance
264 """
265 return self.take( self.argsort( sortKey, cmpfunc ))
266
267
268 - def valuesOf(self, key, default=None, indices=None, unique=0 ):
269 """
270 Get all values assigned to a certain key of all or some
271 items. If unique==0, the result is guaranteed to have the same
272 length as the collection (or the list of given
273 indices). Missing values are replaced by default (None).
274
275 @param key: key for item attribute
276 @type key: any
277 @param default: default value if key is not found (default: None)
278 @type default: any
279 @param indices: indices defining a subset of this list (default: None)
280 @type indices: list of int OR None
281 @param unique: report each value only once (set union), (default 0)
282 @type unique: 1|0
283
284 @return: list of values
285 @rtype: list
286 """
287 l = self
288 if indices != None:
289 l = self.take( indices )
290
291 if not unique:
292 return [ self.getValue( i,key,default) for i in range(len(l)) ]
293
294 r = []
295 for i in range( len(l) ):
296 if self.getValue( i, key, default) not in r:
297 r += [ self.getValue( i, key, default ) ]
298 return r
299
300
302 """
303 Get indices of items where vLow <= item[ key ] <= vHigh.
304
305 @param key: item attribute
306 @type key: any
307 @param vLow: lower bound
308 @type vLow: any
309 @param vHigh: upper bound
310 @type vHigh: any
311
312 @return: array of int
313 @rtype: array
314 """
315 vLst = self.valuesOf( key )
316
317 maskL = N.greater_equal( vLst, vLow )
318 maskH = N.less_equal( vLst, vHigh )
319
320 return N.nonzero( maskL * maskH )
321
322
324 """
325 Get indices of items for which item[ key ] in lst.
326
327 @param key: item attribute
328 @type key: any
329 @param lst: [ any ], list of allowed values
330 @type lst: list
331
332 @return: array of int
333 @rtype: array
334 """
335 mask = [ self.getValue( i,key) in lst for i in range( len(self)) ]
336 return N.nonzero( mask )
337
338
340 """
341 Get indices of items for which f( item ) == 1.
342
343 @param f: f must take a single item as argument and return 1 or 0
344 @type f: function
345
346 @return: array of int
347 @rtype: array
348 """
349 mask = [ f( c ) for c in self ]
350 return N.nonzero( mask )
351
352
353 - def filter( self, key, cond ):
354 """
355 Extract items matching condition.
356
357 @param key: item attribute (not used if cond is function )
358 @type key: any
359 @param cond: conditon::
360 - (vLow, vHigh) -> vLow <= item[ key ] <= vHigh
361 - list -> item[ key ] in cond
362 - function -> cond( c ) == 1
363 @type cond: any
364
365 @return: new instance (or sub-class)
366 @rtype: instance
367
368 @raise ConditionError: if cond is neither list nor tuple nor function:
369 """
370 indices = None
371
372 if type( cond ) == tuple:
373
374 indices = self.filterRange( key, cond[0], cond[1] )
375
376 if type( cond ) == list:
377
378 indices = self.filterEqual( key, cond )
379
380 if type( cond ) == types.FunctionType:
381
382 indices = self.filterFunct( cond )
383
384 if indices == None:
385 try:
386 indices = self.filterEqual( key, [cond] )
387 except:
388 raise ConditionError( "Can't interprete filter condition.")
389
390 return self.take(indices)
391
392
394 """
395 @param key: item attribute
396 @type key: any
397
398 @return: index of item with highest item[key] value
399 @rtype: int
400 """
401 vLst = self.valuesOf( key )
402 return N.argmax( vLst )
403
404
405 - def max( self, key ):
406 """
407 @param key: item attribute
408 @type key: any
409
410 @return: item with highest item[key] value
411 @rtype: float
412 """
413 return self[ self.argmax(key) ]
414
415
417 """
418 @param key: item attribute
419 @type key: any
420
421 @return: index of item with lowest item[infokey] value
422 @rtype: int
423 """
424 vLst = self.valuesOf( key )
425 return N.argmin( vLst )
426
427
428 - def min( self, key ):
429 """
430 @param key: item attribute
431 @type key: any
432
433 @return: item with lowest item[key] value
434 @rtype: float
435 """
436 return self[ self.argmin( key ) ]
437
438
440 """
441 @param key: item attribute
442 @type key: any
443 @param value: item value
444 @type value: any
445
446 @return: position of item for which item[key] == value
447 @rtype: int
448
449 @raise AmbiguousMatch: ItemNotFound,
450 if there are more or less than 1 matches
451 """
452 l = self.filterEqual( key, [ value ] )
453
454 if len( l ) == 1:
455 return l[0]
456
457 if len( l ) > 1:
458 raise AmbiguousMatch('More than one Complexes match.')
459
460 raise ItemNotFound("No matching item.")
461
462
464 """
465 @param key: item attribute
466 @type key: any
467 @param value: item value
468 @type value: any
469
470 @return: item for which item[key] == value
471 @rtype: any
472
473 @raise AmbiguousMatch: ItemNotFound,
474 if there are more or less than 1 matches
475 """
476 return self[ self.getIndex( key, value ) ]
477
478
480 """
481 Convert collection into dict indexed by the value of a certain item
482 attribute. If several items have the same value, the result will have
483 a list registered for this key.
484
485 C{ EXAMPLE: lst.toDict('soln') }
486 C{ -> {soln1:Item, soln3:Item, solnN:Item} }
487
488 @param key: item attribute
489 @type key: any
490
491 @return: { info1:dict, info2:dict, info3:[dict, dict].. }
492 @rtype: dict
493 """
494 result = {}
495 for i in range( len(self)):
496 t.dictAdd( result, self.getValue( i, key), self[i] )
497
498 return result
499
500
502 """
503 @return: simple python list of items
504 @rtype: [ item ]
505 """
506 return list( self )
507
508
510 """
511 Take out positions from l1 and l2 that are None in either of them.
512
513 @param l1: first list
514 @type l1: list
515 @param l2: second list
516 @type l2: list
517
518 @return: modified lists
519 @rtype: (l1, l2)
520 """
521 r1, r2 = [],[]
522
523 for i in range( len(l1)):
524 if l1[i] != None and l2[i] != None:
525 r1 += [ l1[i] ]
526 r2 += [ l2[i] ]
527
528 return r1, r2
529
530
531 - def plot( self, xkey, *ykey, **arg ):
532 """
533 Plot pairs of item values. The additional arg arguments are handed
534 over to biggles.Points(). The special xkey value 'index' uses the
535 position of each item as x-axis. If only one key is given,
536 it is taken as ykey and the x-axis is the index of each item
537 (xkey='index').
538
539 C{ EXAMPLE: plot( xkey, [ykey1, ykey2..],[arg1=x, arg2=y]) }
540 C{ -> biggles.FramedPlot }
541
542 @param xkey: key for x-values
543 @type xkey: any
544 @param ykey: key for y-values
545 @type ykey: any
546 @param arg: arguments handed over to biggles.Points()
547 @type arg: any
548
549 @return: Biggles.FramedPlot, display with show() !
550 @rtype: Biggles.FramedPlot
551 """
552 if not biggles:
553 raise ImportError, 'biggles module could not be imported.'
554
555 if len(ykey) == 0:
556 xkey, ykey = 'index', [ xkey ]
557
558 plot = biggles.FramedPlot()
559
560 plot.xlabel = xkey
561
562 colors = C.colorRange( len( ykey ) )
563
564 if not 'size' in arg:
565 arg['size'] = 1
566
567 for i in range( len(ykey)):
568
569 x = range( len( self ) )
570 if xkey != 'index':
571 x = self.valuesOf( xkey )
572
573 y = self.valuesOf( ykey[i] )
574
575 x, y = self.__maskNone( x, y )
576
577 plot.add( biggles.Points( x, y, color=colors[i], **arg ) )
578
579 plot.add( biggles.PlotLabel( 0.2, 0.95-i/8.0, ykey[i],
580 color=colors[i] ) )
581
582 return plot
583
584
586 """
587 Plot pairs of item values.
588
589 C{ EXAMPLE: plot( xkey, [ykey1, ykey2..],[arg1=x, arg2=y]) }
590 C{ -> biggles.FramedPlot }
591
592 @param xkey: key for x-values
593 @type xkey: any
594 @param ykey: key for y-values
595 @type ykey: any
596 @param arg: arguments handed over to biggles.Points()
597 @type arg: any
598
599 @return: Biggles.FramedArray, display with show()
600 @rtype: Biggles.FramedArray
601 """
602 if not biggles:
603 raise ImportError, 'biggles module could not be imported.'
604
605 if len(ykey) == 0:
606 xkey, ykey = 'index', [ xkey ]
607
608 plot = biggles.FramedArray( len(ykey),1 )
609
610 plot.xlabel = xkey
611
612 colors = C.colorRange( len( ykey ) )
613
614 if not 'size' in arg:
615 arg['size'] = 1
616
617 for i in range( len(ykey)):
618
619 x = range( len( self ) )
620 if xkey != 'index':
621 x = self.valuesOf( xkey )
622
623 y = self.valuesOf( ykey[i] )
624
625 x, y = self.__maskNone( x, y )
626
627 plot[i,0].add( biggles.Points( x, y, color=colors[i], **arg ) )
628
629 plot[i,0].add( biggles.PlotLabel( 0.2, 0.95, ykey[i],
630 color=colors[i] ) )
631
632 return plot
633
634
635
636
637 import Biskit.test as BT
638
639 -class Test(BT.BiskitTest):
640 """Mock test, the BisList is tested in L{Biskit.DictList}"""
641 pass
642