Package libSBtab :: Module Table
[hide private]
[frames] | no frames]

Source Code for Module libSBtab.Table

  1  """Table class for storing Rows 
  2   
  3  One base class defines the methods 
  4  For each BioTable there is a subclass that defines 
  5      * columns 
  6      * validations 
  7      * special method implementations 
  8  """ 
  9  import csv 
 10  import copy 
 11   
 12  try: 
 13      import semanticSBML.kegg2sbml 
 14      kegg2sbml_imported = True 
 15  except ImportError: 
 16      kegg2sbml_imported = False 
 17  except: # while unstable rebuilding of semanticsbml 
 18      kegg2sbml_imported = False 
 19   
 20  import Row 
 21  import Column 
 22  import Exception 
 23  from libSBtab import GlobalOptions 
 24  import Warning 
 25  import Validator 
 26  import Helper 
 27   
28 -def enzyme_regulation(string):
29 """temp function until a correct enzyme regulation parser is implemented""" 30 arr = string.split("*") 31 ret = "" 32 for elem in arr: 33 if elem.startswith("modI"): 34 ret += "".join(["-%s " % splitted_elem for splitted_elem in elem[5:len(elem)-1].split(",")]) 35 else: 36 ret += "".join(["+%s " % splitted_elem for splitted_elem in elem[1:len(elem)-1].split(",")]) 37 return ret
38
39 -def eval_sort(row1, row2, sort_columns):
40 """function for comparing two rows by specified attributes 41 42 @param row1: first row 43 @type row1: Row 44 @param row2: second row 45 @type row2: Row 46 @param sort_columns: attribute list which is used for comparison 47 ["name", "number"] will compare first by name and if this is equal by number 48 [("name", "desc"), "number"] will do the same but comparing name will be reversed 49 @type sort_columns: list of (str, tuple of str) 50 @return: +1 if row1 < row2 according to sort_columns 51 -1 if row1 > row2 52 0 if row1 == row2 53 @rtype: int 54 """ 55 if sort_columns: 56 sort_column = sort_columns.pop(0) 57 if isinstance(sort_column, tuple): 58 reverse = sort_column[1].lower() == "desc" 59 sort_column = sort_column[0] 60 else: 61 reverse = False 62 return (reverse and -1 or 1) * cmp(row1.attributeGet(sort_column), row2.attributeGet(sort_column)) or eval_sort(row1, row2, sort_columns) 63 else: 64 return 0
65
66 -class Table:
67 """Base class for all BioTables 68 69 Provides functionality that all table types share 70 """ 71
72 - def __init__(self, table_pile = None):
73 self.__table_pile = table_pile 74 self.rows = [] 75 self.known = Validator.Known() 76 self.required = Validator.Required() 77 self.columns_mapping = {} 78 self.addColumnsMapping({ 79 "Description" : Column.Description, 80 "Comment" : Column.SimpleString, 81 "Reference" : Column.SimpleString, 82 "Reference.PubMed" : Column.SimpleString, 83 "Reference.DOI" : Column.SimpleString, 84 }) 85 self.first_line = [] 86 self.filename = None 87 self.activated_columns = True
88
89 - def getDocument(self):
90 if self.__table_pile is not None: 91 return self.__table_pile 92 raise Exception.NoDocumentError(self)
93
94 - def addColumnsMapping(self, columns_mapping):
95 self.columns_mapping.update(columns_mapping)
96
97 - def addReference(self, reference_name, table = None, required = True):
98 reference_name_id = reference_name + "ID" 99 if table is None: 100 try: 101 table = getattr(Table, reference_name + "Table") 102 except AttributeError: 103 table = None 104 self.addColumnsMapping({ 105 reference_name : Column.ReferenceName, 106 reference_name_id : Column.ReferenceID, 107 }) 108 if required: 109 self.addRequiredColumns((reference_name, reference_name_id)) 110 if table: 111 self.addToBeKnown([[reference_name_id, table, "ID"], [reference_name_id, table, reference_name]])
112
113 - def addToBeKnown(self, *sequence):
114 self.known.add(tuple(sequence))
115
116 - def changeToBeKnown(self, *sequence):
117 self.known.change(tuple(sequence))
118
119 - def addRequiredColumns(self, *columns):
120 self.required.add(tuple(columns))
121
122 - def changeRequiredColumns(self, *columns):
123 self.required.change(tuple(columns))
124
125 - def activateColumns(self):
126 for column_proxy in self.first_line: 127 if column_proxy in self.columns_mapping: 128 column = self.columns_mapping[column_proxy]() 129 else: 130 if column_proxy[0] != '"' or column_proxy[-1] != '"': 131 try: 132 column_row = self.getDocument().getNickname(column_proxy) 133 except Exception.NoDocumentError: 134 column_row = None 135 if column_row is not None: 136 column = column_row.toColumn() 137 else: 138 Warning.warn(Warning.NoSuchColumnWarning(self.filename, 1, column_proxy, self.row_class.__name__)) 139 column = Column.SimpleString() 140 column_proxy.setColumn(self, column) 141 self.activated_columns = True
142
143 - def removeColumn(self, column_name):
144 """ removes a (all) columns with column_name 145 146 @param column_name: name of the column to be removed 147 @type column_name: str 148 @return: None 149 @return type: None 150 """ 151 while column_name in self.first_line: 152 self.first_line.remove(column_name)
153
154 - def changeColumns(self, sequence):
155 self.first_line = [Column.ColumnProxy(column_name) for column_name in sequence] 156 self.activated_columns = False
157
158 - def changeAndActivateColumns(self, first_line):
159 self.changeColumns(first_line) 160 self.activateColumns()
161
162 - def fromTSVFile(self, filename, append = False):
163 self.filename = filename 164 fobj = open(self.filename, "rb") 165 try: 166 self.fromCSVReader(fobj, append) 167 finally: 168 fobj.close()
169
170 - def fromTSVString(self, string, append = False):
171 fobj = string.splitlines() 172 self.fromCSVReader(fobj, append)
173
174 - def fromCSVReader(self, fobj, append = False):
175 """Pseudo Constructor. Parsing from File 176 """ 177 if not append: 178 self.clear() 179 reader = csv.reader(fobj, self.getOption("csv_dialect")) 180 first_line = reader.next() 181 self.changeAndActivateColumns(first_line) 182 for line in reader: 183 if len(line) > 0 and line[0].startswith("#"): 184 if self.activated_columns: 185 attribute = line[0][1:] 186 line = line[1:] 187 for i, value in enumerate(line, 1): 188 column_proxy = self.first_line[i] 189 if attribute == "Quantity": 190 quantity = None 191 for quantity_table in self.getDocument().getTables(QuantityTable): 192 quantity = quantity_table.findByQuantity(value) 193 if quantity: 194 break 195 if quantity: 196 column_proxy.mergeQuantity(quantity) 197 else: 198 column_proxy.setAttribute(attribute, value) 199 else: 200 Warning.warn(Warning.TableNotActivatedWarning(self)) 201 else: 202 row = self.newRow() 203 row.updateByTSVLine(line, reader.line_num) 204 if row: 205 try: 206 self.appendRow(row, False) 207 except Exception.RowError, err: 208 raise Exception.GeneralError(self.filename, row.line_num, str(err)) 209 self.extractNicknames()
210
211 - def toTSV(self, filename = None, csv_dialect = None):
212 """Generates tab-seperated-values file from self.rows 213 214 filename will be created or overwritten if existing 215 permissions must be present 216 """ 217 if not csv_dialect: 218 csv_dialect = self.getOption("csv_dialect") 219 if not filename: 220 filename = self.filename 221 writer = csv.writer(open(filename, "w"), csv_dialect) 222 writer.writerow(self.first_line) 223 writer.writerows([row.toAttributeList() for row in self])
224
225 - def valid(self):
226 """Validates the Table 227 228 Validates the Table by validating each row. Empty table is invalid 229 """ 230 #for row in self.rows: 231 # if not row.invalid: 232 # 233 # else: return True 234 return all(row.valid() and not row.invalid for row in self.rows)
235
236 - def empty(self):
237 return len(self.rows) == 0
238
239 - def clear(self):
240 self.rows = []
241
242 - def toSBML(self, outfile = None):
243 """Generates SBML File from this table 244 """ 245 raise Exception.exceptions.NotImplementedError("toSBML is only implemented for certain tables classes")
246
247 - def extractNicknames(self, force_extraction = False, clear = False):
248 if clear: 249 self.getDocument().clearNicknames(self.rows) 250 nickname_name = self.row_class.__name__ 251 if force_extraction or nickname_name in self.first_line: 252 for row in self.rows: 253 if row.attributePresent(nickname_name): 254 self.getDocument().addNickname(row.attributeGet(nickname_name), row)
255
256 - def mergeTable(self, other_table):
257 if self.__class__ is not other_table.__class__: 258 raise TypeError("cannot merge a '%s' into a '%s'" % (other_table.className(), self.className())) 259 for row in other_table: 260 new_row = copy.deepcopy(row) 261 self.appendRow(new_row)
262
263 - def updateRow(self, index_or_row, dictionary):
264 """ Updates a row with a dictionary 265 266 @param index_or_row: index(0, 1, ...) or Row Instance that should be updated 267 @type index_or_row: (int, Row) 268 @param dictionary: Dict by which row is updated {attributeName : value, ...} 269 @type dictionary: dict 270 @return: None 271 @rtype: None 272 """ 273 self.getRow(index_or_row).updateByDict(dictionary)
274
275 - def appendRow(self, row, auto_set_line_num = True):
276 if row not in self: 277 self.rows.append(row) 278 row.table = self 279 if auto_set_line_num: 280 row.line_num = len(self.rows) + 1 # + 1 because of first_line 281 else: 282 raise Exception.RowError(row)
283
284 - def newRow(self):
285 return self.row_class(self)
286
287 - def addNewRow(self, dictionary = None):
288 """ adds a new Row to the table's rows and optionally updates by dict 289 290 @param dictionary: optional dictionary, which is used to initialize new row 291 @type dictionary: dict 292 @return: the new (and updated) row 293 @rtype: Row 294 """ 295 dictionary = dictionary or {} 296 row = self.newRow() 297 row.updateByDict(dictionary) 298 self.appendRow(row) 299 return row
300
301 - def removeRow(self, index_or_row):
302 row = self.getRow(index_or_row) 303 self.rows.remove(row)
304
305 - def sortRows(self, *sort_columns):
306 self.rows.sort(lambda x, y: eval_sort(x, y, list(sort_columns)))
307
308 - def getLine(self, line):
309 return self.getRow(line - 2)
310
311 - def getRow(self, index_or_row):
312 if isinstance(index_or_row, Row.Row): 313 index_or_row = index_or_row.index() 314 return self.rows[index_or_row]
315
316 - def findBy(self, attribute, value, part = None):
317 found = self.getRows({attribute : (part, value)}) 318 found.append(None) 319 return found[0]
320
321 - def getRows(self, dictionary = None, **kwds):
322 if dictionary: 323 dictionary = dictionary.copy() 324 else: 325 dictionary = {} 326 dictionary.update(kwds) 327 container = Helper.RowContainer(self.rows) 328 return container.filter(dictionary)
329
330 - def className(self):
331 return self.__class__.__name__
332
333 - def index(self, row):
334 return self.rows.index(row)
335
336 - def getOption(self, option_name):
337 try: 338 return self.getDocument().getOption(option_name) 339 except Exception.NoDocumentError: 340 return GlobalOptions[option_name]
341
342 - def __iter__(self):
343 """Iterator over all rows. 344 345 For Syntactic sugar like: 346 for row in table: 347 ... 348 """ 349 return self.rows.__iter__()
350
351 - def __len__(self):
352 return len(self.rows)
353
354 - def __nonzero__(self):
355 return self.valid()
356
357 - def __getattr__(self, name):
358 if name.startswith("findBy"): 359 return lambda value, part = None: self.findBy(name[6:], value, part) 360 raise AttributeError("%s instance has no attribute '%s'" % (self.className(), name))
361
362 -class EntityTable(Table):
363 - def __init__(self, table_pile = None):
364 Table.__init__(self, table_pile) 365 self.addColumnsMapping({ 366 "ID" : Column.ID, 367 "Name" : Column.Name, 368 self.row_class.__name__ : Column.Nickname, 369 "BuildElement" : Column.BuildElement, 370 }) 371 self.addRequiredColumns(("ID", "Name"), self.row_class.__name__) 372 self.addReference("Type", False)
373
374 -class IdentifierTable(Table):
375 - def __init__(self, table_pile = None):
376 Table.__init__(self, table_pile) 377 self.row_class = Row.Identifier 378 self.addColumnsMapping({ 379 "Identifier" : Column.Nickname, 380 "URI" : Column.URI, 381 "IdentifierName" : Column.DBName, 382 "RegularExpression" : Column.IDPattern, 383 }) 384 self.addRequiredColumns("Identifier", "IdentifierName", "URI", "RegularExpression")
385
386 -class GraphicsInformationTable(Table):
387 - def __init__(self, table_pile = None):
388 Table.__init__(self, table_pile) 389 self.row_class = Row.GraphicsInformation 390 self.addColumnsMapping({ 391 "ID" : Column.ID, 392 "Name" : Column.Name, 393 "PositionX" : Column.Position, 394 "PositionY" : Column.Position, 395 "Print" : Column.Print, 396 }) 397 self.addRequiredColumns(("ID", "Name"), "PositionX", "PositionY")
398
399 -class ReactionTable(Table):
400 - def __init__(self, table_pile = None):
401 Table.__init__(self, table_pile) 402 self.row_class = Row.Reaction 403 self.addColumnsMapping({ 404 "ReactionFormula" : Column.ReactionFormula, 405 "ID" : Column.ReactionID, 406 "Gene" : Column.SimpleString, 407 "EnzymeRegulators" : Column.EnzymeRegulators, 408 "EnzymeRegulationBoolean" : Column.EnzymeRegulationBoolean, 409 "TranscriptionalRegulation" : Column.TranscriptionalRegulation, 410 "TranslationalRegulation" : Column.TranslationalRegulation, 411 "BuildReaction" : Column.BuildElement, 412 "BuildExpression" : Column.BuildElement, 413 }) 414 self.addReference("Compartment") 415 self.addRequiredColumns(("ID", "ReactionFormula"))
416
417 - def toSBML(self, outfile = None):
418 """Generates SBML File from this table 419 """ 420 if not kegg2sbml_imported: 421 return NotImplemented 422 reactions = [] 423 reaction2compartment = {} 424 species2compartment = {} 425 426 if not outfile: 427 outfile = self.filename + ".sbml" 428 429 for row in self: 430 if not row: 431 continue 432 reaction = None 433 if row.nonZero("ID"): 434 reaction = row.get("ID") 435 reaction_str = reaction.id 436 reactions.append(reaction_str) 437 elif row.nonZero("ReactionFormula"): 438 reaction = row.get("ReactionFormula") 439 reaction_str = str(reaction) 440 reactions.append(reaction_str) 441 for reactand in reaction: 442 if reactand.compartment and reactand.different_compartment: 443 species2compartment[reactand.species()] = reactand.compartment 444 reaction2compartment[reaction_str] = row.getSpecifiedOrDefaultCompartment() 445 446 transport_compounds = {} 447 create_modifier_enzymes = True 448 compartment_diffusion = False 449 ncbi_id = 4932 # s. cerevisiae 450 mode = 'offline' 451 model_id = 'build_test_model' 452 453 # genes !! 454 reaction2gene = {} # this writes geneA as a semanticSBML internal annotation into reaction R00001 (does nothing but annotate) 455 456 # +a -b !! 457 reaction2modifiers = {} # A activates, and B inhibits reaction R0001 458 459 reaction2metabolic_input_functions = {} # annotate R0001 with metabolic input function "katze" (does nothing but annotate) 460 reaction2use = {} # do not use reaction 2 461 reaction2transcr_reg = {} # build gene regulation module for reaction R00001 462 abbrevations = {} # synonyms 463 464 k2s = semanticSBML.kegg2sbml.kegg2sbml( reactions, 465 reaction2compartment = reaction2compartment, 466 species2compartment = species2compartment, 467 transport_compounds = transport_compounds, 468 create_modifier_enzymes = create_modifier_enzymes, 469 compartment_diffusion = compartment_diffusion, 470 ncbi_id = ncbi_id, 471 mode = mode, 472 model_id = model_id, 473 reaction2gene = reaction2gene, 474 reaction2modifiers = reaction2modifiers, 475 reaction2metabolic_input_functions = reaction2metabolic_input_functions, 476 reaction2use = reaction2use, 477 reaction2transcr_reg = reaction2transcr_reg, 478 abbrevations = abbrevations ) 479 k2s.writeSBML(outfile)
480
481 -class KineticDataTable(Table):
482 - def __init__(self, table_pile = None):
483 Table.__init__(self, table_pile) 484 self.row_class = Row.KineticData 485 self.addColumnsMapping({ 486 "Quantity" : Column.SimpleString, 487 "QuantityType" : Column.SimpleString, 488 "Reaction" : Column.SimpleString, 489 "Compound" : Column.SimpleString, 490 "CompoundName" : Column.SimpleString, 491 "CompoundID" : Column.SimpleString, 492 "Value" : Column.Value, 493 "log(Std)" : Column.Value, 494 "Value_Generated" : Column.SimpleString, 495 "Std" : Column.Std, 496 "Mean" : Column.Value, 497 "Unit" : Column.SimpleString, 498 "Compartment" : Column.SimpleString, 499 "Organism" : Column.SimpleString, 500 "Reference" : Column.SimpleString, 501 "ReactionAnnotation" : Column.SimpleString, 502 "ReactionEC" : Column.SimpleString, 503 "EnzymeName" : Column.SimpleString, 504 "EnzymeID" : Column.SimpleString, 505 "CompoundAnnotation" : Column.SimpleString, 506 "ReactionName" : Column.SimpleString, 507 "ReactionSubstrateAnnotation" : Column.SimpleString, 508 #from here: new columns for quantity data table 509 "SBMLid" : Column.SimpleString, 510 "MathTransformation" : Column.SimpleString, 511 "pH" : Column.SimpleString, 512 "Temperature" : Column.SimpleString, 513 "CompartmentID" : Column.SimpleString, 514 "CompartmentName" : Column.SimpleString, 515 "CompartmentSBMLid" : Column.SimpleString, 516 "CompoundSBMLid" : Column.SimpleString, 517 "ReactionID" : Column.SimpleString, 518 "ReactionSBMLid" : Column.SimpleString, 519 "Enzyme" : Column.SimpleString, 520 "EnzymeSBMLid" : Column.SimpleString, 521 "Gene" : Column.SimpleString, 522 "GeneID" : Column.SimpleString, 523 "GeneName" : Column.SimpleString, 524 "OrganismName" : Column.SimpleString, 525 "OrganismID" : Column.SimpleString, 526 }) 527 self.addRequiredColumns(("Reaction", "Compound"), "QuantityType")
528
529 -class MetaboliteDataTable(Table):
530 - def __init__(self, table_pile = None):
531 Table.__init__(self, table_pile) 532 self.row_class = Row.MetaboliteData 533 self.addColumnsMapping({ 534 "ID" : Column.ID, 535 "Quantity" : Column.Quantity, 536 "Mean" : Column.Mean, 537 "StdDev" : Column.Std, 538 "Unit" : Column.Unit, 539 }) 540 self.addRequiredColumns("ID", "Quantity", "Unit")
541
542 -class CompoundTable(EntityTable):
543 - def __init__(self, table_pile = None):
544 self.row_class = Row.Compound 545 EntityTable.__init__(self, table_pile) 546 self.addColumnsMapping({ 547 "SumFormula": Column.SimpleString, 548 "StructureFormula" : Column.SimpleString, 549 "Charge" : Column.Charge, 550 "Mass" : Column.Mass, 551 "Components" : Column.SimpleString, 552 "ComponentsFormula" : Column.SimpleString, 553 "IsConstant" : Column.IsConstant, 554 "Split" : Column.Split, 555 }) 556 self.addReference("Compartment") 557 self.addReference("State", False) 558 self.addReference("Enzyme") 559 self.addReference("Regulator") 560 self.addRequiredColumns("IsConstant", "Charge", "Mass")
561
562 -class EnzymeTable(EntityTable):
563 - def __init__(self, table_pile = None):
564 self.row_class = Row.Enzyme 565 EntityTable.__init__(self, table_pile) 566 self.addColumnsMapping({ 567 "KineticLawFormula" : Column.SimpleString, 568 "EnzymeRegulation" : Column.SimpleString, 569 }) 570 self.addReference("TargetReaction") 571 self.addReference("KineticLaw") 572 self.addReference("Gene")
573
574 -class GeneTable(EntityTable):
575 - def __init__(self, table_pile = None):
576 self.row_class = Row.Gene 577 EntityTable.__init__(self, table_pile) 578 self.addColumnsMapping({ 579 "GeneName" : Column.SimpleString, 580 "GeneLocus" : Column.SimpleString, 581 "TranscriptionalRegulation": Column.SimpleString, 582 "TranslationalRegulation" : Column.SimpleString, 583 "PosttranslationalRegulation" : Column.SimpleString, 584 }) 585 self.addReference("GeneProduct") 586 self.addReference("Operon", table=GeneTable)
587
588 -class RegulatorTable(EntityTable):
589 - def __init__(self, table_pile = None):
590 self.row_class = Row.Regulator 591 EntityTable.__init__(self, table_pile) 592 self.addColumnsMapping({ 593 "ActivityCondition" : Column.SimpleString, 594 "BindingAbilityCondition" : Column.SimpleString, 595 }) 596 self.addReference("State") 597 self.addReference("TargetGene") 598 self.addReference("TargetOperon") 599 self.addReference("TargetPromotor")
600
601 -class CompartmentTable(EntityTable):
602 - def __init__(self, table_pile = None):
603 self.row_class = Row.Compartment 604 EntityTable.__init__(self, table_pile) 605 self.addColumnsMapping({ 606 "Size" : Column.Size, 607 "SizeUnit" : Column.Unit, 608 }) 609 self.addRequiredColumns("Size", "SizeUnit") 610 self.addReference("OuterCompartment", required = False)
611
612 -class QuantityTable(Table):
613 - def __init__(self, table_pile = None):
614 Table.__init__(self, table_pile) 615 self.row_class = Row.Quantity 616 self.addColumnsMapping({ 617 "Quantity" : Column.Quantity, 618 "Time" : Column.Time, 619 "MathematicalDescriptor" : Column.MathematicalDescriptor, 620 "Unit" : Column.Unit, 621 }) 622 self.addRequiredColumns("Quantity")
623
624 -class ColumnTable(Table):
625 - def __init__(self, table_pile = None):
626 Table.__init__(self, table_pile) 627 self.row_class = Row.Column 628 self.addColumnsMapping({ 629 "Column" : Column.ColumnName, 630 "DataType" : Column.DataType, 631 }) 632 self.addRequiredColumns("Column", "DataType")
633