Package Biskit :: Module BisList
[hide private]
[frames] | no frames]

Source Code for Module Biskit.BisList

  1  ## Automatically adapted for numpy.oldnumeric Mar 26, 2007 by alter_code1.py 
  2   
  3  ## 
  4  ## Biskit, a toolkit for the manipulation of macromolecular structures 
  5  ## Copyright (C) 2004-2006 Raik Gruenberg & Johan Leckner 
  6  ## 
  7  ## This program is free software; you can redistribute it and/or 
  8  ## modify it under the terms of the GNU General Public License as 
  9  ## published by the Free Software Foundation; either version 2 of the 
 10  ## License, or any later version. 
 11  ## 
 12  ## This program is distributed in the hope that it will be useful, 
 13  ## but WITHOUT ANY WARRANTY; without even the implied warranty of 
 14  ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 15  ## General Public License for more details. 
 16  ## 
 17  ## You find a copy of the GNU General Public License in the file 
 18  ## license.txt along with this program; if not, write to the Free 
 19  ## Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
 20  ## 
 21  ## 
 22  ## last $Author: graik $ 
 23  ## last $Date: 2007/04/06 10:16:06 $ 
 24  ## $Revision: 2.15 $ 
 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   
44 -class BisListError( BiskitError ):
45 pass
46
47 -class ConditionError( BisListError ):
48 pass
49
50 -class AmbiguousMatch( ConditionError ):
51 pass
52
53 -class ItemNotFound( ConditionError):
54 pass
55
56 -class BisList:
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
88 - def __init__(self):
89 """ 90 Override but call. 91 """ 92 self.initVersion = t.dateString() + ' ' + self.version()
93
94 - def version( self ):
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 ): # abstract
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 ## def __setitem__(self, i, v ): # abstract 120 ## """ 121 ## c.__setitem( i, v ) <==> c[i] = v 122 ## Override! 123 ## """ 124 ## raise NotImplemented 125 126 ## def __getslice__( self, i, j ): # abstract 127 ## """ 128 ## c.__getslice__( i, j ) <==> c[ i : j ] 129 ## Override! 130 ## i - int, starting position (including) 131 ## j - int, ending position (excluding) 132 ## -> new instance with only the given range of items 133 ## """ 134 ## raise NotImplemented 135 136 ## def __len__( self ): # abstract 137 ## """ 138 ## c.__len__() <==> len( c ) 139 ## Override! 140 ## """ 141 ## raise NotImplemented 142 143
144 - def __add__( self, other ):
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
159 - def __iadd__( self, other ):
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
173 - def extend( self, other ): # abstract
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
184 - def append( self, v ): # abstract
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
195 - def keys( self ):
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 ): # abstract
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
301 - def filterRange( self, key, vLow, vHigh ):
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
323 - def filterEqual( self, key, lst ):
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
339 - def filterFunct( self, f ):
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
393 - def argmax( self, key ):
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
416 - def argmin( self, key ):
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
439 - def getIndex( self, key, value ):
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
463 - def getItem( self, key, value ):
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
479 - def toDict( self, key ):
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
501 - def toList( self ):
502 """ 503 @return: simple python list of items 504 @rtype: [ item ] 505 """ 506 return list( self ) 507 508
509 - def __maskNone( self, l1, l2 ):
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
585 - def plotArray( self, xkey, *ykey, **arg ):
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 ## empty test 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