• Main Page
  • Namespaces
  • Classes
  • Files
  • File List

/home/mark/model/software/ScrumPy/ScrumPy/Util/Seq.py

00001 
00002 
00003 """
00004 
00005 ScrumPy -- Metabolic Modelling with Python
00006 
00007 Copyright Mark Poolman 1995 - 2002
00008 
00009  This file is part of ScrumPy.
00010 
00011     ScrumPy is free software; you can redistribute it and/or modify
00012     it under the terms of the GNU General Public License as published by
00013     the Free Software Foundation; either version 2 of the License, or
00014     (at your option) any later version.
00015 
00016     ScrumPy is distributed in the hope that it will be useful,
00017     but WITHOUT ANY WARRANTY; without even the implied warranty of
00018     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019     GNU General Public License for more details.
00020 
00021     You should have received a copy of the GNU General Public License
00022     along with ScrumPy; if not, write to the Free Software
00023     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00024 
00025 """
00026 """
00027 M.G. Poolman 10/01/01 onwards
00028 Seq.py
00029 some useful things to do to sequences
00030 """
00031 
00032 import math,random
00033 
00034 import Sorter
00035 
00036 #   First some Vector math type stuff
00037 #   These will be (*much*) slower than the NumPy equivs, but are more flexible
00038 #   with regard to element type
00039 
00040 
00041 def Mod(seq):
00042     """ pre: all elements in seq are numeric
00043        post: return |seq| as a float """
00044 
00045     return math.sqrt(
00046         sum(map(lambda x:x*x, map(float,seq)))
00047     )
00048 
00049 
00050 
00051 def DotProd(a,b):
00052     """ pre: all els in a and b are numeric, len(a)==len(b)
00053        post: returns the dot (scalar) product of a and b """
00054 
00055     return sum(map(lambda x,y:x*y, a,b))
00056 
00057 
00058 
00059 
00060 def CosTheta(a,b):
00061     """ pre: Mod(a) !=0, Mod(b) != 0
00062        post: the cosine of the angle between a and b """
00063 
00064     rv = DotProd(a,b)/(Mod(a) * Mod(b))
00065     if rv > 1.0:
00066         rv = 1.0
00067     elif rv < -1.0:
00068         rv = -1.0
00069     return rv
00070 
00071 def AreParallel(a,b):
00072     """ pre: len(a) == len(b), all items are of ArbRat type
00073       post: AreParallel(a,b) =>(a and b are parallel, if parallel relative magintitude) """
00074 
00075     lenab = len(a)
00076     rat1 = 0
00077 
00078     for i in range(lenab):
00079         if b[i] !=0:
00080             if a[i] ==0:
00081                 return False,0
00082             if rat1 == 0:
00083                 rat1 = a[i]/b[i]
00084             elif rat1 != a[i]/b[i]:
00085                     return False,0
00086         elif a[i] != 0:
00087             return False,0
00088     return True,rat1
00089 
00090 
00091 
00092 
00093 
00094 def Angle(a,b):
00095     """ pre: Mod(a) != 0, Mod(b) != 0
00096       post: Angle in radians between a and b"""
00097     # This function should be in Sci ?
00098 
00099     ct = float(CosTheta(a,b))
00100     # trap rounding errors resulting in -1.0 < ct < 1.0 (and marginal time saving for ct == +/-1.0)
00101     if ct >=1.0:
00102         return 0
00103     if ct <= -1.0:
00104         return math.pi
00105     try:
00106         return math.acos(ct)
00107     except:
00108         # probably won't get here, but we may as well give a diagnostic if we do
00109         print a, "\n", b,"\n", ct
00110         raise "Can't calculate Angle"
00111 
00112 def SumSq(seq):
00113 
00114     return sum(map(lambda x:x*x,seq))
00115 
00116     #rv = seq[0]**2
00117     #for i in range(1, len(seq)):
00118     #    rv += seq[i]**2
00119     #return rv
00120 
00121 
00122 def EucDist(a,b):
00123     " the Euclidean distance between a,b "
00124 
00125     moda = Mod(a)
00126     modb = Mod(b)
00127     cosab = DotProd(a,b)/(Mod(a) * Mod(b))
00128 
00129     return math.sqrt(abs(moda*moda + modb*modb - 2*moda*modb*cosab))
00130 
00131 
00132 def Sum(s):
00133 
00134     sys.stderr.write("Util.Sum ?? WTF - Deprecated")
00135     raise  "obsolete function"
00136     rv = 0*s[0]
00137 
00138     for i in s:
00139         rv+= i
00140     return rv
00141 
00142 def Deriv(x,y):
00143     """ pre: len(x) == len(y)==3, all x and y numeric
00144        post: Deriv(x,y) == slope at x[1],y[1], by three point linear derivation """
00145     return 0.5 * (
00146         ((y[1] - y[0]) / (x[1] - x[0])) +
00147         ((y[2] - y[1]) / (x[2] - x[1]))
00148     )
00149 
00150 
00151 
00152 def Fulcrum(Seq):
00153 
00154     rv = 0
00155     halflen = len(Seq)/2.0
00156 
00157 
00158     for idx in range(len(Seq)):
00159         rv = rv + (Seq[idx] * (idx-halflen))
00160 
00161     return rv
00162 
00163 
00164 def AbsFulcrum(Seq):
00165 
00166     rv = 0
00167     halflen = len(Seq)/2.0
00168 
00169 
00170     for idx in range(len(Seq)):
00171         rv = rv + (abs(Seq[idx]) * (idx-halflen))
00172 
00173     return rv
00174 
00175 
00176 def Diff(a,b):
00177     """ pre: len(a) == len(b)
00178                 a[0..i] - b[0..1] valid for i in range len(a)
00179         post: Diff(a,b)[0..1] = a[0..i]-b[0..i] """
00180 
00181     return map(lambda x,y: x-y, a,b)
00182 
00183 
00184 ##
00185 ## more generalised sequence functions
00186 ##
00187 
00188 
00189 def Round(seq,sf):
00190     """ pre: type(seq[0..n]) == FloatType
00191        post: seq[0..n] rounded to sf sig figs """
00192 
00193     for i in range(len(seq)):
00194         m,e = math.frexp(seq[i])
00195         seq[i] = math.ldexp(round(m,sf),e)
00196 
00197 def Flatten(seq, eta):
00198     for i in range(len(seq)):
00199         if abs(seq[i]) < eta:
00200             seq[i] = 0.0
00201 
00202 
00203 
00204 def Fill(seq, fun, args, kwargs):
00205     """ fill seq with fun(args,kwargs) """
00206     seq[:] = map(lambda x, a=args, k=kwargs, f=fun: f(*a,**k), seq)
00207 
00208 
00209 def Split(l,sep):
00210     """ split l in to list of lists about sep, list equivalent of str.split()"""
00211 
00212     rv = []
00213     l = l[:]
00214     l.reverse()
00215     while sep in l:
00216         idx = l.index(sep)
00217         slice = l[:idx]
00218         slice.reverse()
00219         rv.append(slice)
00220         del l[:idx+1]
00221     if len(l) >0:
00222         l.reverse()
00223         rv.append(l)
00224     rv.reverse()
00225     return rv
00226 
00227 def Diffs(a,b):
00228     """ pre: a[n]-b[n] valid for (n <= 0 < len(a)), len(a) == len(b)
00229        post: Diffs(a,b)[n] == a[n] - b[n]
00230     """
00231     return map(lambda x,y:x-y, a,b)
00232 
00233 def AllEq(seq, val):
00234     """
00235     pre : a[n] != val valid for (n <= 0 < len(a))
00236     post: AllEq(seq, val) => all elements in Seq are equal to val
00237     """
00238     for el in seq:
00239         if el != val:
00240             return 0
00241 
00242     return 1
00243 
00244 
00245 def AreEqual(s1, s2):
00246     lens1 = len(s1)
00247 
00248     if lens1 != len(s2):
00249         return 0
00250 
00251     for i in range(lens1):
00252         if s1[i] != s2[i]:
00253             return 0
00254 
00255     return 1
00256 
00257 
00258 def LimInSeq(seq, fun):
00259     """
00260     pre : fun extracts a value from seq w/o changing it
00261     post: LimInSeq(seq, fun) == index and value in seq found by fun(seq)
00262     """
00263     val = fun(seq)
00264     for idx in range(len(seq)):
00265         if seq[idx] == val:
00266             return [idx, val]
00267 
00268 
00269 def PosOfFirst(seq, fun):
00270     """pre: fun is a boolean function which can accept any element of seq as input
00271       post: returns(val,idx) where val and index are the first value and index where fun is true
00272                returns (None, -1) if no matches """
00273 
00274     for el in seq:
00275         if fun(el):
00276             return el, seq.index(el)
00277     return None,-1
00278 
00279 
00280 
00281 def PosOfValsInSeq(seq, val):
00282     """
00283     pre : all elements in seq can be compared with val
00284     post: PosOfValsInSeq == list of indices to elements in seq of value val
00285     """
00286     rv = []
00287     for idx in range(len(seq)):
00288         if seq[idx] == val:
00289             rv.append(idx)
00290 
00291     return rv
00292 
00293 
00294 def Abs(seq):
00295     return map(abs, seq)
00296 
00297 
00298 
00299 def PosOfAbsMin(seq):
00300     seqabs = Abs(seq)
00301     seqnz = []
00302     for el in seqabs:
00303         if el != 0:
00304             seqnz.append(abs(el))
00305 
00306     return seqabs.index(min(seqnz))
00307 
00308 def PosOfMinBeforeZero(seq):
00309     """pre: seq[0] != 0, seq[n]==0 in range(1, len(seq))
00310       post: returns index of  smalles element before the first 0 val element """
00311 
00312     rv = 0
00313     for i in range(1,len(seq)):
00314         if seq[i] == 0:
00315             return rv
00316         else:
00317             if seq[i] < seq[rv]:
00318                 rv = i
00319 
00320 
00321 def RemoveFrom(lis, targs):
00322     """pre : lis and targs are sequences
00323       post: no elements of targs in lis """
00324 
00325     for el in targs:
00326         try:
00327             del lis[lis.index(el)]
00328         except:
00329             pass
00330 
00331 
00332 def NoDups(lis):
00333     " returns copy of l with duplicate items removed"
00334 
00335     rv = []
00336     for el in lis:
00337         if el not in rv:
00338             rv.append(el)
00339 
00340     return rv
00341 
00342 def HasMatches(seq,fun):
00343     for el in seq:
00344         if fun(el):
00345             return 1
00346     return 0
00347 
00348 
00349 def IdxListOfMatches(seq, fun):
00350     """
00351     pre: all elements in seq can be passed to fun which returns a boolean
00352     post: returns a list of indices for which fun(seq[idx])==1 (true)
00353     """
00354     rv = []
00355     for idx in range(len(seq)):
00356         if fun(seq[idx]):
00357             rv.append(idx)
00358 
00359     return rv
00360 
00361 
00362 def NearestIdxVal(l,v):
00363     """ pre: l is ascending ordered numeric list, v is a value  comparable to elements of l
00364        post: returns (i,n) i the index of n in l which is closest in value to v """
00365 
00366     def nvi(l,v,idx):
00367         lenl = len(l)
00368         if lenl<=3:
00369             diffs = map(lambda x: abs(x-v), l)
00370             idx = diffs.index(min(diffs))
00371             return idx,l[idx]
00372 
00373         mid = lenl/2
00374 
00375         if v >= l[mid]:
00376             idx,nearest=nvi(l[mid:],v,idx)
00377             idx += mid
00378             return idx,nearest
00379         return nvi(l[:mid+1],v,idx)
00380 
00381     return nvi(l,v,-1)
00382 
00383 
00384 def IdxOfUnique(seq, fun):
00385     """
00386     pre: all elements in seq can be passed to fun which returns a boolean
00387     post: returns idx for which only fun(idx) is true, else returns None
00388     """
00389 
00390     match = IdxListOfMatches(seq, fun)
00391     if len(match) == 1:
00392         return match[0]
00393     return None
00394 
00395 def NZIdxs(seq,Abs=True):
00396     "list of indices to nonzero elements of seq, sorted by ascending (Abs => absolute) element value "
00397 
00398     idxs = []
00399     for i in range(len(seq)):
00400         if seq[i] !=0:
00401             idxs.append(i)
00402 
00403     s = Sorter.Sorter(idxs)
00404     if Abs:
00405         def fun(a):
00406             return abs(seq[a])
00407     else:
00408         def fun(a):
00409             return seq[a]
00410 
00411     return s.Sort(fun)
00412 
00413 def NumOfMatches(seq, fun):
00414      """
00415     pre: all elements in seq can be passed to fun which returns a boolean
00416     post: returns the number of elements for which fun(seq[idx])==1 (true)
00417     """
00418 
00419      return len(IdxListOfMatches(seq, fun))
00420 
00421 
00422 def AllMatch(seq, fun):
00423     """
00424     pre: all elements in seq can be passed to fun which returns a boolean
00425     post: AllMatch(seq, fun) => fun(el) == True for all el in seq
00426     """
00427 
00428     return NumOfMatches(seq, fun) == len(seq)
00429 
00430 
00431 
00432 
00433 def SwapItem(seq, a,b):
00434     tmp = seq[a]
00435     seq[a] = seq[b]
00436     seq[b] = tmp
00437 
00438 
00439 def Normalise(seq, nidx=-1, KeepSign=1):
00440 
00441     abseq = Abs(seq)
00442 
00443     if nidx < 0:
00444         nidx = abseq.index(max(abseq))
00445 
00446     val = seq[nidx] * 1# * 1 : force creation of new instance for val
00447     if KeepSign:
00448         val = abs(val)
00449 
00450     if val == 0:
00451         raise "Division by zero in Normalise"
00452 
00453     if val != 1:
00454         for i in range(len(seq)):
00455             seq[i] /= val
00456 
00457 
00458 def NormaliseRound(seq, nidx=-1, dec= 12, KeepSign=1):
00459 
00460     if nidx < 0:
00461         nidx = seq.index(max(seq))
00462 
00463     if seq[nidx] == 0:
00464         raise "Division by zero in Normalise"
00465     else: val = 1.0 / seq[nidx] # force creation of new instance for val
00466     if KeepSign:
00467         val = abs(val)
00468 
00469     i = 0
00470     while i < len(seq) :
00471         tmp = seq[i]
00472         if tmp != 0.0 : seq[i] = round (tmp * val, dec)
00473         else : seq[i] = 0.0
00474         i = i+1
00475 
00476 
00477 def Add(a,b):
00478 
00479     rv = a[:]
00480 
00481     for i in range(len(b)):
00482         rv[i] += b[i]
00483     return rv
00484 
00485 
00486 
00487 ##
00488 ##
00489 ##
00490 ##sequences of rationals
00491 ##
00492 
00493 
00494 def LargestDen(seq):
00495     """ pre: all items in Seq have GetDen() method, returning comparable +ve values
00496        post: LargestDen(seq) = largest denominator in seq """
00497 
00498     rv = seq[0].denom()
00499     for i in seq[1:]:
00500         d = i.denom()
00501         if d > rv:
00502             rv = d
00503 
00504     return rv *1
00505 
00506 
00507 def RandOrder(seq):
00508     """ pre: true
00509        post: return a list with items in random order wrt seq"""
00510 
00511 
00512     return random.sample(seq,len(seq))
00513 
00514 
00515 def Integise(seq):
00516     """ pre: LargestDen(seq) is defined
00517        post: seq' = Integise(seq)
00518              seq' items are integer, maintaining ratios of seq """
00519 
00520     den = LargestDen(seq)
00521 
00522     while den > 1:
00523         seq = map(lambda x: x* den,seq)
00524         den = LargestDen(seq)
00525 
00526     #for id in range(len(seq)):
00527      #   seq[id] = int(seq[id])
00528 
00529     return seq
00530 
00531 
00532 
00533 
00534 
00535 
00536 
00537 
00538 
00539 
00540 
00541 
00542 
00543 
00544 
00545 
00546 
00547 
00548 

Generated on Tue Sep 4 2012 15:38:02 for ScrumPy by  doxygen 1.7.1