Package PyDSTool :: Module Points
[hide private]
[frames] | no frames]

Source Code for Module PyDSTool.Points

   1  """Point and Pointset enhanced array classes.
 
   2  
 
   3  (Objects of both classes are mutable.)
 
   4  
 
   5      Robert Clewley, February 2006
 
   6  """ 
   7  
 
   8  # ----------------------------------------------------------------------------
 
   9  
 
  10  from __future__ import division 
  11  
 
  12  ## PyDSTool imports
 
  13  from utils import * 
  14  from common import * 
  15  from errors import * 
  16  from parseUtils import symbolMapClass, mapNames 
  17  
 
  18  ## Other imports
 
  19  from numpy import Inf, NaN, isfinite, array2string, r_, c_, \
 
  20      less, greater, linalg, shape, array, argsort, savetxt, \
 
  21      take, zeros, transpose, resize, indices, concatenate, rank 
  22  
 
  23  from numpy import complex, complexfloating, int, integer, \
 
  24       float, floating, float64, complex128, int32 
  25  from numpy import any, all, alltrue, sometrue, ndarray 
  26  
 
  27  
 
  28  import sys 
  29  from copy import copy, deepcopy 
  30  
 
  31  
 
  32  __all__ = ['Point', 'Pointset', 'isparameterized', 'pointsToPointset',
 
  33             'PointInfo', 'makeNonParameterized', 'arrayToPointset',
 
  34             'VarCaller', 'comparePointCoords', 'importPointset',
 
  35             'exportPointset', 'mergePointsets', 'padPointset',
 
  36             'export_pointset_to_CSV'] 
  37  
 
  38  #----------------------------------------------------------------------------
 
  39  
 
  40  
 
