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
00037
00038
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
00098
00099 ct = float(CosTheta(a,b))
00100
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
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
00117
00118
00119
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
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
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]
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
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
00527
00528
00529 return seq
00530
00531
00532
00533
00534
00535
00536
00537
00538
00539
00540
00541
00542
00543
00544
00545
00546
00547
00548