00001
00002
00003
00004 """
00005
00006 ScrumPy -- Metabolic Modelling with Python
00007
00008 Copyright Mark Poolman 1995 - 2002 Copy
00009
00010 This file is part of ScrumPy.
00011
00012 ScrumPy is free software; you can redistribute it and/or modify
00013 it under the terms of the GNU General Public License as published by
00014 the Free Software Foundation; either version 2 of the License, or
00015 (at your option) any later version.
00016
00017 ScrumPy is distributed in the hope that it will be useful,
00018 but WITHOUT ANY WARRANTY; without even the implied warranty of
00019 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00020 GNU General Public License for more details.
00021
00022 You should have received a copy of the GNU General Public License
00023 along with ScrumPy; if not, write to the Free Software
00024 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
00025
00026 """
00027 """
00028 M. Poolman 10/01/01
00029 Dynamic matrix class
00030 """
00031
00032
00033
00034
00035 import types,math,sys,exceptions
00036 Pi = math.pi
00037 PiOver2 = Pi/2
00038
00039
00040 import Sci,Seq,Sorter,Types,Format, LaTeX, Tree
00041
00042 try:
00043 from ScrumPy import wxGUI as GUI
00044 HaveGUI = True
00045 except:
00046 HaveGUI = False
00047
00048 DefaultConv = Types.ArbRat
00049 DynMatrixError = "Dynamic Matrix Error"
00050 Tol = 1e-9
00051
00052 def DepWarn(Str):
00053 sys.stderr.write( "!! Deprecated " + Str+" !!" )
00054
00055
00056 def GetIdent(n, Conv=DefaultConv, mul=1):
00057 rv = matrix(nrow=n,ncol=n,Conv=Conv)
00058 rv.MakeIdent(mul)
00059 return rv
00060
00061 def FromVec(vec, Conv=None):
00062
00063 if not Conv:
00064 vtype = type(vec[0]).__name__
00065 try:
00066 Conv = {
00067 'float' : float,
00068 'int' : int,
00069 'long int' : long
00070 }[vtype]
00071 except:
00072 Conv = DefaultConv
00073
00074 size = len(vec)
00075 rv = matrix(size,size,Conv)
00076 for n in range(size):
00077 rv[n,n] = Conv(vec[n])
00078
00079 return rv
00080
00081
00082
00083
00084
00085
00086 class matrix:
00087
00088 def __init__(self, nrow=0, ncol=0,rnames=[],cnames=[], Conv=DefaultConv, FromMtx=None):
00089
00090 self.Conv = Conv
00091 self.rnames = []
00092 self.cnames = []
00093 self.rows = []
00094
00095
00096 if FromMtx != None:
00097 try:
00098 self.FromDynMatrix(FromMtx)
00099 return
00100 except:
00101 try:
00102 self.FromNumPyMtx(FromMtx)
00103 except:
00104 self.FromList(FromMtx)
00105
00106 if len(rnames) != 0 and len(rnames) != len(self):
00107 raise exceptions.IndexError, "len(rnames) != len(FromMtx)"
00108 else:
00109 self.rnames = rnames[:]
00110
00111 if len(cnames) != 0 and len(cnames) != len(self.rows[0]):
00112 raise exceptions.IndexError, "len(cnames) != len(FromMtx[0])"
00113 else:
00114 self.cnames = cnames[:]
00115
00116 return
00117
00118 if len(rnames) != 0:
00119 self.rnames = rnames[:]
00120 else:
00121 for r in range(nrow):
00122 self.rnames.append("row"+str(r))
00123
00124 if len(cnames) != 0:
00125 self.cnames = cnames[:]
00126 else:
00127 for c in range(ncol):
00128 self.cnames.append("col"+str(c))
00129
00130 self.MakeZero()
00131
00132
00133
00134 def _getrc(self,rc):
00135 if type(rc) == types.TupleType:
00136 r = rc[0]
00137 c = rc[1]
00138 else:
00139 r = rc
00140 c = None
00141
00142 return r,c
00143
00144
00145 def __len__(self):
00146 return len(self.rows)
00147
00148
00149 def __getitem__(self,rc):
00150 r,c = self._getrc(rc)
00151
00152 try:
00153 row = self.rows[r]
00154 except:
00155 try:
00156 row = self.rows[self.rnames.index(r)]
00157 except:
00158 raise IndexError, r
00159
00160 if c==None:
00161 return row
00162
00163 try:
00164 return row[c]
00165 except:
00166 try:
00167 return row[self.cnames.index(c)]
00168 except:
00169 raise IndexError, c
00170
00171
00172 def __setitem__(self,rc, val):
00173
00174 r,c = self._getrc(rc)
00175
00176 if type(r) == types.StringType:
00177 try:
00178 r = self.rnames.index(r)
00179 except:
00180 raise IndexError, r
00181
00182 if c==None:
00183 self.rows[r] = map(self.Conv, val)
00184 else:
00185 if type(c) == types.StringType:
00186 try:
00187 c = self.cnames.index(c)
00188 except:
00189 raise IndexError, c
00190
00191 self.rows[r][c] = self.Conv(val)
00192
00193
00194 def __getslice__(self,lo,hi):
00195 rv = matrix(Conv=self.Conv)
00196 rv.rows = self.rows[lo:hi]
00197 rv.rnames = self.rnames[lo:hi]
00198 rv.cnames = self.cnames[:]
00199 return rv
00200
00201
00202 def __str__(self):
00203 retstr = " "
00204 for cname in self.cnames:
00205 retstr += str(cname) + " "
00206 retstr += "\n"
00207 for r in range(len(self.rnames)):
00208 retstr += "\n " + str(self.rnames[r])
00209 for el in self[r]:
00210 retstr +=" "+str(el)+ " "
00211
00212 return retstr
00213
00214
00215 def __repr__(self):
00216 try:
00217 return str(self)
00218 except:
00219 return "can't generate string rep'n for this matrix"
00220
00221 def Show(self):
00222 if HaveGUI:
00223 GUI.Mtx.SimpleView(self).Show()
00224 else:
00225 print self
00226
00227
00228 def index(self,row):
00229
00230 for i in range(len(self.rows)):
00231 if Seq.AreEqual(self.rows[i], row):
00232 return i
00233
00234 raise "ValueError", "matrix.index(row): row not in matrix"
00235
00236
00237
00238 def DeQuote(self):
00239 """ replace double quoted strings in row and col names with unquoted
00240 (preserves mappings in self.QuoteD) """
00241
00242 self.QuoteD = {}
00243 rnamesd = map(Format.StripQuote,self.rnames)
00244 cnamesd = map(Format.StripQuote,self.cnames)
00245
00246 for n in range(len(rnamesd)):
00247 self.QuoteD[rnamesd[n]] = self.rnames[n]
00248 self.QuoteD[self.rnames[n]] = rnamesd[n]
00249
00250 for n in range(len(cnamesd)):
00251 self.QuoteD[cnamesd[n]] = self.cnames[n]
00252 self.QuoteD[self.cnames[n]] = cnamesd[n]
00253
00254 self.rnames = rnamesd
00255 self.cnames = cnamesd
00256
00257
00258
00259 def IsDeQuoted(self):
00260 """ bool: self is DeQuote()ed """
00261
00262 return hasattr(self, "QuoteD")
00263
00264
00265 def ReQuote(self):
00266 """ undo DeQuote """
00267
00268 if self.IsDeQuoted():
00269
00270 for n in range(len(self.rnames)):
00271 name = self.rnames[n]
00272 if self.QuoteD.has_key(name):
00273 self.rnames[n] = self.QuoteD[name]
00274
00275 for n in range(len(self.cnames)):
00276 name = self.cnames[n]
00277 if self.QuoteD.has_key(name):
00278 self.cnames[n] = self.QuoteD[name]
00279
00280 del self.QuoteD
00281
00282 def ResetRowNames(self):
00283 """ pre: True
00284 post: self.rnames==["Row_0".."Row_n"] """
00285
00286 self.rnames = map(lambda n: "Row_"+str(n),range(len(self)))
00287
00288
00289 def ChangeType(self, Conv):
00290 self.Conv = Conv
00291 for r in self.rnames:
00292 self[r] = self.MakeSeqSelfType(self[r])
00293
00294 def MakeSeqSelfType(self, seq):
00295
00296 return map(self.Conv, seq)
00297
00298
00299 def MakeZero(self):
00300 Conv = self.Conv
00301 self.rows = []
00302 for n in range(len(self.rnames)):
00303 self.rows.append(map(self.Conv, [0]*len(self.cnames)))
00304
00305
00306 def Clear(self):
00307 "erase all row and col data"
00308
00309 self.cnames, self.rnames, self.rows = [],[],[]
00310
00311
00312
00313 def Copy(self, Conv=None,tx=False):
00314 """ pre: True
00315 post: Copy of self, with Element type Conv (self.Conv if Conv==None)
00316 tx => Copy is transposed """
00317
00318 if Conv==None:
00319 Conv=self.Conv
00320
00321
00322 rv = self.__class__(Conv=Conv)
00323 rv.cnames = self.cnames[:]
00324 rv.rnames = self.rnames[:]
00325 if Conv==self.Conv:
00326 for row in self.rows:
00327 rv.rows.append(row[:])
00328 else:
00329 for row in self.rows:
00330 rv.rows.append(map(Conv, row))
00331
00332 if tx:
00333 rv.Transpose()
00334
00335 return rv
00336
00337 def ReadFile(self, FileName):
00338 """ pre: (FileName != None) -> (File conforms to FileSpec)
00339 post: contents of self are those of FileName """
00340
00341 f = open(FileName, "r")
00342 self.Clear()
00343 Titles = f.readline()
00344 for t in Titles.split():
00345 self.NewCol(name=t)
00346
00347 for row in f.readlines():
00348 row = row.split()
00349 try:
00350 self.NewRow(row[1:], row[0])
00351 except:
00352 print "problem with row", row, "ignoring it"
00353
00354
00355
00356 def WriteFile(self,File,WithHash=False,WithRNames=True,Delim="\t"):
00357 """ pre: (ColOrder == None) || (complete and exclusive list of column headings of self)
00358 post: FileName contains FileSpec representation of self
00359 (ColOrder != None) => Columns in ColOrder
00360 ELSE Exception """
00361
00362 if type(File) == types.StringType:
00363 f = open(File, "w")
00364 else:
00365 f = File
00366
00367 first = {True:["#"],
00368 False:[""]
00369 }[WithHash]
00370
00371 print >>f, Delim.join(first+self.cnames)
00372
00373 if WithRNames:
00374 for n in range(len(self)):
00375 print >>f, Delim.join([self.rnames[n]]+map(str,self.rows[n]))
00376 else:
00377 for n in len(self):
00378 print >>f, Delim.join(map(str,self.rows[n]))
00379
00380
00381
00382 def ToFile(self, fname):
00383 f = open(fname,"w")
00384 f.write(str(self))
00385
00386 def ToList(self):
00387 """ return a list of lists of tuples. Each list corresponds to a row,
00388 each tuple is (name,value) of the column and value of the non zero elements within that row """
00389
00390 rv = []
00391 for row in self:
00392 curlist = []
00393 curidxs = Seq.IdxListOfMatches(row, lambda x: x!=0)
00394 for idx in curidxs:
00395 curlist.append((self.cnames[idx], row[idx]))
00396 rv.append(curlist)
00397
00398 return rv
00399
00400
00401
00402 def ToDict(self):
00403 """ return dictionary in which keys are row names and values lists of tuples (colnames, elval) for elval != 0 """
00404
00405 l = self.ToList()
00406 rv = {}
00407 for idx in range(len(self)):
00408 rv[self.rnames[idx]] = l[idx]
00409 return rv
00410
00411
00412 def UpTri(self):
00413 """ return upper triangular elements of self as a single list """
00414
00415 rv = []
00416 for i in range(len(self)):
00417 rv.extend(self[i][i+1:])
00418 return rv
00419
00420
00421 def FromDicDic(self,d):
00422 """ pre: d is a dictionary of rows,
00423 in which each value is a dictionary mapping col names to values
00424 post: self'[r,c] == d[r,c] for non-zero values, all other values in self are zero """
00425
00426 self.rnames = []
00427 self.rows = []
00428 self.cnames = []
00429
00430 for kr in d.keys():
00431 self.NewRow(name=kr)
00432 rd = d[kr]
00433 for kc in rd.keys():
00434 if not kc in self.cnames:
00435 self.NewCol(name=kc)
00436
00437 self[kr,kc] = d[kr][kc]
00438
00439
00440
00441
00442 def ToColList(self):
00443 """ as ToList, but returns list of columns """
00444
00445 rv = []
00446 for cn in self.cnames:
00447 col = self.GetCol(cn)
00448 curidxs = Seq.IdxListOfMatches(col, lambda x: x!=0)
00449 curlist = []
00450 for idx in curidxs:
00451 curlist.append((self.rnames[idx],col[idx]))
00452 rv.append(curlist)
00453 return rv
00454
00455
00456 def ToColDict(self):
00457 """ return dictionary in which keys are col names and values lists of tuples (rownames, elval) for elval != 0 """
00458
00459 l = self.ToColList()
00460 rv = {}
00461 for idx in range(len(self.cnames)):
00462 rv[self.cnames[idx]] = l[idx]
00463 return rv
00464
00465
00466
00467
00468
00469 def ToSciMtx(self,Conv=None):
00470
00471 sys.stderr.write("ToSciMtx: why do we need this ?\n")
00472
00473 if not Conv:
00474 return Sci.matrix(self.rows)
00475
00476 return Sci.matrix(self.Copy(Conv).rows)
00477
00478
00479 def FromNumPyMtx(self, mtx, Conv=None):
00480 sys.stderr.write("FromNumPyMtx: use from SciMtx ?\n")
00481 return self.FromSciMtx(mtx, Conv)
00482
00483 def FromSciMtx(self, mtx, Conv=None):
00484
00485 sys.stderr.write("FromSciMtx: why do we need this ?\n")
00486
00487 nrow,ncol = mtx.shape
00488 if Conv!=None:
00489 self.Conv = Conv
00490 self.cnames = map(lambda x: "col"+str(x),range(ncol))
00491 self.rnames = map(lambda x: "row"+str(x),range(nrow))
00492
00493 self.rows = mtx.tolist()
00494
00495
00496
00497
00498
00499
00500 def ToOctaveMtx(self, name, Conv = float):
00501 """ pre: True
00502 post: returns a string that when printed assigns the octave matrix, name, to current contents
00503 using Conv as a type converter
00504 """
00505 rv = name + "= ["
00506 for row in self.rows:
00507 rv += "\n ["
00508 for el in row[:-1]:
00509 rv += str(Conv(el)) + ", "
00510 rv += str(Conv(row[-1])) + "]"
00511 rv += "\n]\n"
00512
00513 return rv
00514
00515
00516 def ToCMtx(self, name, Conv = float):
00517 """ pre: True
00518 post: returns a string that when printed declares a C matrix, name, of current contents
00519 type will need editing for non-default Conv, which as ever, is the type converter
00520 """
00521
00522 c_nrows = name + "_nRows"
00523 c_ncols = name + "_nCols"
00524 strlenrow = str(len(self.rows))
00525 strlencol = str(len(self.rows[0]))
00526 rv = " const int "
00527
00528 rv += c_nrows + " = " + strlenrow + ", " + c_ncols + " = " + strlencol + ";"
00529
00530 rv += "\n double " + name + "[" + c_nrows + "]" + "[" + c_ncols + "] = {\n"
00531 for row in self.rows[:-1]:
00532 rv += "\t{"
00533 for el in row[:-1]:
00534 rv += str(Conv(el)) + ", "
00535 rv += str(Conv(row[-1])) + "},\n"
00536
00537 row = self.rows[-1]
00538 rv += "\t{"
00539 for el in row[:-1]:
00540 rv += str(Conv(el)) + ", "
00541 rv += str(Conv(row[-1])) + "} \n } ;"
00542
00543 return rv
00544
00545
00546 def ToStr(self):
00547 """ return an easy-to-interpret string repn of self, reflecting information content
00548 rather than matrix notation -NOT- the same as __str__ or __repr__"""
00549
00550 rv = ""
00551 list = self.ToList()
00552 for row in list:
00553 rowstr = " "
00554 for el in row:
00555 elstr = str(el[1]) + " " + el[0]
00556 if el[1] < 0:
00557 rowstr = elstr + " " + rowstr
00558 else:
00559 rowstr += " " + elstr
00560 rv += rowstr + "\n"
00561 return rv
00562
00563
00564 def ToTexTab(self, RowLabels=True):
00565 "return a string representation in Latex tabular form"
00566
00567 if RowLabels:
00568 colheads = ["Row"] + self.cnames
00569 else:
00570 colheads = self.cnames[:]
00571
00572 ncols = len(colheads)
00573 rv = LaTeX.Tabular(ncols)
00574 rv.AddRow(colheads)
00575
00576 if self.Conv == float:
00577 TeXConv = Float2Tex
00578 else:
00579 TeXConv = str
00580
00581 if RowLabels:
00582 for r in self.rnames:
00583 rv.AddRow([r]+map(TeXConv,self[r]))
00584 else:
00585 for r in self.rows:
00586 rv.AddRow(map(TeXConv,r))
00587
00588 return str(rv)
00589
00590
00591
00592 def FromDynMatrix(self,other):
00593
00594 self.Conv = other.Conv
00595 self.cnames = other.cnames[:]
00596 self.rnames = other.rnames[:]
00597 self.rows = []
00598 for r in other.rows:
00599 self.rows.append(r[:])
00600
00601
00602
00603
00604 def InitRows(self, rnames):
00605 """ pre: rnames is a list of strings
00606 post: self is an empty (r x 0) matrix, using rnames as row names """
00607 self.rnames = rnames[:]
00608 self.rows = []
00609
00610
00611
00612 def Transpose(self):
00613 NewRows = []
00614 for c in range(len(self.cnames)):
00615 NewRows.append(self.GetCol(c))
00616 self.rows = NewRows
00617
00618 tmp = self.cnames
00619 self.cnames = self.rnames
00620 self.rnames = tmp
00621
00622
00623 def RowReorder(self, NewOrder):
00624 tmp = matrix(Conv=self.Conv,ncol = len(self.cnames))
00625
00626 for n in NewOrder:
00627 if type(n)==int:
00628 name = self.rnames[n]
00629 else:
00630 name = n
00631 tmp.NewRow(self[n], name)
00632
00633 self.rows = tmp.rows
00634 self.rnames = tmp.rnames
00635
00636
00637 def ColReorder(self, NewOrder):
00638 self.Transpose()
00639 self.RowReorder(NewOrder)
00640 self.Transpose()
00641
00642 def RandRowOrder(self):
00643 self.RowReorder(Seq.RandOrder(self.rnames))
00644
00645 def RandColOrder(self):
00646 self.ColReorder(Seq.RandOrder(self.cnames))
00647
00648 def RandOrder(self):
00649 self.RandRowOrder()
00650 self.RandColOrder()
00651
00652
00653 def SortBy(self,cname,Descend=False):
00654
00655 origc = self.GetCol(cname)
00656 sortc = sorted(origc,reverse=Descend)
00657 mark = min(origc)-1
00658
00659 neworder=[]
00660 for x in sortc:
00661 idx = origc.index(x)
00662 neworder.append(idx)
00663 origc[idx] = mark
00664
00665 self.RowReorder(neworder)
00666
00667
00668
00669
00670 def RowNamesFromIdxs(self,idxs=[]):
00671 rv = []
00672 for i in idxs:
00673 rv.append(self.rnames[i])
00674 return rv
00675
00676
00677 def ColNamesFromIdxs(self,idxs=[]):
00678 rv = []
00679 for i in idxs:
00680 rv.append(self.cnames[i])
00681 return rv
00682
00683
00684 def RowNZIdxs(self,r,Abs=True):
00685 "list of indices to nonzero elements of r, sorted by ascending (Abs => absolute) element value "
00686
00687 return Seq.NZIdxs(self[r],Abs)
00688
00689
00690 def ColNZIdxs(self,c,Abs=True):
00691 "list of indices to nonzero elements of c, sorted by ascending (Abs => absolute) element value "
00692
00693 return Seq.NZIdxs(self.GetCol(c),Abs)
00694
00695
00696
00697 def Sort(self, fun, **fundat):
00698 """ sort rows in ascending order of fun(row,mtx,fundat) where:
00699 fun returns a numeric type
00700 row is an index into mtx
00701 mtx is self
00702 fundat is optional named args"""
00703
00704 s = Sorter.Sorter()
00705 for r in self.rnames:
00706 s.append(r)
00707 self.RowReorder(s.Sort(fun, mtx=self, **fundat))
00708
00709
00710
00711
00712 def MakeIdent(self, mul):
00713
00714 r = 0
00715 for row in self.rows:
00716 row = map(lambda x: 0, row)
00717 row[r] = mul
00718 self.rows[r] = self.MakeSeqSelfType(row)
00719 r = r + 1
00720
00721 def SetEl(self, row, col, val):
00722
00723 DepWarn("DynMatrix.matrix.SetEl()")
00724
00725 if type(row) != types.IntType:
00726 row = self.rnames.index(row)
00727 if type(col) != types.IntType:
00728 col = self.cnames.index(col)
00729
00730 self.rows[row][col] = self.Conv(val)
00731
00732
00733 def NewEl(self, rname, cname, val):
00734 """ pre: true
00735 post: self[rname,cname] == val
00736 (rows and cols created if needed)
00737 """
00738
00739 if not rname in self.rnames:
00740 self.NewRow(name=rname)
00741 if not cname in self.cnames:
00742 self.NewCol(name=cname)
00743 self[rname,cname] = self.Conv(val)
00744
00745
00746
00747 def NewCol(self, col=None, name=None):
00748
00749 if name == None:
00750 name = "col" + str(len(self.cnames)+1)
00751
00752 self.cnames.append(name)
00753
00754 if col == None:
00755 col = [0] *len(self.rnames)
00756
00757 if len(self.rnames)==0:
00758 for i in range(len(col)):
00759 self.rows.append([self.Conv(col[i])])
00760 self.rnames.append("row_" + str(i))
00761
00762 elif len(col) != len(self.rnames):
00763 raise exceptions.IndexError, "wrong length for new col"
00764
00765 else:
00766 for i in range(len(self.rows)):
00767 self[i].append(self.Conv(col[i]))
00768
00769
00770
00771
00772 def DelCol(self, c=0):
00773
00774 if type(c) == types.StringType:
00775 c = self.cnames.index(c)
00776
00777 del self.cnames[c]
00778 for row in self.rows:
00779 del row[c]
00780
00781
00782 def GetCol(self, c):
00783 if type(c) == types.StringType:
00784 c = self.cnames.index(c)
00785 col = []
00786 for row in self.rows:
00787 col.append(row[c])
00788
00789 return col
00790
00791
00792 def SetCol(self, c, vals):
00793 if type(c) == types.StringType:
00794 c = self.cnames.index(c)
00795 vals = self.MakeSeqSelfType(vals)
00796 r = 0
00797 for row in self.rows:
00798 row[c] = vals[r]
00799 r = r + 1
00800
00801
00802 def AddCol(self, c, vals=None, k = None):
00803
00804 if vals !=None:
00805 self.SetCol(c, map(lambda x,y: x+y, self.GetCol(c), vals))
00806 else:
00807 self.SetCol(c, map(lambda x: x+k, self.GetCol(c)))
00808
00809 def SubCol(self, c, vals=None, k=None):
00810
00811 if vals != None:
00812 self.SetCol(c, map(lambda x,y: x-y, self.GetCol(c), vals))
00813 else:
00814 self.SetCol(c, map(lambda x: x-k, self.GetCol(c)))
00815
00816 def MulCol(self, c, vals=None, k=None):
00817
00818 if vals!=None:
00819 self.SetCol(c, map(lambda x,y: x*y, self.GetCol(c), vals))
00820 else:
00821 self.SetCol(c, map(lambda x: x*k, self.GetCol(c)))
00822
00823 def DivCol(self, c, vals=None, k=None):
00824
00825 if vals!=None:
00826 self.SetCol(c, map(lambda x,y: x/y, self.GetCol(c), vals))
00827 else:
00828 self.SetCol(c, map(lambda x: x/k, self.GetCol(c)))
00829
00830
00831 def FunCol(self, c, Fun):
00832 self.SetCol(c, map(Fun, self.GetCol(c)))
00833
00834 def MaxInCol(self, c):
00835 return max(self.GetCol(c))
00836
00837 def MinInCol(self, c):
00838 return min(self.GetCol(c))
00839
00840 def PosOfValsInCol(self, c, val):
00841 return Seq.PosOfValsInSeq(self.GetCol(c), val)
00842
00843 def NZeroesInCol(self,c):
00844 return len(self.PosOfValsInCol(c,0))
00845
00846
00847 def NonZeroesInCols(self):
00848 """ returns a list vector, one element per col,
00849 each element being the number of non-zero elements in the coresponding col """
00850
00851 rv = []
00852 for c in range(len(self.cnames)):
00853 rv.append(self.NZeroesInCol(c))
00854
00855 return rv
00856
00857 def NamesNonZeroesInCols(self):
00858 """ return a list (one el per col) lists of row names that have no zero element in corresponding col """
00859
00860 rows = self.rows
00861 rnames = self.rnames
00862 rv = []
00863
00864 for c in range(len(self.cnames)):
00865 res = []
00866 for r in range(len(self)):
00867 if rows[r][c] != 0:
00868 res.append(rnames[r])
00869 rv.append(res)
00870 return rv
00871
00872
00873 def MaxInRange(self,names):
00874 """ pre: self.rnames == self.cnames, Set.IsSubset(names, self.rnames)
00875 post: greatest value of self[names[x],names[y]] """
00876
00877 rv = 0.0
00878 for n1 in names:
00879 for n2 in names[names.index(n1):]:
00880 if self[n1,n2] > rv:
00881 rv = self[n1,n2]
00882
00883 return rv
00884
00885
00886
00887
00888 def SwapCol(self, c1, c2):
00889
00890 if type(c1).__name__ == 'string':
00891 c1 = self.cnames.index(c1)
00892
00893 if type(c2).__name__ == 'string':
00894 c2 = self.cnames.index(c2)
00895
00896 tmp = self.GetCol(c1)
00897 self.SetCol(c1, self.GetCol(c2))
00898 self.SetCol(c2, tmp)
00899
00900 tmp = self.cnames[c1]
00901 self.cnames[c1] = self.cnames[c2]
00902 self.cnames[c2] = tmp
00903
00904
00905 def AugCol(self, m):
00906 for c in range(len(m.cnames)):
00907 self.NewCol(m.GetCol(c),m.cnames[c])
00908
00909 def MakeLastCol(self, c):
00910 if type(c) == types.StringType:
00911 cname = c
00912 c = self.cnames.index(c)
00913 else:
00914 cname = self.cnames[c]
00915
00916 col = self.GetCol(c)
00917 self.DelCol(c)
00918 self.NewCol(col, cname)
00919
00920
00921 def MakeFirstCol(self, c):
00922 if type(c) == types.StringType:
00923 cname = c
00924 c = self.cnames.index(c)
00925 else:
00926 cname = self.cnames[c]
00927
00928 col = self.GetCol(c)
00929 self.DelCol(c)
00930 self.cnames.insert(0,cname)
00931 for idx in range(len(col)):
00932 self.rows[idx].insert(0,col[idx])
00933
00934
00935
00936
00937
00938 def NewRow(self, r=None, name=None):
00939
00940 if name == None:
00941 name = "row" + str(len(self.rows)+1)
00942
00943 self.rnames.append(name)
00944
00945 if r == None:
00946 r = [0] *len(self.cnames)
00947 elif len(r) != len(self.cnames):
00948 raise exceptions.IndexError, "wrong length for new row"
00949
00950 self.rows.append(self.MakeSeqSelfType(r))
00951
00952
00953 def DelRow(self, r):
00954 if type(r) == types.StringType:
00955 r = self.rnames.index(r)
00956
00957 del self.rows[r]
00958 del self.rnames[r]
00959
00960
00961 def GetRow(self, r):
00962 if type(r) == types.StringType:
00963 r = self.rnames.index(r)
00964 return(self.rows[r][:])
00965
00966
00967 def SetRow(self, r, vals):
00968 if type(r) == types.StringType:
00969 r = self.rnames.index(r)
00970 self.rows[r] = self.MakeSeqSelfType(vals)
00971
00972
00973 def AddRow(self, r, vals=None, k=None):
00974 if vals and k:
00975 self[r] = map(lambda x,y: x+y, self[r], map(lambda x,mul=k: x*mul,vals))
00976 elif vals:
00977 self[r] = map(lambda x,y: x+y, self[r], vals)
00978 else:
00979 self[r] = map(lambda x,add=k: x+add,vals)
00980
00981
00982 def InsertRow(self, pos, Val=0, Name=None):
00983
00984 r = map(lambda x,v=Val: v, range(0,len(self.cnames)))
00985 self.rows.insert(pos, self.MakeSeqSelfType(r))
00986 if Name:
00987 self.rnames.insert(pos,Name)
00988
00989
00990 def SubRow(self, r, vals):
00991 DepWarn("SubRow")
00992 self.rows[r] = map(lambda x,y: x-y, self.rows[r], vals)
00993
00994
00995 def MulRow(self, r, vals=None, k=None):
00996 if vals and k:
00997 self[r] = map(lambda x,y: x*y, self[r], map(lambda x,mul=k: x*mul,vals))
00998 elif vals:
00999 self[r] = map(lambda x,y: x*y, self[r], vals)
01000 else:
01001 self[r] = map(lambda x,mul=k: x*mul,self[r])
01002
01003
01004 def DivRow(self, r, vals=None, k=None):
01005 if type(r) != types.IntType:
01006 r = self.rnames.index(r)
01007 if vals and k:
01008 self.rows[r] = map(lambda x,y: x/y, self.rows[r], map(lambda x,mul=k: x*mul,vals))
01009 elif vals:
01010 self.rows[r] = map(lambda x,y: x/y, self.rows[r], vals)
01011 else:
01012 self.rows[r] = map(lambda x,den=k: x/den,self.rows[r])
01013
01014
01015 def AddAndRemoveRows(self, rows):
01016 """ pre: rows is subset of self.rnames
01017 post: self'[rows[0]] """
01018
01019 r0 = rows[0]
01020 for r in rows[1:]:
01021 self.AddRow(r0,self[r])
01022 for r in rows[1:]:
01023 self.DelRow(r)
01024
01025 def FunRow(self, r, fun):
01026 self.rows[r] = map(fun, self.rows[r])
01027
01028 def MaxInRow(self, r):
01029 return max(self[r])
01030
01031 def MinInRow(self, c):
01032 return min(self[r])
01033
01034 def PosOfValsInRow(self, r, val):
01035 return Seq.PosOfValsInSeq(self.rows[r], val)
01036
01037
01038
01039 def FunAllRows(self, fun):
01040 for r in range(len(self.rows)):
01041 self.rows[r] = fun(self.rows[r])
01042
01043 def DupRows(self):
01044 """ pre: True
01045 post: self.DupRows() a (possibly empty) list of lists of names of rows with identical elements
01046 """
01047
01048 rv = []
01049 rows = self.rows
01050 rnames = self.rnames
01051 ridxs = range(len(self.rows))
01052 while len(ridxs)>0:
01053 r = ridxs[0]
01054 duplist = [r]
01055 for r2 in ridxs[1:]:
01056 if Seq.AreEqual(rows[r], rows[r2]):
01057 duplist.append(r2)
01058
01059 for d in duplist:
01060 del ridxs[ridxs.index(d)]
01061 if len(duplist)> 1:
01062 rv.append(map(lambda x,rn = self.rnames: rn[x],duplist))
01063
01064 return rv
01065
01066
01067
01068 def DelDupRows(self):
01069 """pre: True
01070 post: len(self.DupRows())==0
01071 """
01072
01073 for dups in self.DupRows():
01074 for d in dups[1:]:
01075 self.DelRow(d)
01076
01077
01078 def MaxCountRow(self, val):
01079 """ return the row name with the greatest number of elements of value val
01080 in the event of a tie the first matching row name is returned
01081 if no rows contain val the first row name is returned """
01082
01083 rv = self.rnames[0]
01084 best = 0
01085 for r in self.rnames:
01086 hits = self[r].count(val)
01087 if hits > best:
01088 best = hits
01089 rv = r
01090 return rv
01091
01092
01093
01094 def RowsWithSingCols(self):
01095 "return list of idxs of rows with at least one non-zero element unique to the column"
01096 rv = []
01097 for c in self.cnames:
01098 col = self.GetCol(c)
01099 idx = Seq.IdxOfUnique(col, lambda x: x!=0)
01100 if idx != None and not idx in rv:
01101 rv.append(idx)
01102 return rv
01103
01104
01105 def SwapRow(self, r1, r2):
01106
01107 if type(r1).__name__ == 'string':
01108 r1 = self.rnames.index(r1)
01109
01110 if type(r2).__name__ == 'string':
01111 r2 = self.rnames.index(r2)
01112
01113 tmp = self.rows[r1]
01114 self.rows[r1] = self.rows[r2]
01115 self.rows[r2] = tmp
01116
01117 tmp = self.rnames[r1]
01118 self.rnames[r1] = self.rnames[r2]
01119 self.rnames[r2] = tmp
01120
01121
01122 def MakeFirstRow(self, r):
01123
01124 row = self[r]
01125 if type(r)==types.StringType:
01126 name = r
01127 else:
01128 name = self.rnames[r]
01129
01130 self.DelRow(r)
01131 self.rows.insert(0,row)
01132 self.rnames.insert(0,name)
01133
01134
01135 def MakeLastRow(self, r):
01136
01137 row = self[r]
01138 if type(r)==types.StringType:
01139 name = r
01140 else:
01141 name = self.rnames[r]
01142
01143 self.DelRow(r)
01144 self.rows.append(row)
01145 self.rnames.append(name)
01146
01147
01148 def AugRow(self, m):
01149 for r in range(len(m.rnames)):
01150 self.NewRow(m.rows[r], m.rnames[r] )
01151
01152
01153 def NonZeroR(self, seed=[], thresh=1e-6):
01154 """pre: mtx has no empty rows, and numrical elements
01155 post: return a seed + list of list of rownames for which r(a,b)> thresh
01156 where r is sample correlaation coeff,
01157 ** self is empty -DESTRUCTIVE ! ** """
01158
01159
01160 lenself = len(self)
01161
01162 if lenself==0:
01163 return seed
01164 elif lenself==1:
01165 return seed+self.rnames[:]
01166
01167 FirstRow = self.rnames[0]
01168 rv = [FirstRow]
01169 for ThisRow in self.rnames[1:]:
01170 if abs(Seq.CosTheta(self[FirstRow],self[ThisRow])) > thresh:
01171 rv.append(ThisRow)
01172 self.DelRow(ThisRow)
01173 self.DelRow(FirstRow)
01174 return self.NonZeroR(seed+[rv], thresh)
01175
01176
01177
01178 def RowDiffMtxFull(self, fun= None, Conv=float):
01179
01180 rv = matrix(rnames=self.rnames, cnames=self.rnames, Conv=Conv)
01181 lenself = len(self)
01182 for r1 in range(lenself):
01183 for r2 in range(lenself):
01184 rv[r1,r2] = fun(self[r1],self[r2])
01185
01186 return rv
01187
01188
01189
01190
01191 def RowDiffMtx(self, fun=None,Conv=float):
01192 """ pre: fun(self[r1],self[r2]) for 0 <= r1,r2 < len(self) returns type Conv
01193 fun(r1,r2) == fun(r2,r1)
01194 post: returns M such that M[r1,r2] = fun(self[r1],self[r2])
01195 """
01196
01197 if fun == None:
01198 fun = lambda x,y: abs(Sci.CosTheta(x,y))
01199
01200 rv = matrix(rnames=self.rnames, cnames=self.rnames, Conv=Conv)
01201 lenself = len(self)
01202 for r1 in range(lenself):
01203 for r2 in range(r1, lenself):
01204 rv.rows[r1][r2] = rv.rows[r2][r1] = fun(self[r1],self[r2])
01205
01206 return rv
01207
01208
01209 def RowDiffMtx2(self,fun):
01210 """ as above, but fun takes self, r1, and r2 as args, and no type conversion is done """
01211
01212 rv = matrix(rnames=self.rnames, cnames=self.rnames, Conv=self.Conv)
01213 lenself = len(self)
01214 for r1 in range(lenself):
01215 for r2 in range(r1+1, lenself):
01216 rv[r1,r2] = rv[r2,r1] = fun(self,r1,r2)
01217
01218 return rv
01219
01220
01221 def ToNJTree(self):
01222 """pre: self is square difference matrix
01223 post: A string representing the NJ tree, based on row differences, as calculated by fun """
01224
01225 def FindNearestRows(mtx):
01226
01227 rv = mtx.rnames[0] ,mtx.cnames[1],mtx[0,1]
01228 if len(mtx) == 2:
01229 return rv
01230
01231 for r in range(len(mtx)):
01232 for c in range(r):
01233 el = mtx[r,c]
01234 if el == 0:
01235 return mtx.rnames[r],mtx.cnames[c],0
01236 else:
01237 if el < rv[2]:
01238 rv = mtx.rnames[r],mtx.cnames[c],mtx[r,c]
01239 return rv
01240
01241 nodes = {}
01242 mtx = self.Copy()
01243 for r in mtx.rnames:
01244 nodes[r] = t = Tree.Tree()
01245 t.add_feature("name", r)
01246
01247 while len(mtx) >1:
01248 print ".",
01249 m0,m1,dist = FindNearestRows(mtx)
01250 ch0 = nodes[m0]
01251 ch0.add_feature("dist", dist)
01252 ch1 = nodes[m1]
01253 ch1.add_feature("dist", dist)
01254
01255 p = Tree.Tree()
01256 p.add_child(ch0)
01257 p.add_child(ch1)
01258
01259 pname = m0+m1
01260 nodes[pname] = p
01261
01262 mtx.NewRow(name=pname)
01263 mtx.NewCol(name=pname)
01264
01265
01266 for i in range(len(mtx)):
01267 mtx[pname,i] = mtx[i,pname] = (mtx[m0,i]+mtx[m1,i])/2
01268
01269 for m in m0, m1:
01270 mtx.DelRow(m)
01271 mtx.DelCol(m)
01272 del nodes[m]
01273
01274 return nodes.values()[0]
01275
01276
01277
01278
01279
01280 def ToNJTreeOld(self):
01281 """pre: self is square difference matrix
01282 post: A string representing the NJ tree, based on row differences, as calculated by fun """
01283
01284 def FindNearestRows(mtx):
01285
01286 rv = 0,1,mtx[0,1]
01287 if len(mtx) == 2:
01288 return rv
01289
01290 for r in range(len(mtx)):
01291 for c in range(r):
01292 el = mtx[r,c]
01293 if el == 0:
01294 return r,c,0
01295 else:
01296 if el < rv[2]:
01297 rv = r,c,mtx[r,c]
01298 return rv
01299
01300 while len(self) >1:
01301 m0,m1,v = FindNearestRows(self)
01302 strv = ":"+str(v)
01303 self.rnames[m0] = "(" + self.rnames[m0] + strv + "," + self.rnames[m1] + strv+")"
01304 for i in range(len(self)):
01305 self[m0,i] = self[i,m0] = (self[m0,i]+self[m1,i])/2
01306
01307 self.DelRow(m1)
01308 self.DelCol(m1)
01309
01310 return self.rnames[0] + ";"
01311
01312
01313 def IntegiseR(self):
01314 """pre: Seq.Integise(r) for all r in self.rows
01315 post: all denominators = 1, ratios unchanged
01316
01317 !!! Will Fail **SILENTLY** if self[r,c].GetNum() > max(int) !!!
01318
01319 """
01320
01321 self.Conv=int
01322 for r in self.rnames:
01323 self[r] = map(int,Seq.Integise(self[r]))
01324
01325 def IntegiseC(self):
01326
01327 self.Conv=int
01328 for c in self.cnames:
01329 self.SetCol(c, map(int,Seq.Integise(self.GetCol(c))))
01330
01331
01332 def ZapZeroes(self):
01333
01334 for r in range(len(self)-1, -1, -1):
01335 if Seq.AllEq(self[r],0):
01336 self.DelRow(r)
01337
01338 def Mul(self, other):
01339 try:
01340 rows = (Sci.matrix(self.rows) *Sci.matrix(other.rows)).tolist()
01341 rv = self.__class__(rnames=self.rnames, cnames=other.cnames,Conv=self.Conv)
01342 rv.rows = rows
01343 return rv
01344 except:
01345 print "Matrix.Mul() ?",
01346 selfr = len(self)
01347 otherr = len(other)
01348 selfc = len(self.cnames)
01349 otherc = len(other.cnames)
01350
01351 if selfc != otherr:
01352 raise exceptions.ArithmeticError, "inconsistant matrices for multiplication"
01353
01354 rv = self.__class__(selfr,otherc,Conv=self.Conv)
01355 other.Transpose()
01356 for i in range(selfr):
01357 for j in range(otherc):
01358 rv.rows[i][j] = sum(map(lambda x,y:x*y,self.rows[i],other.rows[j]))
01359 other.Transpose()
01360 rv.cnames = other.cnames[:]
01361 rv.rnames = self.rnames[:]
01362 return rv
01363
01364
01365 def OrthNullSpace(self):
01366 "orthogonal nullspace as matrix of floats"
01367
01368 try:
01369 N = Sci.matrix(self.rows,float)
01370 K = Sci.null(N)
01371 Z = N*K
01372 Fail = Z.max() > Sci.TOL or Z.min() < -Sci.TOL
01373 if Fail:
01374 print "OrthNullSpace out of tol"
01375 except:
01376 Fail = True
01377 print "Sci.null() fails !"
01378
01379 if not Fail:
01380 rv = matrix(FromMtx=K,Conv=float)
01381 rv.rnames = self.cnames[:]
01382 rv.cnames =map(lambda x: "c_"+str(x), range(len(rv[0])))
01383 else:
01384 print "falling back to (slow) GaussJ + GramS\n"
01385 if not self.Conv == Types.ArbRat:
01386 rv = self.Copy(Conv=Types.ArbRat).NullSpace()
01387 else:
01388 rv = self.NullSpace()
01389 rv.Orthogonalise()
01390
01391 return rv
01392
01393
01394 def Orthogonalise(self):
01395 """ Use Gram-Schmidt orthogonalisation to make self orthognal"""
01396
01397 self.Transpose()
01398 self.ChangeType(float)
01399
01400 for j in range(len(self)):
01401 vj = self[j][:]
01402 for i in range(j):
01403 ui = self[i]
01404 vjui = -Seq.DotProd(vj,ui)
01405 self.AddRow(j, map(lambda x: x*vjui, ui))
01406 NormJ = math.sqrt(sum(map(lambda x:x*x,self[j])))
01407 self.DivRow(j,k=NormJ)
01408 self.Transpose()
01409
01410
01411 def PseudoInverse(self, AsSci=False):
01412
01413 if self.Conv != float:
01414 rows = self.Copy(float).rows
01415 else:
01416 rows = self.rows
01417
01418 rvrows = Sci.pinv(rows)
01419 if AsSci:
01420 rv = rvrows
01421 else:
01422 nrows, ncols = len(rvrows), len(rvrows[0])
01423 rv = self.__class__(nrows, ncols, Conv=float)
01424 rv.rows = rvrows.tolist
01425 return rv
01426
01427
01428 def SetPivot(self,Row):
01429
01430 GotOne = 0
01431 vals=self.GetCol(Row)
01432
01433 vals = map(abs, vals)[Row:]
01434 PivVal = max(vals)
01435 if PivVal != 0:
01436 GotOne = 1
01437 PivIdx = Row+vals.index(PivVal)
01438 if PivIdx != Row:
01439 self.SwapRow(PivIdx,Row)
01440
01441 return GotOne
01442
01443
01444 def ElimCol(self, Row):
01445 "pre: self.GetEl(Row,Row)==1"
01446
01447 RowVals = self.rows[Row]
01448 for r in range(len(self.rows)):
01449 if r != Row and self.rows[r][Row] != 0:
01450 mul = -self.rows[r][Row]
01451 self.AddRow(r, RowVals, mul)
01452
01453
01454 def RedRowEch(self):
01455 """ put self in reduced row echelon form,
01456 uses col swaps in pivoting """
01457
01458 CurRow = 0
01459 done = 0
01460 if self.Conv == float:
01461 Flatten = Seq.Flatten
01462 else:
01463 Flatten = lambda x,y: 0
01464
01465 while not done:
01466 if Seq.AllEq(self.rows[CurRow], 0):
01467 self.DelRow(CurRow)
01468 else:
01469 if self.SetPivot(CurRow):
01470 self.DivRow(CurRow,vals=None, k=self.rows[CurRow][CurRow])
01471 self.ElimCol(CurRow)
01472 Flatten(self[CurRow], 1e-6)
01473 CurRow += 1
01474 else:
01475 self.MakeLastCol(CurRow)
01476
01477 done = CurRow == len(self.rows)
01478
01479
01480 def NullSpace(self):
01481
01482 rv = self.Copy(Conv=DefaultConv)
01483 rv.RedRowEch()
01484 nsrnames = rv.cnames[:]
01485 slice = len(rv.rows)
01486 if len(rv.cnames) != slice:
01487 for r in range(len(rv.rows)):
01488 rv.rows[r] = rv.rows[r][slice:]
01489 rv.cnames = map(lambda x: "c_"+str(x), range(len(rv[0])))
01490 id = GetIdent(len(rv.cnames),Conv=self.Conv, mul = -1)
01491 rv.AugRow(id)
01492 rv.rnames = nsrnames
01493 rv.RowReorder(self.cnames)
01494 else:
01495
01496 rv.cnames = []
01497 rv.rnames = self.cnames[:]
01498 rv.rows = map(lambda x:[], rv.rnames)
01499 return rv
01500
01501
01502