41 -class VarCaller(object):
42 """Wrapper for Variable type to call Pointset and return array type.""" 43
44 - def __init__(self, pts):
45 if isinstance(pts, (Point, Pointset)): 46 self.pts = pts 47 else: 48 raise TypeError("Invalid type for pts argument")
49
50 - def __call__(self, x):
51 return self.pts(x).toarray()
52 53 54 #---------------------------------------------------------------------------- 55 56 57 # for internal use 58 point_keys = ['coorddict', 'coordarray', 'coordtype', 'norm', 'labels'] 59 60
61 -class Point(object):
62 """N-dimensional point class.""" 63 64 # Contains an ordered list of names for the coordinates (to 65 # suggest how the points belong to a particular space specified 66 # using a particular basis)
67 - def __init__(self, kwd=None, **kw):
68 if kwd is not None: 69 if kw != {}: 70 raise ValueError("Cannot mix keyword dictionary and keywords") 71 kw = kwd 72 self._parameterized = False 73 self.labels = {} 74 if intersect(kw.keys(), point_keys) == []: 75 # creating Point from dictionary 76 temp_kw = {} 77 temp_kw['coorddict'] = copy(kw) 78 kw = copy(temp_kw) 79 if 'coorddict' in kw: 80 coorddict = {} 81 try: 82 ct = kw['coordtype'] 83 except KeyError: 84 self.coordtype = float 85 else: 86 try: 87 self.coordtype = _num_equivtype[ct] 88 except KeyError: 89 raise TypeError('Coordinate type %s not valid for Point'%str(ct)) 90 for c, v in kw['coorddict'].iteritems(): 91 if not isinstance(c, str): 92 c_key = repr(c) 93 else: 94 c_key = c 95 if isinstance(v, list): 96 coorddict[c_key] = array(v, self.coordtype) 97 elif isinstance(v, ndarray): 98 if len(v) == 1: 99 coorddict[c_key] = v[0] 100 else: 101 coorddict[c_key] = array(v) 102 assert compareNumTypes(self.coordtype, coorddict[c_key].dtype.type), \ 103 'type mismatch' 104 elif isinstance(v, _float_types): 105 assert compareNumTypes(self.coordtype, _float_types), \ 106 'type mismatch' 107 coorddict[c_key] = array([v], self.coordtype) 108 elif isinstance(v, _int_types): 109 assert compareNumTypes(self.coordtype, _real_types), \ 110 'type mismatch' 111 coorddict[c_key] = array([v], self.coordtype) 112 ## elif isinstance(v, _complex_types): 113 ## assert compareNumTypes(self.coordtype, complex), 'type mismatch' 114 ## coorddict[c_key] = array([v], self.coordtype) 115 else: 116 raise TypeError("Must pass numeric type or sequence of " 117 "numeric types") 118 self.coordnames = coorddict.keys() 119 # only way to order dictionary keys for array is to sort 120 self.coordnames.sort() 121 self.dimension = len(self.coordnames) 122 datalist = [] 123 for c in self.coordnames: 124 assert not isinstance(coorddict[c], (list, tuple)), 'type mismatch' 125 datalist.append(coorddict[c][0]) 126 self.coordarray = array(datalist, self.coordtype) 127 r = rank(self.coordarray) 128 if r == 1: 129 pass 130 elif r == 0: 131 self.coordarray = self.coordarray.ravel() 132 else: 133 raise ValueError("Invalid rank for coordinate array: %i"%r) 134 assert self.dimension == self.coordarray.shape[0], "Invalid coord array" 135 elif 'coordarray' in kw: 136 # 'coordtype' key is optional unless 'array' is actually a list, 137 # when this key specifies the internal Python to use 138 if isinstance(kw['coordarray'], ndarray): 139 # use 'array' constructor to ensure that copy is made of array 140 # in case either original or new array is independently changed. 141 array_temp = array(kw['coordarray']) 142 try: 143 self.coordtype = _num_equivtype[array_temp.dtype.type] 144 except KeyError: 145 raise TypeError('Coordinate type %s not valid for Point'%str(ct)) 146 elif isinstance(kw['coordarray'], list): 147 try: 148 self.coordtype = _num_equivtype[kw['coordtype']] 149 except KeyError: 150 raise TypeError('Coordinate type %s not valid for Point'%str(ct)) 151 array_temp = array(kw['coordarray'], self.coordtype) 152 else: 153 raise TypeError('Coordinate type %s not valid for Point'%str(type(kw['coordarray']))) 154 r = rank(array_temp) 155 if r == 1: 156 self.coordarray = array_temp 157 elif r == 0: 158 self.coordarray = array_temp.ravel() 159 else: 160 raise ValueError("Invalid rank for coordinate array: %i"%r) 161 self.dimension = self.coordarray.shape[0] 162 if 'coordnames' in kw: 163 if isinstance(kw['coordnames'], str): 164 coordnames = [kw['coordnames']] 165 else: 166 coordnames = kw['coordnames'] 167 else: 168 coordnames = [str(cix) for cix in range(self.dimension)] 169 if len(coordnames) != self.dimension: 170 print "Point initialization error:" 171 print "Found coord names: ", coordnames, \ 172 "(dimension = %s)"%len(coordnames) 173 print "vs. data dimension =", self.dimension 174 raise ValueError("Mismatch between number of coordnames and " 175 "dimension of data") 176 cs = array(coordnames) 177 order = cs.argsort() 178 self.coordnames = cs[order].tolist() 179 self.coordarray = self.coordarray[order] 180 else: 181 raise ValueError("Missing coord info in keywords") 182 assert isUniqueSeq(self.coordnames), 'Coordinate names must be unique' 183 self.makeIxMaps() 184 if 'norm' in kw: 185 if kw['norm'] == 0: 186 raise ValueError("Norm order for point cannot be zero") 187 self._normord = kw['norm'] 188 else: 189 self._normord = 2 190 # extra information (for special bifurcation point data) 191 if 'labels' in kw: 192 self.addlabel(kw['labels'])
193 194
195 - def mapNames(self, themap):
196 """Map coordinate names and label(s), using a symbol 197 map of class symbolMapClass.""" 198 new_coordnames = array(themap(self.coordnames)) 199 assert isUniqueSeq(new_coordnames.tolist()), 'Coordinate names must be unique' 200 order = argsort(new_coordnames) 201 self.coordarray = self.coordarray[order] 202 self.coordnames = new_coordnames[order].tolist() 203 self.makeIxMaps() 204 # the following call will be inherited by Pointset, and 205 # works on Point labels-as-dict and Pointset labels-as- 206 # PointInfo objects, as the latter have their own 207 # mapNames method which will get called. 208 self.labels = mapNames(themap, self.labels)
209 210
211 - def addlabel(self, label):
212 if label is None: 213 pass 214 elif isinstance(label, str): 215 self.labels = {label: {}} 216 elif isinstance(label, tuple) and len(label)==2: 217 if isinstance(label[0], str) and isinstance(label[1], dict): 218 self.labels[label[0]] = label[1] 219 elif isinstance(label, dict): 220 self.labels = label 221 else: 222 raise TypeError("Point label must be a string, a pair, or a dict")
223 224
225 - def removelabel(self):
226 self.labels = {}
227 228
229 - def makeIxMaps(self):
230 self._name_ix_map = dict(zip(self.coordnames, range(self.dimension))) 231 self._ix_name_map = copy(self.coordnames)
232 233
234 - def todict(self, aslist=False):
235 """Convert Point to a dictionary of array values (or of list with aslist=True).""" 236 if aslist: 237 return dict(zip(self._ix_name_map, self.coordarray.tolist())) 238 else: 239 return dict(zip(self._ix_name_map, self.coordarray))
240
241 - def __contains__(self, coord):
242 return coord in self.coordnames
243
244 - def __delitem__(self, k):
245 raise NotImplementedError
246
247 - def get(self, coord, d=None):
248 if coord in self.coordnames: 249 return self.__call__(coord) 250 else: 251 return d
252
253 - def update(self, d):
254 for k, v in d.iteritems(): 255 self.coordarray[self._map_names_to_ixs(k)] = v
256
257 - def items(self):
258 return zip(self._ix_name_map, self.coordarray)
259
260 - def iteritems(self):
261 return iter(zip(self._ix_name_map, self.coordarray))
262
263 - def values(self):
264 return self.coordarray.tolist()
265
266 - def itervalues(self):
267 return iter(self.coordarray.tolist())
268
269 - def keys(self):
270 return self._ix_name_map
271
272 - def iterkeys(self):
273 return iter(self._ix_name_map)
274
275 - def has_key(self, k):
276 return k in self.coordnames
277 278
279 - def _map_names_to_ixs(self, namelist):
280 try: 281 try: 282 # single string 283 return self._name_ix_map[namelist] 284 except TypeError: 285 # list of strings 286 return [self._name_ix_map[n] for n in namelist] 287 except KeyError, e: 288 raise PyDSTool_KeyError("Name not found: "+str(e))
289 290
291 - def __len__(self):
292 return self.dimension
293 294
295 - def _force_coords_to_ixlist(self, x):
296 if x is None: 297 return range(self.dimension) 298 elif x in xrange(self.dimension): 299 # only used for recursive calls 300 return [x] 301 elif x in self.coordnames: 302 # only used for recursive calls 303 return [self._name_ix_map[x]] 304 elif isinstance(x, _seq_types): 305 if len(x) == 0: 306 return range(self.dimension) 307 else: 308 return [self._force_coords_to_ixlist(el)[0] for el in x] 309 elif isinstance(x, slice): 310 stop = x.stop or self.dimension 311 s1, s2, s3 = x.indices(stop) 312 if s1 < 0 or s2 > self.dimension or s1 >= self.dimension: 313 raise ValueError("Slice index out of range") 314 return range(s1, s2, s3) 315 else: 316 raise ValueError("Invalid coordinate / index: %s"%str(x) + \ 317 " -- coord names are: %s"%str(self.coordnames))
318 319
320 - def __call__(self, coords):
321 if coords in xrange(self.dimension+1): 322 if coords == self.dimension: 323 # trap for when Point is used as an iterator, i.e. as 324 # for x in pt -- avoids writing an __iter__ method that 325 # will be inherited by Pointset, which already iterates fine 326 raise StopIteration 327 else: 328 return self.coordarray[coords] 329 elif coords in self.coordnames: 330 ix = self._name_ix_map[coords] 331 return self.coordarray[ix] 332 else: 333 ixlist = self._force_coords_to_ixlist(coords) 334 return Point({'coordarray': self.coordarray[ixlist], 335 'coordnames': [self.coordnames[i] for i in ixlist], 336 'coordtype': self.coordtype, 337 'norm': self._normord, 338 'labels': self.labels})
339 340 __getitem__ = __call__ 341 342 # def __iter__(self): 343 # return self.coordarray.__iter__() 344 345
346 - def __setitem__(self, ixarg, val):
347 """Change coordinate array values.""" 348 ixs = self._force_coords_to_ixlist(ixarg) 349 if len(ixs) == 1: 350 val = [val] 351 try: 352 for i, v in zip(ixs,val): 353 self.coordarray[i] = v 354 except TypeError: 355 raise TypeError("Bad value type for Point")
356 357
358 - def toarray(self):
359 if self.dimension == 1: 360 return self.coordarray[0] 361 else: 362 return self.coordarray
363 364
365 - def __add__(self, other):
366 res = self.copy() 367 try: 368 res.coordarray += other.coordarray 369 except AttributeError: 370 res.coordarray += other 371 return res
372 373 __radd__ = __add__ 374
375 - def __sub__(self, other):
376 res = self.copy() 377 try: 378 res.coordarray -= other.coordarray 379 except AttributeError: 380 res.coordarray -= other 381 return res
382
383 - def __rsub__(self, other):
384 res = self.copy() 385 try: 386 res.coordarray = other.coordarray - res.coordarray 387 except AttributeError: 388 res.coordarray = other - res.coordarray 389 return res
390
391 - def __mul__(self, other):
392 res = self.copy() 393 try: 394 res.coordarray *= other.coordarray 395 except AttributeError: 396 res.coordarray *= other 397 return res
398 399 __rmul__ = __mul__ 400
401 - def __div__(self, other):
402 res = self.copy() 403 try: 404 res.coordarray /= other.coordarray 405 except AttributeError: 406 res.coordarray /= other 407 return res
408 409 __truediv__ = __div__ 410
411 - def __rdiv__(self, other):
412 res = self.copy() 413 try: 414 res.coordarray = other.coordarray / res.coordarray 415 except AttributeError: 416 res.coordarray = other / res.coordarray 417 return res
418 419 __rtruediv__ = __rdiv__ 420
421 - def __pow__(self, other):
422 res = self.copy() 423 res.coordarray **= other 424 return res
425
426 - def __neg__(self):
427 res = self.copy() 428 res.coordarray = - res.coordarray 429 return res
430
431 - def __pos__(self):
432 return self.copy()
433
434 - def __lt__(self, other):
435 try: 436 assert shape(self) == shape(other) 437 if hasattr(other, 'coordnames'): 438 if self.coordnames != other.coordnames: 439 raise ValueError("Coordinate mismatch") 440 return linalg.norm(self.coordarray, self._normord) < \ 441 linalg.norm(other.coordarray, self._normord) 442 except (AttributeError, TypeError, AssertionError): 443 return self.coordarray < other 444 except ZeroDivisionError: 445 raise ValueError("Norm order for point cannot be zero")
446
447 - def __gt__(self, other):
448 try: 449 assert shape(self) == shape(other) 450 if hasattr(other, 'coordnames'): 451 if self.coordnames != other.coordnames: 452 raise ValueError("Coordinate mismatch") 453 return linalg.norm(self.coordarray, self._normord) > \ 454 linalg.norm(other.coordarray, self._normord) 455 except (AttributeError, TypeError, AssertionError): 456 return self.coordarray > other 457 except ZeroDivisionError: 458 raise ValueError("Norm order for point cannot be zero")
459
460 - def __le__(self, other):
461 try: 462 assert shape(self) == shape(other) 463 if hasattr(other, 'coordnames'): 464 if self.coordnames != other.coordnames: 465 raise ValueError("Coordinate mismatch") 466 return linalg.norm(self.coordarray, self._normord) <= \ 467 linalg.norm(other.coordarray, self._normord) 468 except (AttributeError, TypeError, AssertionError): 469 return self.coordarray <= other 470 except ZeroDivisionError: 471 raise ValueError("Norm order for point cannot be zero")
472
473 - def __ge__(self, other):
474 try: 475 assert shape(self) == shape(other) 476 if hasattr(other, 'coordnames'): 477 if self.coordnames != other.coordnames: 478 raise ValueError("Coordinate mismatch") 479 return linalg.norm(self.coordarray, self._normord) >= \ 480 linalg.norm(other.coordarray, self._normord) 481 except (AttributeError, TypeError, AssertionError): 482 return self.coordarray >= other 483 except ZeroDivisionError: 484 raise ValueError("Norm order for point cannot be zero")
485
486 - def __eq__(self, other):
487 try: 488 assert shape(self) == shape(other) 489 if hasattr(other, 'coordnames'): 490 if self.coordnames != other.coordnames: 491 raise ValueError("Coordinate mismatch") 492 return linalg.norm(self.coordarray, self._normord) == \ 493 linalg.norm(other.coordarray, self._normord) 494 except (AttributeError, TypeError, AssertionError): 495 return self.coordarray == other 496 except ZeroDivisionError: 497 raise ValueError("Norm order for point cannot be zero")
498
499 - def __ne__(self, other):
500 try: 501 assert shape(self) == shape(other) 502 if hasattr(other, 'coordnames'): 503 if self.coordnames != other.coordnames: 504 raise ValueError("Coordinate mismatch") 505 return linalg.norm(self.coordarray, self._normord) != \ 506 linalg.norm(other.coordarray, self._normord) 507 except (AttributeError, TypeError, AssertionError): 508 return self.coordarray != other 509 except ZeroDivisionError: 510 raise ValueError("Norm order for point cannot be zero")
511 512
513 - def _infostr(self, verbose=0):
514 precision = 8 515 if verbose == 0: 516 outputStr = "Point with coords:\n" 517 for c in self.coordnames: 518 outputStr += c 519 if c != self.coordnames[-1]: 520 outputStr += "\n" 521 elif verbose > 0: 522 outputStr = '' 523 for c in self.coordnames: 524 v = self.coordarray[self._map_names_to_ixs(c)] 525 if isinstance(v, ndarray): 526 dvstr = str(v[0]) 527 else: 528 # only alternative is a singleton numeric value (not list) 529 dvstr = str(v) 530 outputStr += c+': '+dvstr 531 if c != self.coordnames[-1]: 532 outputStr += "\n" 533 for label, infodict in self.labels.iteritems(): 534 outputStr += "\nLabels: %s (%s)"%(label, str(infodict)) 535 return outputStr
536 537
538 - def __repr__(self):
539 return self._infostr(verbose=1)
540 541 542 __str__ = __repr__ 543 544
545 - def info(self, verboselevel=1):
546 print self._infostr(verboselevel)
547 548
549 - def __abs__(self):
550 return linalg.norm(self.coordarray, self._normord)
551 552
553 - def __copy__(self):
554 return Point({'coordarray': copy(self.coordarray), 555 'coordnames': copy(self.coordnames), 556 'coordtype': self.coordtype, 557 'norm': self._normord, 558 'labels': self.labels})
559 560 copy = __copy__ 561 562
563 - def __getstate__(self):
564 d = copy(self.__dict__) 565 # remove reference to Cfunc type 566 d['coordtype'] = _num_type2name[self.coordtype] 567 return d
568 569
570 - def __setstate__(self, state):
571 self.__dict__.update(state) 572 # reinstate Cfunc type 573 self.coordtype = _num_name2type[self.coordtype]
574 575 576 #---------------------------------------------------------------------------- 577 578
579 -class Pointset(Point):
580 """1D parameterized or non-parameterized set of discrete points. 581 (If present, the independent variable must be a float64 or an int32)""" 582
583 - def __init__(self, kwd=None, **kw):
584 if kwd is not None: 585 if kw != {}: 586 raise ValueError("Cannot mix keyword dictionary and keywords") 587 kw = kwd 588 if intersect(kw.keys(), point_keys) == []: 589 # creating Pointset from dictionary 590 temp_kw = {} 591 temp_kw['coorddict'] = copy(kw) 592 kw = copy(temp_kw) 593 # Deal with independent variable, if present 594 if 'indepvardict' in kw: 595 assert len(kw['indepvardict']) == 1 596 try: 597 it = kw['indepvartype'] 598 except KeyError: 599 self.indepvartype = float64 600 else: 601 try: 602 self.indepvartype = _num_equivtype[it] 603 except KeyError: 604 raise TypeError('Independent variable type %s not valid'%str(it)) 605 vals = kw['indepvardict'].values()[0] 606 self.indepvarname = kw['indepvardict'].keys()[0] 607 if isinstance(vals, _seq_types): 608 self.indepvararray = array(vals, self.indepvartype) 609 else: 610 try: 611 assert self.indepvartype == _num_equivtype[type(vals)] 612 except (AssertionError, KeyError): 613 raise TypeError("Invalid type for independent variable value") 614 else: 615 self.indepvararray = array([vals], self.indepvartype) 616 elif 'indepvararray' in kw: 617 if 'indepvarname' in kw: 618 self.indepvarname = kw['indepvarname'] 619 else: 620 self.indepvarname = 't' 621 vals = kw['indepvararray'] 622 if isinstance(vals, list): 623 try: 624 it = kw['indepvartype'] 625 except: 626 self.indepvartype = float64 627 else: 628 try: 629 self.indepvartype = _num_equivtype[it] 630 except KeyError: 631 raise TypeError('Independent variable type %s not valid'%str(it)) 632 self.indepvararray = array(vals, self.indepvartype) 633 elif isinstance(vals, ndarray): 634 # call 'array' constructor to ensure copy is made in case 635 # either array is independently changed. 636 if rank(vals) in [0,2]: 637 self.indepvararray = array(vals.ravel()) 638 else: 639 self.indepvararray = array(vals) 640 try: 641 self.indepvartype = _num_equivtype[self.indepvararray.dtype.type] 642 except KeyError: 643 raise TypeError('Independent variable type ' 644 '%s not valid'%self.indepvararray.dtype) 645 else: 646 raise TypeError("Invalid type for independent variable " 647 "array: "+str(type(vals))) 648 649 else: 650 # non-parameterized case 651 self.indepvarname = None 652 self.indepvartype = None 653 self.indepvararray = None 654 self._parameterized = False 655 if self.indepvarname: 656 # do validation checks 657 assert isinstance(self.indepvarname, str), \ 658 'independent variable name must be a string' 659 try: 660 self.indepvartype = _num_equivtype[self.indepvararray.dtype.type] 661 except KeyError: 662 raise TypeError('Independent variable type ' 663 '%s not valid'%self.indepvararray.dtype) 664 r=rank(self.indepvararray) 665 if r == 1: 666 pass 667 elif r == 0: 668 self.indepvararray = self.indepvararray.ravel() 669 else: 670 raise ValueError("Invalid rank for " 671 "independent variable array %i"%r) 672 # if user gave independent variable array in reverse order, 673 # then we'll reverse this and the coord arrays and the labels 674 # at the end of initialization 675 do_reverse = not isincreasing(self.indepvararray) 676 self._parameterized = True 677 # Deal with coordinate data 678 if 'coorddict' in kw: 679 coorddict = {} 680 try: 681 ct = kw['coordtype'] 682 except KeyError: 683 self.coordtype = float64 684 else: 685 try: 686 self.coordtype = _num_equivtype[ct] 687 except KeyError: 688 raise TypeError('Coordinate type %s not valid for Point'%str(ct)) 689 for c, v in kw['coorddict'].iteritems(): 690 if isinstance(c, str): 691 c_key = c 692 else: 693 c_key = repr(c) 694 if isinstance(v, list): 695 coorddict[c_key] = array(v, self.coordtype) 696 elif isinstance(v, ndarray): 697 # call 'array' constructor on array to ensure it is a copy 698 # if either array is independently changed. 699 coorddict[c_key] = array(v, self.coordtype) 700 elif isinstance(v, Pointset): 701 coorddict[c_key] = v.toarray() 702 else: 703 try: 704 assert self.coordtype == _num_equivtype[type(v)] 705 except (AssertionError, KeyError): 706 raise TypeError("Must pass arrays, lists, or numeric types") 707 else: 708 coorddict[c_key] = array([v], self.coordtype) 709 self.coordnames = coorddict.keys() 710 # only way to order dictionary keys for array is to sort 711 self.coordnames.sort() 712 self.dimension = len(self.coordnames) 713 datalist = [] 714 # loop over coordnames to ensure correct ordering of coordarray 715 if self._parameterized: 716 my_len = len(self.indepvararray) 717 else: 718 my_len = len(coorddict[self.coordnames[0]]) 719 for c in self.coordnames: 720 xs = coorddict[c] 721 if my_len != len(xs): 722 if self._parameterized: 723 raise ValueError('Independent variable array length must match ' 724 'that of each coordinate array') 725 else: 726 raise ValueError('All coordinate arrays must have same length') 727 datalist.append(xs) 728 self.coordarray = array(datalist, self.coordtype) 729 r = rank(self.coordarray) 730 if r == 2: 731 pass 732 elif r == 1: 733 self.coordarray = array([self.coordarray], self.coordtype) 734 elif r == 0: 735 self.coordarray = array([self.coordarray.ravel()], self.coordtype) 736 else: 737 raise ValueError("Invalid rank for coordinate array: %i"%r) 738 assert self.dimension == self.coordarray.shape[0], "Invalid coord array" 739 elif 'coordarray' in kw: 740 if not isinstance(kw['coordarray'], _seq_types): 741 raise TypeError('Coordinate type %s not valid for Pointset'%str(type(kw['coordarray']))) 742 try: 743 ct = kw['coordtype'] 744 except KeyError: 745 self.coordtype = float64 746 else: 747 try: 748 self.coordtype = _num_equivtype[ct] 749 except KeyError: 750 raise TypeError('Coordinate type %s not valid'%str(ct)) 751 # calling 'array' constructor creates a copy if original or new 752 # array is altered 753 array_temp = array(kw['coordarray'], self.coordtype) 754 r = rank(array_temp) 755 if r == 2: 756 self.coordarray = array_temp 757 elif r == 1: 758 self.coordarray = array([kw['coordarray']], self.coordtype) 759 elif r == 0: 760 self.coordarray = array([array_temp.ravel()], self.coordtype) 761 else: 762 raise ValueError("Invalid rank for coordinate array %i"%r) 763 self.dimension = self.coordarray.shape[0] 764 if 'coordnames' in kw: 765 if isinstance(kw['coordnames'], str): 766 coordnames = [kw['coordnames']] 767 else: 768 coordnames = kw['coordnames'] 769 else: 770 coordnames = [str(cix) for cix in range(self.dimension)] 771 if len(coordnames) != self.dimension: 772 print "Pointset initialization error:" 773 print "Found Coordnames: ", coordnames, \ 774 "(dimension = %s)"%len(coordnames) 775 print "vs. data dimension =", self.dimension 776 raise ValueError("Mismatch between number of coordnames and " 777 "dimension of data") 778 cs = array(coordnames) 779 order = cs.argsort() 780 self.coordnames = cs[order].tolist() 781 self.coordarray = take(self.coordarray,order,axis=0) 782 self.coordtype = self.coordarray.dtype.type 783 else: 784 raise ValueError("Missing coord info in keywords") 785 assert isUniqueSeq(self.coordnames), 'Coordinate names must be unique' 786 self.makeIxMaps() 787 if self._parameterized: 788 assert self.indepvarname not in self.coordnames, \ 789 "Independent variable name appeared in coordinate names" 790 # if len(self.coordarray.shape) > 1: 791 assert self.coordarray.shape[1] == len(self.indepvararray), \ 792 ("Coord array length mismatch with independent variable" 793 " array length") 794 #else: 795 # assert self.coordarray.shape[0] == len(self.indepvararray) 796 # process choice of indep var tolerance 797 if 'checklevel' in kw: 798 checklevel = kw['checklevel'] 799 if checklevel in [0,1]: 800 self.checklevel = checklevel 801 else: 802 raise ValueError("Invalid check level") 803 else: 804 # default to use tolerance in indep val resolution 805 self.checklevel = 1 806 if 'tolerance' in kw: 807 tol = kw['tolerance'] 808 if tol > 0: 809 self._abseps = tol 810 else: 811 raise ValueError("Tolerance must be a positive real number") 812 else: 813 self._abseps = 1e-13 814 if 'name' in kw: 815 if isinstance(kw['name'], str): 816 self.name = kw['name'] 817 else: 818 raise TypeError("name argument must be a string") 819 else: 820 self.name = "" 821 if 'norm' in kw: 822 if kw['norm'] == 0: 823 raise ValueError("Norm order for point cannot be zero") 824 self._normord = kw['norm'] 825 else: 826 self._normord = 2 827 if 'labels' in kw: 828 try: 829 self.labels = PointInfo(kw['labels'].by_index) 830 except AttributeError: 831 self.labels = PointInfo(kw['labels']) 832 else: 833 self.labels = PointInfo() 834 if 'tags' in kw: 835 self.tags = kw['tags'] 836 else: 837 self.tags = {} 838 if self._parameterized: 839 if do_reverse: 840 # finish the operation of reversing the reverse-order 841 # input arrays 842 self.indepvararray = self.indepvararray[::-1] 843 self.reverse() 844 if not isincreasing(self.indepvararray): 845 raise ValueError("Independent variable values must be in " 846 "increasing order")
847 848
849 - def __delitem__(self, k):
850 """Remove point by index or by coordinate.""" 851 if k in self.coordnames: 852 cs = remain(self.coordnames, k) 853 p_result = copy(self[cs]) 854 self.coordnames = cs 855 self.coordarray = p_result.coordarray 856 self.labels = p_result.labels 857 self.indepvararray = p_result.indepvararray 858 self.makeIxMaps() 859 else: 860 # assume integer 861 self.remove(k)
862 863
864 - def remove(self, ix):
865 """Remove individual Point by its index.""" 866 if ix == 0: 867 try: 868 p_result = copy(self[1:]) 869 except ValueError: 870 # slice index out of range => only 1 point left! 871 raise ValueError("Cannot remove only point in pointset!") 872 else: 873 ix = ix % len(self) 874 p_result = copy(self[:ix]) 875 try: 876 p_result.append(self[ix+1:]) 877 except ValueError: 878 # ix was at end, so nothing left to append 879 pass 880 self.coordarray = p_result.coordarray 881 self.labels = p_result.labels 882 self.indepvararray = p_result.indepvararray 883 self.makeIxMaps()
884 885
886 - def reverse(self):
887 """Reverse order of points *IN PLACE*.""" 888 self.coordarray = self.coordarray[:,::-1] 889 self.labels.mapIndices(dict(zip(range(0,len(self)),range(len(self)-1,-1,-1))))
890
891 - def rename(self, coord, newcoord):
892 """Rename a coordinate.""" 893 try: 894 ix = self.coordnames.index(coord) 895 except ValueError: 896 raise ValueError("No such coordinate: %s"%coord) 897 self.coordnames[ix] = newcoord 898 self.makeIxMaps()
899
900 - def makeIxMaps(self):
901 self._name_ix_map = dict(zip(self.coordnames, range(self.dimension))) 902 self._ix_name_map = copy(self.coordnames) 903 if self._parameterized: 904 self._indepvar_ix_map = makeArrayIxMap(self.indepvararray) 905 else: 906 self._indepvar_ix_map = None
907 908
909 - def addlabel(self, ix, label, info=None):
910 """Add string label to indexed point. info dictionary is optional""" 911 if ix < 0: 912 ix = len(self)+ix 913 if ix in xrange(len(self)): 914 self.labels.update(ix, label, info) 915 else: 916 raise ValueError("Index out of range")
917 918
919 - def removelabel(self, ix):
920 """Remove all labels at indexed point.""" 921 del self.labels[ix]
922 923
924 - def bylabel(self, s):
925 """Return pointset containing points labelled with the supplied 926 labels. Argument s can be a string or a list of strings.""" 927 if isinstance(s, str): 928 if s == '': 929 raise ValueError("Label must be non-empty") 930 else: 931 ixlist = sortedDictKeys(self.labels[s]) 932 if ixlist != []: 933 return self[ixlist] 934 else: 935 return None 936 elif isinstance(s, list): 937 ixlist = [] 938 for ss in s: 939 if isinstance(ss, str): 940 if ss == '': 941 raise ValueError("Label must be non-empty") 942 ixlist = sortedDictKeys(self.labels[ss]) 943 else: 944 raise TypeError("Invalid label type") 945 if ixlist != []: 946 return self[ixlist] 947 else: 948 return None 949 else: 950 raise TypeError("Invalid label type")
951 952 953
954 - def __setitem__(self, ix, p):
955 """Change individual points, accessed by index (no slicing supported). 956 Individual coordinate values of a point can be changed by adding a 957 cross-reference coordinate name or index. 958 If ix is a variable name then the entire row can be changed (again, 959 no slicing supported).""" 960 if isinstance(ix, _int_types): 961 if isinstance(p, Point): 962 if compareNumTypes(self.coordtype, int32) and \ 963 compareNumTypes(p.coordtype, float64): 964 raise ValueError("Cannot update integer pointset with a float") 965 self.coordarray[:,ix] = p.toarray() 966 if len(p.labels) > 0: 967 self.labels.update({ix: p.labels}) 968 elif isinstance(p, dict): 969 vlist = [] 970 for k in self.coordnames: 971 vlist.append(p[k]) 972 self.coordarray[:,ix] = array(vlist, self.coordtype) 973 elif isinstance(p, _seq_types): 974 self.coordarray[:,ix] = array(p, self.coordtype) 975 else: 976 raise TypeError("Invalid index reference") 977 elif isinstance(ix, tuple) and len(ix) == 2: 978 # note that index order must be reversed 979 try: 980 c = self._name_ix_map[ix[1]] 981 except KeyError: 982 c = ix[1] 983 if isinstance(p, _int_types): 984 self.coordarray[c,ix[0]] = p 985 elif isinstance(p, _float_types): 986 if self.coordtype == float64: 987 self.coordarray[c,ix[0]] = p 988 else: 989 raise TypeError("Cannot update an integer pointset with a float") 990 elif isinstance(p, ndarray) and p.shape==(1,): 991 self.coordarray[c,ix[0]] = p[0] 992 elif isinstance(p, list) and len(list) == 1: 993 self.coordarray[c,ix[0]] = p[0] 994 elif isinstance(p, Point) and p.dimension == 1: 995 self.coordarray[c,ix[0]] = p[0] 996 if len(p.labels) > 0: 997 self.labels.update({ix: p.labels}) 998 else: 999 raise TypeError("New value is not a singleton numeric type") 1000 elif isinstance(ix, str): 1001 if ix == self.indepvarname: 1002 if isinstance(p, Pointset): 1003 if compareNumTypes(self.indepvartype, int32) and \ 1004 compareNumTypes(p.indepvartype, float64): 1005 raise ValueError("Cannot update integer independent variable with a float") 1006 if len(self) == len(p): 1007 self.indepvararray = p.toarray() 1008 else: 1009 raise ValueError("Size mismatch for new independent variable array") 1010 # labels ignored 1011 elif isinstance(p, dict): 1012 if len(self) == len(p[c]): 1013 self.indepvararray = array(p[c], self.indepvartype) 1014 else: 1015 raise ValueError("Size mismatch for new independent variable array") 1016 elif isinstance(p, _seq_types): 1017 if len(self) == len(p): 1018 self.indepvararray = array(p, self.indepvartype) 1019 else: 1020 raise ValueError("Size mismatch for new independent variable array") 1021 else: 1022 raise TypeError("Invalid data") 1023 elif ix in self.coordnames: 1024 c = self._name_ix_map[ix] 1025 if isinstance(p, Pointset): 1026 if compareNumTypes(self.coordtype, int32) and \ 1027 compareNumTypes(p.coordtype, float64): 1028 raise ValueError("Cannot update integer pointset with a float") 1029 self.coordarray[c,:] = p.toarray() 1030 # labels ignored 1031 elif isinstance(p, dict): 1032 self.coordarray[c,:] = array(p[c], self.coordtype) 1033 elif isinstance(p, _seq_types): 1034 self.coordarray[c,:] = array(p, self.coordtype) 1035 elif isinstance(p, _real_types): 1036 self.coordarray[c,:] = float(p) 1037 else: 1038 raise TypeError("Invalid data") 1039 else: 1040 raise TypeError("Invalid variable reference") 1041 else: 1042 raise TypeError("Invalid Pointset reference")
1043 1044
1045 - def __getitem__(self, ix):
1046 # select points 1047 if isinstance(ix, _int_types): 1048 # The labels (PointInfo) object doesn't understand -ve indices, 1049 # but don't take modulo length otherwise iteration will break 1050 if ix < 0: 1051 ix = ix + self.coordarray.shape[1] 1052 if ix in self.labels: 1053 label = self.labels[ix] 1054 else: 1055 label = {} 1056 return Point({'coordarray': self.coordarray[:,ix], 1057 'coordnames': self.coordnames, 1058 'norm': self._normord, 1059 'labels': label}) 1060 elif isinstance(ix, tuple): 1061 if len(ix) != 2: 1062 raise ValueError("Only use 2-tuples in referencing pointset") 1063 ref1 = ix[0] 1064 ref2 = ix[1] 1065 elif isinstance(ix, str): 1066 # reference by coord name 1067 if self._parameterized: 1068 if ix == self.indepvarname: 1069 return self.indepvararray 1070 else: 1071 return self.coordarray[self._map_names_to_ixs(ix),:] 1072 else: 1073 return self.coordarray[self._map_names_to_ixs(ix),:] 1074 elif isinstance(ix, list): 1075 if all([x in self.coordnames for x in ix]): 1076 ref1 = slice(len(self)) 1077 ref2 = ix 1078 else: 1079 ref1 = ix 1080 ref2 = None 1081 elif isinstance(ix, (ndarray, slice)): 1082 ref1 = ix 1083 ref2 = None 1084 else: 1085 raise IndexError("Illegal index %s"%str(ix)) 1086 if isinstance(ref1, (list, ndarray, _int_types)): 1087 if isinstance(ref1, _int_types): 1088 ref1 = [ref1] 1089 try: 1090 ca = take(self.coordarray, ref1, axis=1) 1091 except ValueError: 1092 raise ValueError("Invalid variable names given: "%(str(ref1))) 1093 try: 1094 ci = take(self.indepvararray, ref1, axis=0) 1095 except (IndexError, AttributeError): 1096 # non-parameterized pointset 1097 pass 1098 cl = self.labels[ref1] 1099 cl_ixs = cl.getIndices() 1100 ixmap = invertMap(ref1) 1101 new_cl_ixs = [ixmap[i] for i in cl_ixs] 1102 elif isinstance(ref1, slice): 1103 ls = len(self) 1104 if ref1.stop is None: 1105 stop = ls 1106 else: 1107 if ref1.stop < 0: 1108 stop = ref1.stop + self.coordarray.shape[1] + 1 1109 else: 1110 stop = ref1.stop 1111 s1, s2, s3 = ref1.indices(stop) 1112 if s1 < 0 or s2 > ls or s1 >= ls: 1113 raise ValueError("Slice index out of range") 1114 ca = take(self.coordarray, xrange(s1, s2, s3), axis=1) 1115 try: 1116 ci = take(self.indepvararray, xrange(s1, s2, s3),axis=0) 1117 except (IndexError, AttributeError): 1118 # non-parameterized pointset 1119 pass 1120 cl = self.labels[ref1] 1121 cl_ixs = cl.getIndices() 1122 lowest_ix = ref1.start or 0 1123 if lowest_ix < 0: 1124 lowest_ix = len(self)+lowest_ix 1125 new_cl_ixs = [i-lowest_ix for i in cl_ixs] 1126 else: 1127 print "ref1 argument =", ref1 1128 raise TypeError("Type %s is invalid for Pointset indexing"%str(type(ref1))) 1129 ixlist = self._force_coords_to_ixlist(ref2) 1130 ca = take(ca, ixlist, axis=0) 1131 try: 1132 cl.mapIndices(dict(zip(cl_ixs, new_cl_ixs))) 1133 except AttributeError: 1134 pass 1135 if self._parameterized: 1136 return Pointset({'coordarray': ca, 1137 'coordnames': [self.coordnames[i] for i in ixlist], 1138 'indepvararray': ci, 1139 'indepvarname': self.indepvarname, 1140 'norm': self._normord, 1141 'labels': cl}) 1142 else: 1143 return Pointset({'coordarray': ca, 1144 'coordnames': [self.coordnames[i] for i in ixlist], 1145 'norm': self._normord, 1146 'labels': cl})
1147 1148
1149 - def _resolve_indepvar(self, p):
1150 if self.checklevel == 0: 1151 return self._indepvar_ix_map[p] 1152 else: 1153 try: 1154 return self._indepvar_ix_map[p] 1155 except: 1156 ixs = self.findIndex(p) 1157 lval = self.indepvararray[ixs[0]] 1158 rval = self.indepvararray[ixs[1]] 1159 if p - lval < self._abseps: 1160 return ixs[0] 1161 elif rval - p <= self._abseps: 1162 return ixs[1] 1163 else: 1164 lerr = p - lval 1165 rerr = rval - p 1166 raise KeyError( \ 1167 "%f not found in (%f, %f) @tol=%.16f: mismatches=(%.16f, %.16f)"%(p,lval,rval,self._abseps,lerr,rerr))
1168 1169
1170 - def setTol(self, tol):
1171 if tol > 0: 1172 self._abseps = tol 1173 else: 1174 raise ValueError("tolerance must be a positive real number")
1175 1176
1177 - def __call__(self, p, coords=None):
1178 if not self._parameterized: 1179 raise TypeError("Cannot call a non-parameterized Pointset") 1180 if isinstance(p, _seq_types): 1181 # assume p is an all-numeric list, so it should be treated as 1182 # an independent variable. 1183 try: 1184 ix = [self._resolve_indepvar(i) for i in p] 1185 except KeyError: 1186 raise ValueError("Independent variable value not valid: %s"%str(p)) 1187 else: 1188 # assume p is an integer or float, appropriate to independent var 1189 try: 1190 ix = self._resolve_indepvar(p) 1191 except KeyError: 1192 raise ValueError("Independent variable value not valid: " \ 1193 + str(p)) 1194 if coords is None: 1195 if isinstance(ix, _int_types): 1196 label = self.labels[ix] 1197 try: 1198 label.mapIndices({ix: 0}) 1199 except AttributeError: 1200 # empty 1201 pass 1202 return Point({'coordarray': self.coordarray[:,ix], 1203 'coordnames': self.coordnames, 1204 'norm': self._normord, 1205 'labels': label}) 1206 else: 1207 labels = self.labels[ix] 1208 cl_ixs = labels.getIndices() 1209 ixmap = invertMap(ix) 1210 new_cl_ixs = [ixmap[i] for i in cl_ixs] 1211 if isinstance(ix, slice): 1212 lowest_ix = ix.start or 0 1213 new_cl_ixs = [i-lowest_ix for i in cl_ics] 1214 elif isinstance(ix, (list, ndarray)): 1215 new_cl_ixs = [ixmap[i] for i in cl_ixs] 1216 try: 1217 labels.mapIndices(dict(zip(cl_ixs, new_cl_ixs))) 1218 except AttributeError: 1219 # empty 1220 pass 1221 return Pointset({'coordarray': take(self.coordarray, ix, axis=1), 1222 'coordnames': self.coordnames, 1223 'indepvarname': self.indepvarname, 1224 'indepvararray': take(self.indepvararray, ix, axis=0), 1225 'norm': self._normord, 1226 'labels': labels}) 1227 else: 1228 clist = self._force_coords_to_ixlist(coords) 1229 if isinstance(ix, _int_types): 1230 label = self.labels[ix] 1231 try: 1232 label.mapIndices({ix: 0}) 1233 except AttributeError: 1234 # empty 1235 pass 1236 return Point({'coordarray': self.coordarray[clist, ix], 1237 'coordnames': [self.coordnames[i] for i in clist], 1238 'norm': self._normord, 1239 'labels': label}) 1240 else: 1241 labels = self.labels[ix] 1242 try: 1243 labels.mapIndices(dict(zip(labels, [i-ix[0] for i in labels.getIndices()]))) 1244 except AttributeError: 1245 # empty 1246 pass 1247 return Pointset({'coordarray': take(self.coordarray[clist], ix, axis=1), 1248 'coordnames': [self.coordnames[i] for i in clist], 1249 'indepvarname': self.indepvarname, 1250 'indepvararray': take(self.indepvararray, ix, axis=0), 1251 'norm': self._normord, 1252 'labels': labels})
1253 1254
1255 - def __len__(self):
1256 return self.coordarray.shape[1]
1257 1258
1259 - def __contains__(self, other):
1260 for i in xrange(len(self)): 1261 if comparePointCoords(self.__getitem__(i), other): 1262 return True 1263 return False
1264 1265
1266 - def __lt__(self, other):
1267 if isinstance(other, Pointset): 1268 if not all(self.indepvararray == other.indepvararray): 1269 raise ValueError("Independent variable arrays are not the same") 1270 return array([self[i] < other[i] for i in range(len(self))], 'Bool') 1271 elif isinstance(other, Point): 1272 return array([p < other for p in self], 'Bool') 1273 else: 1274 try: 1275 return self.coordarray < other 1276 except: 1277 raise TypeError("Invalid type for comparison with Pointset")
1278
1279 - def __gt__(self, other):
1280 if isinstance(other, Pointset): 1281 if not all(self.indepvararray == other.indepvararray): 1282 raise ValueError("Independent variable arrays are not the same") 1283 return array([self[i] > other[i] for i in range(len(self))], 'Bool') 1284 elif isinstance(other, Point): 1285 return array([p > other for p in self], 'Bool') 1286 else: 1287 try: 1288 return self.coordarray > other 1289 except: 1290 raise TypeError("Invalid type for comparison with Pointset")
1291
1292 - def __le__(self, other):
1293 if isinstance(other, Pointset): 1294 if not all(self.indepvararray == other.indepvararray): 1295 raise ValueError("Independent variable arrays are not the same") 1296 return array([self[i] <= other[i] for i in range(len(self))], 'Bool') 1297 elif isinstance(other, Point): 1298 return array([p <= other for p in self], 'Bool') 1299 else: 1300 try: 1301 return self.coordarray <= other 1302 except: 1303 raise TypeError("Invalid type for comparison with Pointset")
1304
1305 - def __ge__(self, other):
1306 if isinstance(other, Pointset): 1307 if not all(self.indepvararray == other.indepvararray): 1308 raise ValueError("Independent variable arrays are not the same") 1309 return array([self[i] >= other[i] for i in range(len(self))], 'Bool') 1310 elif isinstance(other, Point): 1311 return array([p >= other for p in self], 'Bool') 1312 else: 1313 try: 1314 return self.coordarray >= other 1315 except: 1316 raise TypeError("Invalid type for comparison with Pointset")
1317
1318 - def __eq__(self, other):
1319 if isinstance(other, Pointset): 1320 if not all(self.indepvararray == other.indepvararray): 1321 raise ValueError("Independent variable arrays are not the same") 1322 return array([self[i] == other[i] for i in range(len(self))], 'Bool') 1323 elif isinstance(other, Point): 1324 return array([p == other for p in self], 'Bool') 1325 else: 1326 try: 1327 return self.coordarray == other 1328 except: 1329 raise TypeError("Invalid type for comparison with Pointset")
1330
1331 - def __ne__(self, other):
1332 if isinstance(other, Pointset): 1333 if not all(self.indepvararray == other.indepvararray): 1334 raise ValueError("Independent variable arrays are not the same") 1335 return array([self[i] != other[i] for i in range(len(self))], 'Bool') 1336 elif isinstance(other, Point): 1337 return array([p != other for p in self], 'Bool') 1338 else: 1339 try: 1340 return self.coordarray != other 1341 except: 1342 raise TypeError("Invalid type for comparison with Pointset")
1343 1344
1345 - def insert(self, parg, ix=None):
1346 """Insert individual Point or Pointset before the given index. 1347 1348 If ix is not given then the source and target Pointsets must 1349 be parameterized. In this case the Point or Pointset will be 1350 inserted according to the ordering of independent variable 1351 values.""" 1352 p=copy(parg) 1353 if ix is None: 1354 if self._parameterized: 1355 if isinstance(p, Point) and self.indepvarname in p.coordnames: 1356 t = p[self.indepvarname] 1357 tix = self.find(t) 1358 if isinstance(tix, tuple): 1359 self.insert(p, tix[1]) 1360 else: 1361 # tix was an integer, meaning that t is 1362 # already present in Pointset 1363 raise ValueError("Point at independent variable" 1364 "value %f already present"%t) 1365 elif isinstance(p, Pointset) and p._parameterized and \ 1366 p.indepvarname == self.indepvarname: 1367 # Don't do a straight self.insert call in case the 1368 # new indep var values need to be interleaved with 1369 # the present ones. 1370 # 1371 # convert self.indepvararray and self.coordarray into lists (by self.todict()) 1372 iva = self.indepvararray.tolist() 1373 vd = self.todict(aslist=True) 1374 # get list of findIndex results for each of p indepvar vals 1375 # add i for each one because each previous one will have been inserted, 1376 # increasing the length of self. 1377 if len(intersect(self._ix_name_map, p._ix_name_map)) != self.dimension: 1378 raise ValueError("Dimension mismatch with inserted Pointset") 1379 iva_p = p.indepvararray 1380 lenp = len(p) 1381 vd_p = p.todict() 1382 try: 1383 s_ixs = [self.findIndex(iva_p[i])[1]+i for i in xrange(lenp)] 1384 except TypeError: 1385 raise ValueError("Independent variable " 1386 "values in Pointset already present") 1387 p_label_ixs = p.labels.getIndices() 1388 s_label_ixs = self.labels.getIndices() 1389 sLabelMap = {} 1390 pLabelMap = {} 1391 for i in xrange(lenp): 1392 s_ix = s_ixs[i] 1393 if i in p_label_ixs: 1394 pLabelMap[i] = s_ix 1395 for s_label_ix in s_label_ixs: 1396 if s_label_ix >= s_ix-i: 1397 sLabelMap[s_label_ix] = s_label_ix+i+1 1398 # for each one, list-insert new point data 1399 for p_ix in xrange(lenp): 1400 s_ix = s_ixs[p_ix] 1401 iva.insert(s_ix, iva_p[p_ix]) 1402 for k in self._ix_name_map: 1403 vd[k].insert(s_ix, vd_p[k][p_ix]) 1404 # restore self's arrays 1405 self.indepvararray = array(iva) 1406 datalist = [] 1407 for c in p._ix_name_map: 1408 datalist.append(vd[c]) 1409 self.coordarray = array(datalist, self.coordtype) 1410 # update labels 1411 self.labels.mapIndices(sLabelMap) 1412 p_labels = copy(p.labels) 1413 p_labels.mapIndices(pLabelMap) 1414 self.labels.update(p_labels) 1415 else: 1416 raise TypeError("Inserted Point/Pointset must be " 1417 "parameterized and share same independent" 1418 "parameter name") 1419 else: 1420 raise TypeError("Source Pointset must be parameterized") 1421 else: 1422 if ix > 0: 1423 p_result = copy(self[:ix]) 1424 p_result.append(p) 1425 else: 1426 p_result = pointsToPointset(p, self.indepvarname) 1427 try: 1428 p_result.append(self[ix:]) 1429 except ValueError: 1430 # ix > greatest index, so no points left to add 1431 # (i.e., p was appended to end) 1432 pass 1433 self.coordarray = p_result.coordarray 1434 self.labels = p_result.labels 1435 self.indepvararray = p_result.indepvararray 1436 self.makeIxMaps()
1437 1438
1439 - def append(self, parg, t=None, skipMatchingIndepvar=False):
1440 """Append individual Point or Pointset in place. 1441 1442 skipMatchingIndepvar option causes a matching independent 1443 variable value at the beginning of p to be skipped (only 1444 meaningful for appending parameterized Pointsets). This 1445 option is mainly for internal use.""" 1446 1447 # test isinstance for Pointset first because it is a sub-class of Point 1448 # and so isinstance(p, Point) will also catch Pointsets! 1449 p = copy(parg) 1450 if isinstance(p, Pointset): 1451 assert p._parameterized == self._parameterized, "Parameterization mismatch" 1452 # check p dimension and coordnames and type 1453 if compareNumTypes(self.coordtype, int32) and \ 1454 compareNumTypes(p.coordtype, float64): 1455 raise TypeError("Cannot add float64 pointset to an int32 Pointset") 1456 pdim = p.dimension 1457 if self._parameterized: 1458 if t is None: 1459 if self.indepvarname in p.coordnames: 1460 t = p[self.indepvarname] 1461 pdim = pdim - 1 1462 elif self.indepvarname == p.indepvarname: 1463 t = p.indepvararray 1464 else: 1465 raise ValueError("Independent variable missing from Pointset") 1466 if t[0] == self.indepvararray[-1] and skipMatchingIndepvar: 1467 tval = t[1:] 1468 start_ix = 1 1469 else: 1470 tval = t 1471 start_ix = 0 1472 if len(tval) > 0 and tval[0] <= self.indepvararray[-1]: 1473 #print tval[0], " <= ", self.indepvararray[-1] 1474 raise ValueError("Independent variable value too small to add pointset") 1475 added_len = len(tval) 1476 else: 1477 if t[0] == self.indepvararray[-1] and skipMatchingIndepvar: 1478 tval = t[1:] 1479 start_ix = 1 1480 else: 1481 tval = t[:] # ensures tval is an array (t might be a Pointset) 1482 start_ix = 0 1483 if len(tval) > 0 and tval[0] <= self.indepvararray[-1]: 1484 #print tval[0], " <= ", self.indepvararray[-1] 1485 raise ValueError("Independent variable value too small to add pointset") 1486 added_len = len(tval) 1487 else: 1488 if t is not None: 1489 raise TypeError("t argument cannot be used for non-parameterized pointsets") 1490 added_len = p.coordarray.shape[1] 1491 start_ix = 0 1492 assert pdim == self.dimension, "Dimension mismatch with Pointset" 1493 if pdim < p.dimension: 1494 pcoords = copy(p.coordnames) 1495 pcoords.remove(p.indepvarname) 1496 else: 1497 pcoords = p.coordnames 1498 if remain(pcoords, self.coordnames) != []: 1499 raise ValueError("Coordinate name mismatch with Pointset") 1500 old_len = self.coordarray.shape[1] 1501 new_len = old_len + added_len 1502 old_coords = self.coordarray 1503 self.coordarray = zeros((self.dimension, new_len), 1504 self.coordarray.dtype) 1505 if self._parameterized: 1506 self.indepvararray.resize(new_len) 1507 tvals = tval[range(added_len)] 1508 self.indepvararray[old_len:] = tvals 1509 for tix in xrange(old_len): 1510 self.coordarray[:, tix] = old_coords[:, tix] 1511 pdict = p.todict() 1512 self.coordarray[:, old_len:] = r_[[pdict[c][start_ix:] for c in self._ix_name_map]] 1513 p_labels = copy(p.labels) 1514 pixs = p.labels.getIndices() 1515 if start_ix == 1: 1516 p_labels.mapIndices(dict(zip(pixs, [i+old_len-1 for i in pixs]))) 1517 else: 1518 p_labels.mapIndices(dict(zip(pixs, [i+old_len for i in pixs]))) 1519 self.labels.update(p_labels) 1520 elif isinstance(p, Point): 1521 # check p dimension and coordnames and type 1522 if compareNumTypes(self.coordtype, int32) and \ 1523 compareNumTypes(p.coordtype, float64): 1524 raise TypeError("Cannot add float64 Point to an int32 Pointset") 1525 pdim = p.dimension 1526 if self._parameterized: 1527 if t is None: 1528 if self.indepvarname not in p.coordnames: 1529 raise ValueError("Independent variable missing from Point") 1530 else: 1531 tval = p[self.indepvarname] 1532 if tval <= self.indepvararray[-1]: 1533 raise ValueError("Independent variable value too small to add Point") 1534 pdim = pdim - 1 1535 else: 1536 if t <= self.indepvararray[-1]: 1537 raise ValueError("Independent variable value too small to add Point") 1538 tval = t 1539 elif t is not None: 1540 raise TypeError("t argument cannot be used for non-parameterized Pointsets") 1541 assert pdim == self.dimension, "Dimension mismatch with Point" 1542 if pdim < p.dimension: 1543 pcoords = copy(p.coordnames) 1544 if self._parameterized: 1545 pcoords.remove(self.indepvarname) 1546 else: 1547 pcoords = p.coordnames 1548 if remain(pcoords, self.coordnames) != []: 1549 raise ValueError("Coordinate name mismatch with Point") 1550 new_len = self.coordarray.shape[1]+1 1551 old_coords = self.coordarray 1552 self.coordarray = zeros((self.dimension, new_len), self.coordarray.dtype) 1553 if self._parameterized: 1554 self.indepvararray.resize(new_len) 1555 self.indepvararray.resize(new_len) 1556 self.indepvararray[new_len-1] = tval 1557 for tix in xrange(new_len-1): 1558 self.coordarray[:, tix] = old_coords[:, tix] 1559 for ix in xrange(self.dimension): 1560 self.coordarray[ix,new_len-1] = p(self._ix_name_map[ix]) 1561 if len(p.labels) > 0: 1562 self.labels.update({new_len-1: p.labels}) 1563 else: 1564 raise TypeError("append requires Point or Pointset argument") 1565 self.makeIxMaps()
1566 1567 1568 extend = append # for intuitive compatibility! 1569 1570
1571 - def toarray(self, include_indepvar=False):
1572 """Convert the pointset to a D x L array (one variable per row), 1573 where D is the dimension of the pointset and L is its length. 1574 (This is a copy of the internal attribute 'coordarray'.) 1575 1576 If the optional include_indepvar switch is set True (default False), 1577 the first row is the independent variable, and the whole 1578 array will be (D+1) x L in shape. 1579 """ 1580 if self.dimension==1: 1581 ca = copy(self.coordarray[0]) 1582 else: 1583 ca = copy(self.coordarray) 1584 if include_indepvar: 1585 ia = copy(self.indepvararray) 1586 ia.shape = (1, len(ia)) 1587 return concatenate((ia,ca)) 1588 else: 1589 return ca
1590 1591
1592 - def todict(self, aslist=False):
1593 """Convert Pointset to a dictionary of arrays (or of lists with aslist=True).""" 1594 if aslist: 1595 d = dict(zip(self._ix_name_map, self.coordarray.tolist())) 1596 else: 1597 d = dict(zip(self._ix_name_map, self.coordarray)) 1598 if self._parameterized: 1599 d[self.indepvarname] = self.indepvararray 1600 return d
1601 1602
1603 - def _infostr(self, verbose=0):
1604 if self.name == '': 1605 outputStr = "Pointset <no name>" 1606 else: 1607 outputStr = "Pointset " + self.name 1608 if self._parameterized: 1609 outputStr += " (parameterized)" 1610 else: 1611 outputStr += " (non-parameterized)" 1612 if verbose > 0: 1613 precision = 8 1614 lenv = len(self) 1615 if lenv > 8: 1616 ixslo = range(0,2) 1617 ixshi = range(lenv-2,lenv) 1618 outputStr += "\n" 1619 if self._parameterized: 1620 iv = self.indepvararray 1621 if not isinstance(iv, ndarray): 1622 iv = array(iv, self.indepvartype) # permits slicing (lists don't) 1623 if lenv > 8: 1624 alo = array2string(iv[ixslo],precision=precision) 1625 ahi = array2string(iv[ixshi],precision=precision) 1626 ivstr = alo[:-1] + ", ..., " + ahi[1:] 1627 else: 1628 ivstr = array2string(iv,precision=precision) 1629 outputStr += "Independent variable:\n" 1630 outputStr += self.indepvarname + ': '+ivstr+"\n" 1631 outputStr += "Coordinates:\n" 1632 for c in self.coordnames: 1633 v = self.coordarray[self._map_names_to_ixs(c)] 1634 if not isinstance(v, ndarray): 1635 # only alternative is a singleton numeric value (not a list) 1636 v = array([v], self.coordtype) 1637 if lenv > 8: 1638 alo = array2string(v[ixslo],precision=precision) 1639 ahi = array2string(v[ixshi],precision=precision) 1640 dvstr = alo[:-1] + ", ..., " + ahi[1:] 1641 else: 1642 dvstr = array2string(v, precision=precision) 1643 outputStr += c+': '+dvstr 1644 if c != self.coordnames[-1]: 1645 outputStr += "\n" 1646 outputStr += "\nLabels by index: " + self.labels._infostr(17) 1647 return outputStr
1648 1649
1650 - def __repr__(self):
1651 return self._infostr(verbose=1)
1652 1653
1654 - def __str__(self):
1655 return self._infostr(verbose=0)
1656 1657
1658 - def info(self, verboselevel=1):
1659 print self._infostr(verboselevel)
1660 1661
1662 - def __copy__(self):
1663 if self._parameterized: 1664 return Pointset({'coordarray': copy(self.coordarray), 1665 'coordnames': copy(self.coordnames), 1666 'indepvarname': copy(self.indepvarname), 1667 'indepvararray': copy(self.indepvararray), 1668 'norm': self._normord, 1669 'labels': copy(self.labels) 1670 }) 1671 else: 1672 return Pointset({'coordarray': copy(self.coordarray), 1673 'coordnames': copy(self.coordnames), 1674 'norm': self._normord, 1675 'labels': copy(self.labels)})
1676 1677 copy = __copy__ 1678 1679
1680 - def __getstate__(self):
1681 d = copy(self.__dict__) 1682 # remove reference to Cfunc types by converting them to strings 1683 try: 1684 d['indepvartype'] = _num_type2name[self.indepvartype] 1685 except KeyError: 1686 # non-parameterized Pointset 1687 pass 1688 d['coordtype'] = _num_type2name[self.coordtype] 1689 return d
1690 1691
1692 - def __setstate__(self, state):
1693 self.__dict__.update(state) 1694 # reinstate Cfunc types 1695 try: 1696 self.indepvartype = _num_name2type[self.indepvartype] 1697 except KeyError: 1698 # non-parameterized Pointset 1699 pass 1700 self.coordtype = _num_name2type[self.coordtype]
1701
1702 - def _match_indepvararray(self, other):
1703 """Verifies the matching of independent variable arrays in two pointsets. 1704 Does nothing if either object is not a parameterized pointset.""" 1705 try: 1706 if other._parameterized and self._parameterized: 1707 if not all(self.indepvararray == other.indepvararray): 1708 print self.indepvararray 1709 print other.indepvararray 1710 raise ValueError("Mismatched independent variable arrays") 1711 except AttributeError: 1712 pass
1713
1714 - def __add__(self, other):
1715 self._match_indepvararray(other) 1716 return Point.__add__(self, other)
1717
1718 - def __radd__(self, other):
1719 self._match_indepvararray(other) 1720 return Point.__radd__(self, other)
1721
1722 - def __sub__(self, other):
1723 self._match_indepvararray(other) 1724 return Point.__sub__(self, other)
1725
1726 - def __rsub__(self, other):
1727 self._match_indepvararray(other) 1728 return Point.__rsub__(self, other)
1729
1730 - def __mul__(self, other):
1731 self._match_indepvararray(other) 1732 return Point.__mul__(self, other)
1733
1734 - def __rmul__(self, other):
1735 self._match_indepvararray(other) 1736 return Point.__rmul__(self, other)
1737
1738 - def __div__(self, other):
1739 self._match_indepvararray(other) 1740 return Point.__div__(self, other)
1741
1742 - def __rdiv__(self, other):
1743 self._match_indepvararray(other) 1744 return Point.__rdiv__(self, other)
1745
1746 - def find(self, indepval, end=None):
1747 """find returns an integer index for where to place 1748 a point having independent variable value <indepval> in 1749 the Pointset, if <indepval> already exists. Otherwise, a 1750 pair indicating the nearest independent variable values 1751 present in the Pointset is returned. 1752 1753 To ensure an integer is always returned, choose a left or 1754 right side to choose from the pair, using end=0 or 1 respectively.""" 1755 if not self._parameterized: 1756 raise TypeError("Cannot find index from independent variable for " 1757 "a non-parameterized Pointset") 1758 try: 1759 ix = self.indepvararray.tolist().index(indepval) 1760 result = ix 1761 except ValueError: 1762 cond = less(self.indepvararray, indepval).tolist() 1763 try: 1764 ix = cond.index(0) 1765 result = (ix-1, ix) 1766 except ValueError: 1767 result = (len(self.indepvararray)-1, len(self.indepvararray)) 1768 if end is not None: 1769 result = result[end] 1770 return result
1771 1772 # deprecated 1773 findIndex = find
1774 1775 1776 # ---------------------------------------------------------------------------- 1777 1778
1779 -class PointInfo(object):
1780 """Structure for storing individual point labels and information 1781 dictionaries within a Pointset object. 1782 1783 This class will not know the size of the Pointset it is associated with, 1784 so index upper limits will not be checked in advance. 1785 1786 Do not use a PointInfo object as an iterator, as it is 'infinite' in size! 1787 (It uses DefaultDicts as its internal storage, which return {} for 1788 undefined labels.)""" 1789
1790 - def __init__(self, ptlabels=None):
1791 if ptlabels is None: 1792 self.by_label = DefaultDict({}) 1793 self.by_index = DefaultDict({}) 1794 elif isinstance(ptlabels, PointInfo): 1795 self.by_label = ptlabels.by_label 1796 self.by_index = ptlabels.by_index 1797 elif isinstance(ptlabels, dict): 1798 # always expect the dictionary to be based on index 1799 self.by_label = DefaultDict({}) 1800 self.by_index = DefaultDict({}) 1801 for k, v in ptlabels.iteritems(): 1802 if not isinstance(k, _int_types): 1803 raise TypeError("Initialization dictionary must be keyed " 1804 "by integer indices") 1805 if isinstance(v, str): 1806 self.by_label[v][k] = {} 1807 self.by_index[k][v] = {} 1808 else: 1809 for label, infodict in v.iteritems(): 1810 self.by_label[label][k] = infodict 1811 self.by_index[k][label] = infodict 1812 else: 1813 raise TypeError("Invalid labels at initialization of PointInfo")
1814 1815
1816 - def mapIndices(self, ixMapDict):
1817 by_index = {} 1818 ixMap = symbolMapClass(ixMapDict) 1819 for ix, rest in self.by_index.iteritems(): 1820 by_index[ixMap(ix)] = rest 1821 self.__init__(by_index)
1822 1823
1824 - def mapNames(self, themap):
1825 """Map labels, using a symbol map of class symbolMapClass.""" 1826 self.by_label = mapNames(themap, self.by_label) 1827 new_by_index = {} 1828 for ix, labdict in self.by_index.iteritems(): 1829 new_by_index[ix] = mapNames(themap, labdict) 1830 self.by_index = new_by_index
1831 1832
1833 - def sortByIndex(self):
1834 ixkeys = sortedDictKeys(self.by_index) 1835 return zip(ixkeys,[self.by_index[ix] for ix in ixkeys])
1836 1837
1838 - def sortByLabel(self):
1839 labelkeys = sortedDictKeys(self.by_label) 1840 return zip(labelkeys,[self.by_label[label] for label in labelkeys])
1841 1842
1843 - def getIndices(self):
1844 return sortedDictKeys(self.by_index)
1845 1846
1847 - def getLabels(self):
1848 return sortedDictKeys(self.by_label)
1849 1850
1851 - def __contains__(self, key):
1852 return key in self.by_index or key in self.by_label
1853 1854
1855 - def __getitem__(self, key):
1856 # indices already are enforced to be integers, and labels strings, 1857 # so this is a safe way to search! 1858 # Note: if don't use if-then test then DefaultDict will 1859 # create an empty entry for the failed key when .values() is called! 1860 if isinstance(key, tuple): 1861 raise TypeError("Can only reference PointInfo with a single key") 1862 else: 1863 if isinstance(key, (slice, list, ndarray)): 1864 if isinstance(key, slice): 1865 self_ixs = self.getIndices() 1866 if len(self_ixs) == 0: 1867 max_ixs = 0 1868 else: 1869 max_ixs = max(self_ixs) 1870 stop = key.stop or max_ixs+1 1871 try: 1872 s1, s2, s3 = key.indices(stop) 1873 ixs = xrange(s1, s2, s3) 1874 key = intersect(ixs, self_ixs) 1875 except TypeError: 1876 key = self_ixs 1877 else: 1878 if all([isinstance(k, str) for k in key]): 1879 keylabels = intersect(key, self.getLabels()) 1880 key = [] 1881 for l in keylabels: 1882 key.extend(self.by_label[l].keys()) 1883 key = makeSeqUnique(key) 1884 elif all([isinstance(k, _int_types) for k in key]): 1885 key = intersect(key, self.getIndices()) 1886 else: 1887 raise TypeError("Invalid key type for PointInfo") 1888 return PointInfo(dict(zip(key,[self.by_index[i] for i in key]))) 1889 elif key in self.by_index: 1890 return self.by_index[key] 1891 elif key in self.by_label: 1892 return self.by_label[key] 1893 elif isinstance(key, int) and key < 0: 1894 raise IndexError("Cannot use negative indices for PointInfo") 1895 else: 1896 return {}
1897 1898
1899 - def __setitem__(self, key1, the_rest):
1900 if isinstance(the_rest, tuple) and len(the_rest) == 2: 1901 if isinstance(the_rest[0], str): 1902 label = the_rest[0] 1903 ix = None 1904 elif isinstance(the_rest[0], _int_types): 1905 ix = the_rest[0] 1906 label = None 1907 else: 1908 raise TypeError("String expected for label") 1909 if isinstance(the_rest[1], dict): 1910 info = copy(the_rest[1]) 1911 else: 1912 raise TypeError("Dictionary expected for info") 1913 elif isinstance(the_rest, str): 1914 label = the_rest 1915 ix = None 1916 info = {} 1917 elif isinstance(the_rest, _int_types): 1918 ix = the_rest 1919 label = None 1920 info = {} 1921 elif isinstance(the_rest, list): 1922 self.__setitem__(key1, the_rest[0]) 1923 for item in the_rest[1:]: 1924 if isinstance(item, tuple) and len(item) == 2: 1925 self.update(key1, item[0], item[1]) 1926 else: 1927 self.update(key1, item) 1928 return 1929 else: 1930 raise TypeError("Invalid item to set in PointInfo") 1931 if isinstance(key1, _int_types): 1932 if label is None: 1933 raise TypeError("Label expected") 1934 ix = key1 1935 elif isinstance(key1, str): 1936 if ix is None: 1937 raise TypeError("Index expected") 1938 label = key1 1939 if ix < 0: 1940 raise IndexError("Index must be non-negative") 1941 try: 1942 self.by_label[label].update({ix: info}) 1943 except KeyError: 1944 self.by_label[label] = {ix: info} 1945 try: 1946 self.by_index[ix].update({label: info}) 1947 except KeyError: 1948 self.by_index[ix] = {label: info}
1949 1950
1951 - def __len__(self):
1952 return len(self.by_index)
1953 1954
1955 - def remove(self, key1, *key2):
1956 """remove one or more items, keyed either by index or label.""" 1957 byix = key1 in self.by_index 1958 if key2 == (): 1959 # remove all labels associated with index, or vice versa 1960 if byix: 1961 key2 = self.by_index[key1].keys() 1962 else: 1963 key2 = self.by_label[key1].keys() 1964 if byix: 1965 for k in key2: 1966 # have to check k in dict otherwise DefaultDict creates entry! 1967 if k in self.by_label: 1968 del self.by_index[key1][k] 1969 del self.by_label[k][key1] 1970 else: 1971 raise KeyError("Label not found") 1972 if self.by_label[k] == {}: 1973 del self.by_label[k] 1974 if self.by_index[key1] == {}: 1975 del self.by_index[key1] 1976 else: 1977 for k in key2: 1978 # have to check k in dict otherwise DefaultDict creates entry! 1979 if k in self.by_index: 1980 del self.by_index[k][key1] 1981 del self.by_label[key1][k] 1982 else: 1983 raise KeyError("Index not found") 1984 if self.by_index[k] == {}: 1985 del self.by_index[k] 1986 if self.by_label[key1] == {}: 1987 del self.by_label[key1]
1988 1989
1990 - def update(self, key1, key2=None, info=None):
1991 if isinstance(key1, PointInfo): 1992 if key2 is None and info is None: 1993 for k, v in key1.by_index.iteritems(): 1994 for vk, vv in v.iteritems(): 1995 self.update(k, vk, vv) 1996 else: 1997 raise TypeError("Invalid calling sequence to update") 1998 elif isinstance(key1, dict): 1999 if key2 is None and info is None: 2000 for k, v in key1.iteritems(): 2001 if isinstance(k, _int_types): 2002 if k < 0: 2003 k = k + len(self.by_index) 2004 if isinstance(v, str): 2005 k2 = v 2006 k3 = {} 2007 self.update(k, k2, k3) 2008 elif isinstance(v, tuple) and len(v)==2: 2009 k2 = v[0] 2010 k3 = v[1] 2011 self.update(k, k2, k3) 2012 elif isinstance(v, dict): 2013 for k2, k3 in v.iteritems(): 2014 self.update(k, k2, k3) 2015 else: 2016 raise ValueError("Invalid data for update") 2017 else: 2018 raise TypeError("Invalid index for label") 2019 else: 2020 raise TypeError("Invalid calling sequence to update") 2021 elif isinstance(key1, _int_types): 2022 if info is None: 2023 info = {} 2024 if key1 in self.by_index: 2025 if key2 in self.by_index[key1]: 2026 self.by_index[key1][key2].update(info) 2027 else: 2028 self.__setitem__(key1, (key2, info)) 2029 else: 2030 self.__setitem__(key1, (key2, info)) 2031 elif isinstance(key1, str): 2032 if info is None: 2033 info = {} 2034 if key1 in self.by_label: 2035 if key2 in self.by_label[key1]: 2036 self.by_label[key1][key2].update(info) 2037 else: 2038 self.__setitem__(key2, (key1, info)) 2039 else: 2040 self.__setitem__(key2, (key1, info)) 2041 else: 2042 raise TypeError("Invalid type for update")
2043 2044
2045 - def __delitem__(self, key):
2046 if key in self.by_index: 2047 labels = self.by_index[key].keys() 2048 del self.by_index[key] 2049 for label in labels: 2050 del self.by_label[label][key] 2051 if self.by_label[label] == {}: 2052 del self.by_label[label] 2053 elif key in self.by_label: 2054 ixs = self.by_label[key].keys() 2055 del self.by_label[key] 2056 for ix in ixs: 2057 del self.by_index[ix][key] 2058 if self.by_index[ix] == {}: 2059 del self.by_index[ix] 2060 else: 2061 raise KeyError("Index or label not found")
2062 2063
2064 - def __eq__(self, other):
2065 try: 2066 return all(self.by_index.keys() == other.by_index.keys()) and \ 2067 all(self.by_label.keys() == other.by_label.keys()) 2068 except AttributeError: 2069 raise TypeError("Invalid type for comparison to PointInfo")
2070 2071
2072 - def __ne__(self, other):
2073 return not self.__eq__(other)
2074
2075 - def _infostr(self, tab=0):
2076 lenself = len(self) 2077 tabstr = " "*tab 2078 basestr = ",\n"+tabstr 2079 if lenself > 0: 2080 entries = self.sortByIndex() 2081 if lenself > 8: 2082 return basestr.join([_pretty_print_label(i) for i in entries[0:3]]) + ",\n" +\ 2083 (tabstr + " .\n")*3 + tabstr +\ 2084 basestr.join([_pretty_print_label(i) for i in entries[-3:]]) 2085 else: 2086 return basestr.join([_pretty_print_label(i) for i in entries]) 2087 else: 2088 return "Empty"
2089 2090
2091 - def __repr__(self):
2092 return self._infostr()
2093 2094 __str__ = __repr__
2095 2096
2097 -def _pretty_print_label(d):
2098 """Internal utility to pretty print point label info.""" 2099 s = " %s: "%repr(d[0]) 2100 entry_keys = d[1].keys() 2101 ki = 0 2102 kimax = len(entry_keys) 2103 for k in entry_keys: 2104 keys = d[1][k].keys() 2105 if len(keys) == 0: 2106 s += "{%s: {}}"%k 2107 else: 2108 s += "{%s: {keys=%s}}"%(k,",".join(keys)) 2109 if ki < kimax-1: 2110 s += ', ' 2111 ki += 1 2112 return s
2113 2114 # ------------------------------------------------ 2115 2116
2117 -def comparePointCoords(p1, p2, fussy=False):
2118 """Compare two Points, Pointsets, or dictionary of point data, coordinate-wise. 2119 If p1 or p2 are Pointsets, their independent variable values, if present, are 2120 *not* compared. 2121 2122 fussy option causes point norm order and coordinate types to be 2123 checked too (requires both arguments to be Points or Pointsets). 2124 """ 2125 try: 2126 p1d = dict(p1) 2127 p1dk = p1d.keys() 2128 p2d = dict(p2) 2129 p2dk = p2d.keys() 2130 except: 2131 raise TypeError("Invalid Points, Pointsets, or dictionaries passed " 2132 "to comparePointCoords") 2133 test1 = alltrue([ks[0]==ks[1] for ks in zip(p1dk, p2dk)]) 2134 test2 = alltrue([vs[0]==vs[1] for vs in \ 2135 zip([p1d[k] for k in p1dk], [p2d[k] for k in p2dk])]) 2136 if fussy: 2137 try: 2138 test3 = p1._normord == p2._normord 2139 test4 = compareNumTypes(p1.coordtype, p2.coordtype) 2140 return test1 and test2 and test3 and test4 2141 except AttributeError: 2142 raise TypeError("Invalid Points, Pointsets, or dictionaries passed " 2143 "to comparePointCoords with fussy option") 2144 else: 2145 return test1 and test2
2146 2147
2148 -def isparameterized(p):
2149 """Returns True if Point or Pointset p is parameterized, False otherwise""" 2150 return p._parameterized
2151 2152
2153 -def makeNonParameterized(p):
2154 """Return a new Pointset stripped of its parameterization. 2155 """ 2156 if isinstance(p, Pointset) and p._isparameterized: 2157 return Pointset({'coordarray': copy(p.coordarray), 2158 'coordnames': copy(p.coordnames), 2159 'norm': p._normord, 2160 'labels': copy(p.labels)}) 2161 else: 2162 raise TypeError("Must provide a parameterized Pointset")
2163 2164
2165 -def pointsToPointset(pointlist, indepvarname='', indepvararray=None, 2166 indepvartype=float, norm=2):
2167 """Generate a Pointset from a list of Point objects (or a singleton Point). 2168 2169 Include a name for the independent variable if constructing a 2170 parameterized pointset. The independent variable should be a 2171 coordinate of the Points passed, otherwise it can be passed as the 2172 optional third argument. 2173 """ 2174 2175 if not isinstance(indepvarname, str): 2176 raise TypeError("String expected for independent variable name") 2177 if isinstance(pointlist, Point): 2178 pointlist = [pointlist] 2179 coordnames = [] 2180 ptype = '' 2181 paramd = indepvarname != "" 2182 if not paramd and indepvararray is not None: 2183 raise ValueError("Must supply independent variable name for " 2184 "parameterized Pointset") 2185 if paramd and indepvararray is None: 2186 iv = [] 2187 i = 0 2188 labels = {} 2189 for p in pointlist: 2190 assert isinstance(p, Point), \ 2191 "pointlist argument must only contain Points" 2192 if coordnames == []: 2193 ptype = p.coordtype 2194 pdim = p.dimension 2195 coordnames = p.coordnames 2196 xcoordnames = copy(coordnames) 2197 if paramd and indepvararray is None: 2198 assert indepvarname in coordnames, \ 2199 "Independent variable name missing" 2200 del xcoordnames[xcoordnames.index(indepvarname)] 2201 dv = {}.fromkeys(xcoordnames) 2202 for c in xcoordnames: 2203 dv[c] = [] 2204 if p.labels != {}: 2205 labels.update({0: p.labels}) 2206 i += 1 2207 else: 2208 # coerce ints to float types if mixed 2209 if compareNumTypes(ptype, int32): 2210 if compareNumTypes(p.coordtype, float64): 2211 ptype = float64 2212 elif compareNumTypes(p.coordtype, int32): 2213 pass 2214 else: 2215 raise TypeError("Type mismatch in points") 2216 elif compareNumTypes(ptype, float64): 2217 if not compareNumTypes(p.coordtype, (float64, int32)): 2218 raise TypeError("Type mismatch in points") 2219 else: 2220 raise TypeError("Type mismatch in points") 2221 assert pdim == p.dimension, "Dimension mismatch in points" 2222 if remain(coordnames,p.coordnames) != []: 2223 raise ValueError("Coordinate name mismatch in points") 2224 if p.labels != {}: 2225 labels.update({i: p.labels}) 2226 i += 1 2227 for c in xcoordnames: dv[c].append(p(c)) 2228 if paramd and indepvararray is None: 2229 iv.append(p(indepvarname)) 2230 # submit data as array to maintain coordname ordering present in Points 2231 dim = len(xcoordnames) 2232 ca = array([dv[c] for c in xcoordnames], ptype) 2233 argDict = {'coordarray': ca, 2234 'coordnames': xcoordnames, 2235 'coordtype': ptype, 2236 'labels': labels, 2237 'norm': norm 2238 } 2239 if paramd: 2240 if indepvararray is None: 2241 indepvararray = array(iv, ptype) 2242 argDict.update({'indepvarname': indepvarname, 2243 'indepvararray': indepvararray, 2244 'indepvartype': indepvartype}) 2245 return Pointset(argDict)
2246 2247
2248 -def arrayToPointset(a, vnames=None, ia=None, iname=""):
2249 """Convert an array to a non-parameterized Pointset. The inclusion of an 2250 optional independent variable array creates a parameterized Pointset. 2251 2252 Coordinate (and independent variable) names are optional: the defaults are 2253 the array indices (and 't' for the independent variable). 2254 """ 2255 if rank(a) > 2: 2256 raise ValueError("Cannot convert arrays of rank > 2") 2257 if rank(a) == 0: 2258 raise ValueError("Cannot convert arrays of rank 0") 2259 if vnames is None: 2260 vnames = [str(i) for i in range(shape(a)[0])] 2261 else: 2262 if len(vnames) != shape(a)[0]: 2263 raise ValueError("Mismatch between number of coordinate names and" 2264 " number of rows in array.\nCoordinates are " 2265 "assumed to be the rows of the array") 2266 if ia is None: 2267 assert iname=="", ("Independent variable name must be none if no " 2268 "independent variable array provided") 2269 return Pointset({'coordarray': a, 2270 'coordnames': vnames}) 2271 else: 2272 if iname == "": 2273 iname = "t" 2274 return Pointset({'coordarray': a, 2275 'coordnames': vnames, 2276 'indepvararray': ia, 2277 'indepvarname': iname})
2278
2279 -def exportPointset(thepointset, infodict, separator=' ', 2280 precision=12, varvaldir='col', 2281 ext='', append=False):
2282 """Export a pointset to a set of ASCII whitespace- (or 2283 user-defined character-) separated data files. Option to list each 2284 variable's data in rows ('across') or in columns ('down'). 2285 Existing files of the same names will be overwritten, unless the 2286 'append' boolean option is set. 2287 2288 NB. If the file extension argument 'ext' is present without a 2289 leading dot, one will be added. 2290 2291 infodict should consist of: keys = filenames, values = tuples of 2292 pointset variable names to export. 2293 """ 2294 assert varvaldir in ['col', 'row'], \ 2295 "invalid variable value write direction" 2296 # in order to avoid import cycles, cannot explicitly check that 2297 # thepointset is of type Pointset, because Points.py imports this file 2298 # (utils.py), so check an attribute instead. 2299 try: 2300 thepointset.coordnames 2301 except AttributeError: 2302 raise TypeError("Must pass Pointset to this function: use " 2303 "arrayToPointset first!") 2304 infodict_usedkeys = [] 2305 for key, info in infodict.iteritems(): 2306 if isinstance(info, str): 2307 infodict_usedkeys += [info] 2308 elif info == []: 2309 infodict[key] = copy.copy(thepointset.coordnames) 2310 infodict_usedkeys.extend(thepointset.coordnames) 2311 else: 2312 infodict_usedkeys += list(info) 2313 allnames = copy(thepointset.coordnames) 2314 if thepointset._parameterized: 2315 allnames.append(thepointset.indepvarname) 2316 remlist = remain(infodict_usedkeys, allnames+range(len(allnames))) 2317 if remlist != []: 2318 print "Coords not found in pointset:", remlist 2319 raise ValueError("invalid keys in infodict - some not present " 2320 "in thepointset") 2321 assert isinstance(ext, str), "'ext' extension argument must be a string" 2322 if ext != '': 2323 if ext[0] != '.': 2324 ext = '.'+ext 2325 if append: 2326 assert varvaldir == 'col', ("append mode not supported for row" 2327 "format of data ordering") 2328 modestr = 'a' 2329 else: 2330 modestr = 'w' 2331 totlen = len(thepointset) 2332 if totlen == 0: 2333 raise ValueError("Pointset is empty") 2334 for fname, tup in infodict.iteritems(): 2335 try: 2336 f = open(fname+ext, modestr) 2337 except IOError: 2338 print "There was a problem opening file "+fname+ext 2339 raise 2340 try: 2341 if isinstance(tup, str): 2342 try: 2343 varray = thepointset[tup] 2344 except TypeError: 2345 raise ValueError("Invalid specification of coordinates") 2346 elif isinstance(tup, int): 2347 try: 2348 varray = thepointset[:,tup].toarray() 2349 except TypeError: 2350 raise ValueError("Invalid specification of coordinates") 2351 elif isinstance(tup, (list, tuple)): 2352 if alltrue([isinstance(ti,str) for ti in tup]): 2353 thetup=list(tup) 2354 if thepointset.indepvarname in tup: 2355 tix = thetup.index(thepointset.indepvarname) 2356 thetup.remove(thepointset.indepvarname) 2357 try: 2358 vlist = thepointset[thetup].toarray().tolist() 2359 except TypeError: 2360 raise ValueError("Invalid specification of coordinates") 2361 if len(thetup)==1: 2362 vlist = [vlist] 2363 if thepointset.indepvarname in tup: 2364 vlist.insert(tix, thepointset.indepvararray.tolist()) 2365 varray = array(vlist) 2366 elif alltrue([isinstance(ti,_int_types) for ti in tup]): 2367 try: 2368 varray = thepointset[:,tup].toarray() 2369 except TypeError: 2370 raise ValueError("Invalid specification of coordinates") 2371 else: 2372 raise ValueError("Invalid specification of coordinates") 2373 else: 2374 f.close() 2375 raise TypeError("infodict values must be singletons or " 2376 "tuples/lists of strings or integers") 2377 except IOError: 2378 f.close() 2379 print "Problem writing to file"+fname+ext 2380 raise 2381 except KeyError: 2382 f.close() 2383 raise KeyError("Keys in infodict not found in pointset") 2384 if isinstance(precision, int): 2385 assert precision > 0 2386 ps = str(precision) 2387 else: 2388 raise TypeError("precision must be a positive integer") 2389 if varvaldir == 'row': 2390 savetxt(f, varray, '%.'+ps+'f', separator) 2391 else: 2392 savetxt(f, transpose(varray), '%.'+ps+'f', separator) 2393 f.close()
2394 2395
2396 -def importPointset(xFileName, t=None, indices=None, sep=" ", 2397 preamblelines=0):
2398 """Import ASCII format files containing data points. 2399 If the first row contains string names then the output 2400 will be a pointset, otherwise a numeric array. 2401 2402 A dictionary is returned, with keys 'vararray' will point to the 2403 data. The 't' argument can specify one of several things: 2404 2405 string: filename to read single-column of time values (same length as 2406 xFileName) 2407 sequence type: time values (same length as xFileName) 2408 integer: column in xFileName to treat as time data 2409 2410 If used, this leads to and an additional key in the return 2411 dictionary where 't' points to the independent variable array. 2412 2413 Specific columns can be selected for the variable data array by 2414 specifying a list of column indices in argument 'indices'. 2415 2416 The separator used in the ASCII file can be specified by argument 2417 'sep' (defaults to single whitespace character). 2418 2419 preamblelines (positive integer) specifies how many lines to skip before 2420 starting to read data (in case of preceding text) -- default 0. 2421 """ 2422 2423 if indices is None: 2424 indices = [] 2425 xFile = open(xFileName, 'r') 2426 xFileStrList = xFile.readlines() 2427 filelen = len(xFileStrList)-preamblelines 2428 if filelen == 1 and '\r' in xFileStrList[0]: 2429 # fix problem when no newlines picked up, only '\r' 2430 xFileStrList = xFileStrList[0].split('\r') 2431 filelen = len(xFileStrList) 2432 if filelen <= 1: 2433 raise ValueError("Only 1 data point found in variables datafile") 2434 x_dummy_all = xFileStrList[preamblelines].rstrip("\n") 2435 x_dummy_vallist = filter(lambda s: s != '', x_dummy_all.split(sep)) 2436 if t is None: 2437 get_t = 0 2438 elif isinstance(t, str): 2439 tFileName = t 2440 tFile = open(tFileName, 'r') 2441 tFileStrList = tFile.readlines() 2442 if len(tFileStrList)-preamblelines != filelen: 2443 raise ValueError("Length of data and time files must be equal" 2444 " -- are there any blank lines in the files?") 2445 get_t = 1 2446 elif isinstance(t, _seq_types): 2447 if len(t) != filelen: 2448 raise ValueError("Length of data file and t array must be " 2449 "equal -- are there any blank lines in the files?") 2450 tVals = t 2451 get_t = 0 2452 elif isinstance(t, _int_types): 2453 # t represents column index to find time data in data file 2454 if t >= len(x_dummy_vallist) or t < 0: 2455 raise ValueError("t index out of range") 2456 get_t = 2 2457 if indices == []: 2458 if get_t == 2: 2459 dim = len(x_dummy_vallist)-1 2460 indices = remain(range(0,dim+1),[t]) 2461 else: 2462 dim = len(x_dummy_vallist) 2463 indices = range(0,dim) 2464 else: 2465 dim = len(indices) 2466 if get_t == 2: 2467 if t in indices: 2468 raise ValueError("You specified column "+str(t)+" as time " 2469 "data, but you have specified it as a data column in " 2470 "indices argument") 2471 # try to find variable names. if successful, start at row 1 2472 start = preamblelines 2473 # replace unnecessary quote marks in strings 2474 test_line = [n.strip('"').strip("'") for n in \ 2475 xFileStrList[preamblelines].lstrip(sep).lstrip(" ").rstrip("\n").rstrip("\r").split(sep)] 2476 def is_float(vstr): 2477 try: 2478 val = float(vstr) 2479 except ValueError: 2480 return False 2481 else: 2482 return True
2483 if alltrue([not is_float(n) for n in test_line]): 2484 # success 2485 start += 1 2486 # replace any internal spaces with underscores, remove dots 2487 test_line = [n.replace(" ", "_").replace(".","") for n in test_line] 2488 if get_t == 2: 2489 t_name = test_line[t] 2490 varnames = test_line[0:t]+test_line[t+1:] 2491 else: 2492 if get_t == 1: 2493 # try first line of t file 2494 t_test = tFileStrList[0].lstrip(" ").rstrip("\n").rstrip("\r").replace(".","").replace(" ","_").strip('"').strip("'") 2495 if is_float(t_test): 2496 # already checked that file lengths were the same 2497 raise ValueError("First line of t file shouldn't be a number") 2498 else: 2499 t_name = t_test 2500 else: 2501 t_name = 't' 2502 varnames = test_line 2503 else: 2504 t_name = 't' 2505 varnames = None 2506 tVals = zeros(filelen-start, float) 2507 xVals = zeros([filelen-start, dim], float) 2508 # read rest of file 2509 for i in xrange(filelen-start): 2510 vLine = xFileStrList[i+start].rstrip("\n") 2511 if vLine == '': 2512 continue 2513 vLineVals = filter(lambda s: s != '', vLine.split(sep)) 2514 if get_t == 1: 2515 # Additional left strip of space char in case sep is different 2516 tLine = tFileStrList[i+start].rstrip("\n").lstrip(sep).lstrip(" ") 2517 if len(tLine.split(sep)) != 1: 2518 raise ValueError("Only one t value expected per line of" 2519 " datafile") 2520 if tLine == '': 2521 continue 2522 tVals[i] = float(tLine) 2523 elif get_t == 2: 2524 tVals[i] = float(vLineVals[t]) 2525 try: 2526 xLineVals = [vLineVals[ix] for ix in indices] 2527 except IndexError: 2528 print "Valid indices were: 0 -", len(vLineVals)-1 2529 raise 2530 if len(xLineVals) != dim: 2531 raise ValueError("Exactly "+str(dim)+" values expected per " 2532 "line of datafile") 2533 xVals[i] = array([float(xstr) for xstr in xLineVals], float) 2534 xFile.close() 2535 if get_t == 1: 2536 tFile.close() 2537 if get_t == 0: 2538 if varnames is None: 2539 return xVals 2540 else: 2541 # non-parameterized pointset 2542 return Pointset(dict(zip(varnames, xVals))) 2543 else: 2544 if varnames is None: 2545 return {t_name: tVals, 'vararray': xVals.T} 2546 else: 2547 return Pointset(indepvardict={t_name: tVals}, 2548 coorddict=dict(zip(varnames,xVals.T))) 2549 2550
2551 -def export_pointset_to_CSV(filename, pts):
2552 """Simple export that ignores all metadata in pts, 2553 including name, tags, norm, etc. 2554 Data is arranged by row only. 2555 2556 Independent variable is first column, if it exists in pts. 2557 """ 2558 import csv 2559 outfile = open(filename, 'w') 2560 writer = csv.writer(outfile) 2561 2562 # header row 2563 if pts._parameterized: 2564 rows_header = [pts.indepvarname] + pts.coordnames 2565 else: 2566 rows_header = pts.coordnames 2567 writer.writerow(rows_header) 2568 2569 # data rows 2570 if pts._parameterized: 2571 for i in xrange(len(pts)): 2572 writer.writerow([pts.indepvararray[i]] + list(pts.coordarray[:,i])) 2573 else: 2574 for i in xrange(len(pts)): 2575 writer.writerow(pts.coordarray[:,i]) 2576 2577 outfile.close()
2578 2579
2580 -def mergePointsets(pts1, pts2):
2581 """Merges two pointsets into a new pointset, preserving (merging) any 2582 metadata in each. 2583 2584 In particular, if each have different accuracy tolerances, the 2585 larger of the two will be used. If each have different 'checklevel' values, 2586 the larger of the two will be used. Point labels will be merged. Names 2587 will also be merged. 2588 2589 If both are parameterized, their independent variable arrays 2590 must be identical. If only one is parameterized, the result will be 2591 too. The two pointsets must be identical in length. 2592 2593 The norm associated with each pointset must be the same. 2594 """ 2595 len1 = len(pts1) 2596 len2 = len(pts2) 2597 assert len1 == len2, "Pointsets must have equal length" 2598 assert pts1._normord == pts2._normord, "Pointsets must use the same norm" 2599 isparam1 = isparameterized(pts1) 2600 isparam2 = isparameterized(pts2) 2601 if isparam1 and isparam2: 2602 assert pts1.indepvarname == pts2.indepvarname, \ 2603 "Parameterized pointsets must have identical independent variable names" 2604 assert all(pts1.indepvararray == pts2.indepvararray), \ 2605 "Parameterized pointsets must have identical independent variable values" 2606 common_coords = intersect(pts1.coordnames, pts2.coordnames) 2607 for c in common_coords: 2608 assert all(pts1[c] == pts2[c]), \ 2609 "Pointsets must not share any coordinate names whose values are not identical" 2610 args = {} 2611 if isparam1 or isparam2: 2612 if isparam1: 2613 tvals = pts1.indepvararray 2614 tname = pts1.indepvarname 2615 else: 2616 tvals = pts2.indepvararray 2617 tname = pts2.indepvarname 2618 args['indepvardict'] = {tname: tvals} 2619 args['checklevel'] = max(pts1.checklevel, pts2.checklevel) 2620 args['tolerance'] = max(pts1._abseps, pts2._abseps) 2621 if name is None: 2622 if pts1.name == "": 2623 name1 = "<unnamed>" 2624 else: 2625 name1 = pts1.name 2626 if pts2.name == "": 2627 name2 = "<unnamed>" 2628 else: 2629 name2 = pts2.name 2630 args['name'] = "Merged %s:%s" % (name1, name2) 2631 coorddict = pts1.todict() 2632 coorddict.update(pts2.todict()) 2633 args['coorddict'] = coorddict 2634 lab1 = deepcopy(pts1.labels) 2635 lab2 = deepcopy(pts2.labels) 2636 lab2.update(lab1) 2637 args['labels'] = lab2 2638 return Pointset(**args)
2639 2640
2641 -def padPointset(pts, pinterval, value_dict, eps=None):
2642 """Pad a pointset pts with values from value_dict over the interval given 2643 by pinterval (pair). For each side of the interval outside of the current independent 2644 variable domain of pts, two new points are added, one at the outer limit 2645 of the interval, and one a distance eps (default the abseps setting of pts) 2646 from the existing closest point in pts. 2647 """ 2648 tlo, thi = pinterval 2649 ts = pts.indepvararray 2650 all_dict = value_dict.copy() 2651 assert remain(value_dict.keys(), pts.coordnames) == [] 2652 if eps is None: 2653 eps = pts._abseps 2654 if tlo < ts[0]: 2655 all_dict['t'] = tlo 2656 pts.insert(Point(coorddict=all_dict, 2657 labels='pad')) 2658 all_dict['t'] = ts[0]-eps 2659 pts.insert(Point(coorddict=all_dict, 2660 labels='pad')) 2661 if thi > ts[-1]: 2662 all_dict['t'] = ts[-1]+eps 2663 pts.insert(Point(coorddict=all_dict, 2664 labels='pad')) 2665 all_dict['t'] = thi 2666 pts.insert(Point(coorddict=all_dict, 2667 labels='pad')) 2668 return pts
2669