Changeset 1349:f82ea66d57ff in orange-bioinformatics


Ignore:
Timestamp:
03/15/11 15:52:45 (3 years ago)
Author:
ales_erjavec <ales.erjavec@…>
Branch:
default
Convert:
2326ab29d00437d775273b6a58a1f6ff642c4222
Message:

Reworked OBO file parsing.
Handle escape characters in files.
Added methods to query the ontology for parent/child, super/sub terms.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • obiOntology.py

    r1326 r1349  
    1 """  
     1"""\ 
     2obiOntology 
     3----------- 
     4 
    25Python module for manipulating OBO Ontology files (http://www.obofoundry.org/) 
    36 
    4 TODO: 
    5     - handle escape characters !!! 
    6  
    77Example:: 
     8 
    89    >>> term = OBOObject("Term", id="foo:bar", name="Foo bar") 
    910    >>> print term 
     
    1314     
    1415    >>> ontology = OBOOntology() 
    15     >>> ontology.addObject(term) 
     16    >>> ontology.add_object(term) 
    1617     
    1718""" 
     
    1920from itertools import chain 
    2021from collections import defaultdict 
     22from StringIO import StringIO 
    2123import itertools 
    22  
    23 builtinOBOObjects = [ 
     24import re 
     25 
     26BUILTIN_OBO_OBJECTS = [ 
    2427"""[Typedef] 
    2528id: is_a 
     
    6467definition: Indicates that a term is the intersection of several others [OBO:defs]""" 
    6568] 
    66  
    67 def _splitAndStrip(string, sep): 
     69     
     70def _split_and_strip(string, sep): 
    6871    head, tail = string.split(sep, 1) 
    6972    return head.rstrip(" "), tail.lstrip(" ") 
     
    7376    """ Represents a generic OBO object (e.g. Term, Typedef, Instance, ...) 
    7477    Example:: 
    75         >>> term = OBOObject(stanzaType="Term", id="FOO:001", name="bar") 
     78        >>> term = OBOObject(stanza_type="Term", id="FOO:001", name="bar") 
    7679    """ 
    77     def __init__(self, stanzaType="Term", **kwargs): 
     80    def __init__(self, stanza_type="Term", **kwargs): 
    7881        """ Init from keyword arguments. 
    7982        Example:: 
    80             >>> term = OBOObject(stanzaType="Term", id="FOO:001", name="bar", def_="Example definition {modifier=frob} ! Comment") 
    81             >>> term = OBOObject(stanzaType="Term", id="FOO:001", name="bar", def_=("Example definition", [("modifier", "frob")], "Comment")) 
    82             >>> term = OBOObject(stanzaType="Term", id="FOO:001", name="bar", def_=("Example definition", [("modifier", "frob")])) # without the comment 
    83             >>> term = OBOObject(stanzaType="Term", id="FOO:001", name="bar", def_=("Example definition",)) # without the modifiers and comment 
    84         """ 
    85         self.stanzaType = stanzaType 
     83            >>> term = OBOObject(stanza_type="Term", id="FOO:001", name="bar", def_="Example definition { modifier=frob } ! Comment") 
     84            >>> term = OBOObject(stanza_type="Term", id="FOO:001", name="bar", def_=("Example definition", [("modifier", "frob")], "Comment")) 
     85            >>> term = OBOObject(stanza_type="Term", id="FOO:001", name="bar", def_=("Example definition", [("modifier", "frob")])) # without the comment 
     86            >>> term = OBOObject(stanza_type="Term", id="FOO:001", name="bar", def_=("Example definition",)) # without the modifiers and comment 
     87        """ 
     88        self.stanza_type = stanza_type 
    8689         
    8790        self.modifiers = [] 
    8891        self.comments = [] 
    89         self.tagValues = [] 
     92        self.tag_values = [] 
    9093        self.values = {} 
    9194         
    92         sortedTags = sorted(kwargs.iteritems(), key=lambda key_val: chr(1) if key_val[0] == "id" else key_val[0]) 
    93         for tag, value in sortedTags: 
     95        sorted_tags = sorted(kwargs.iteritems(), key=lambda key_val: chr(1) if key_val[0] == "id" else key_val[0]) 
     96        for tag, value in sorted_tags: 
    9497            if isinstance(value, basestring): 
    95                 tag, value, modifiers, comment = self.parseTagValue(self.name_demangle(tag), value) 
     98                tag, value, modifiers, comment = self.parse_tag_value(self.name_demangle(tag), value) 
    9699            elif isinstance(value, tuple): 
    97100                tag, value, modifiers, comment = ((self.name_demangle(tag),) + value + (None, None))[:4] 
    98             self.addTag(tag, value, modifiers, comment) 
     101            self.add_tag(tag, value, modifiers, comment) 
    99102         
    100103        self.related = set() 
    101         self.relatedTo = set() 
     104#        self.related_to = set() 
    102105             
    103106    @property 
    104107    def is_annonymous(self): 
    105         value = self.getValue("is_annonymous") 
     108        value = self.get_value("is_annonymous") 
    106109        return bool(value) 
    107110     
     
    125128            return tag 
    126129         
    127     def addTag(self, tag, value, modifiers=None, comment=None): 
     130    def add_tag(self, tag, value, modifiers=None, comment=None): 
    128131        """ Add `tag`, `value` pair to the object with optional modifiers and 
    129132        comment. 
    130133        Example:: 
    131134            >>> term = OBOObject("Term") 
    132             >>> term.addTag("id", "FOO:002", comment="This is an id") 
     135            >>> term.add_tag("id", "FOO:002", comment="This is an id") 
    133136            >>> print term 
    134137            [Term] 
     
    136139              
    137140        """ 
    138         self.tagValues.append((tag, value)) 
     141        tag = intern(tag) # a small speed and memory benefit 
     142        self.tag_values.append((tag, value)) 
    139143        self.modifiers.append(modifiers) 
    140144        self.comments.append(comment) 
     
    155159        to the end except for the `id` tag. 
    156160        """  
    157         for (tag, value), modifiers, comment in zip(other.tagValues, other.modifiers, other.comments): 
     161        for (tag, value), modifiers, comment in zip(other.tag_values, other.modifiers, other.comments): 
    158162            if tag != "id": 
    159                 self.addTag(tag, value, modifiers, comment) 
    160          
    161     def getValue(self, tag, group=True): 
     163                self.add_tag(tag, value, modifiers, comment) 
     164         
     165    def get_value(self, tag, group=True): 
    162166        if group: 
    163             pairs = [pair for pair in self.tagValues if pair[0] == tag] 
     167            pairs = [pair for pair in self.tag_values if pair[0] == tag] 
    164168            return pairs 
    165169        else: 
     
    170174                raise ValueError("No value for tag: %s" % tag) 
    171175         
    172     def tagCount(self): 
     176    def tag_count(self): 
    173177        """ Retrun the number of tags in this object 
    174178        """ 
    175         return len(self.tagValues) 
     179        return len(self.tag_values) 
    176180     
    177181    def tags(self): 
    178182        """ Retrun an iterator over the (tag, value) pairs. 
    179183        """ 
    180         for i in range(self.tagCount()): 
    181             yield self.tagValues[i] + (self.modifiers[i], self.comments[i]) 
    182          
    183     def formatSingleTag(self, index): 
     184        for i in range(self.tag_count()): 
     185            yield self.tag_values[i] + (self.modifiers[i], self.comments[i]) 
     186         
     187    def format_single_tag(self, index): 
    184188        """Return a formated string representing index-th tag pair value 
    185189        Example:: 
    186190            >>> term = OBOObject("Term", id="FOO:001", name="bar", def_="Example definition {modifier=frob} ! Comment") 
    187             >>> term.formatSingleTag(0) 
     191            >>> term.format_single_tag(0) 
    188192            'id: FOO:001' 
    189             >>> term.formatSingleTag(1) 
     193            >>> term.format_single_tag(1) 
    190194            'def: Example definition { modifier=frob } ! Comment' 
    191195        """ 
    192         tag, value = self.tagValues[index] 
     196        tag, value = self.tag_values[index] 
    193197        modifiers = self.modifiers[index] 
    194198        comment = self.comments[index] 
     
    200204        return " ".join(res) 
    201205     
    202     def formatStanza(self): 
     206    def format_stanza(self): 
    203207        """ Return a string stanza representation of this object  
    204208        """ 
    205         stanza = ["[%s]" % self.stanzaType] 
    206         for i in range(self.tagCount()): 
    207             stanza.append(self.formatSingleTag(i)) 
     209        stanza = ["[%s]" % self.stanza_type] 
     210        for i in range(self.tag_count()): 
     211            stanza.append(self.format_single_tag(i)) 
    208212        return "\n".join(stanza) 
    209213             
    210214    @classmethod      
    211     def parseStanza(cls, stanza): 
     215    def parse_stanza(cls, stanza): 
    212216        r""" Parse and return an OBOObject instance from a single stanza. 
    213217        Example:: 
    214             >>> term = OBOObject.parseStanza("[Term]\nid: FOO:001\nname:bar") 
     218            >>> term = OBOObject.parse_stanza("[Term]\nid: FOO:001\nname:bar") 
    215219            >>> print term.id, term.name 
    216220            FOO:001 bar 
     
    218222        """ 
    219223        lines = stanza.splitlines() 
    220         stanzaType = lines[0].strip("[]") 
     224        stanza_type = lines[0].strip("[]") 
    221225        tag_values = [] 
    222226        for line in lines[1:]: 
    223227            if ":" in line: 
    224                 tag_values.append(cls.parseTagValue(line)) 
    225          
    226         obo = OBOObject(stanzaType) 
     228                tag_values.append(cls.parse_tag_value(line)) 
     229         
     230        obo = OBOObject(stanza_type) 
    227231        for i, (tag, value, modifiers, comment) in enumerate(tag_values): 
    228232#            print tag, value, modifiers, comment 
    229             obo.addTag(tag, value, modifiers, comment) 
     233            obo.add_tag(tag, value, modifiers, comment) 
    230234        return obo 
    231235     
    232236         
    233237    @classmethod 
    234     def parseTagValue(cls, tagValuePair, *args): 
     238    def parse_tag_value_1(cls, tag_value_pair, *args): 
    235239        """ Parse and return a four-tuple containing a tag, value, a list of modifier pairs, comment. 
    236240        If no modifiers or comments are present the corresponding entries will be None. 
    237241         
    238242        Example:: 
    239             >>> OBOObject.parseTagValue("foo: bar {modifier=frob} ! Comment") 
     243            >>> OBOObject.parse_tag_value("foo: bar {modifier=frob} ! Comment") 
    240244            ('foo', 'bar', 'modifier=frob', 'Comment') 
    241             >>> OBOObject.parseTagValue("foo: bar") 
     245            >>> OBOObject.parse_tag_value("foo: bar") 
    242246            ('foo', 'bar', None, None) 
    243247            >>> #  Can also pass tag, value pair already split    
    244             >>> OBOObject.parseTagValue("foo", "bar {modifier=frob} ! Comment") 
     248            >>> OBOObject.parse_tag_value("foo", "bar {modifier=frob} ! Comment") 
    245249            ('foo', 'bar', 'modifier=frob', 'Comment') 
    246250        """ 
    247         if args and ":" not in tagValuePair: 
    248             tag, rest = tagValuePair, args[0] 
     251        if args and ":" not in tag_value_pair: 
     252            tag, rest = tag_value_pair, args[0] 
    249253        else: 
    250             tag, rest = _splitAndStrip(tagValuePair, ":") 
     254            tag, rest = _split_and_strip(tag_value_pair, ":") 
    251255        value, modifiers, comment = None, None, None 
    252256         
    253257        if "{" in rest: 
    254             value, rest = _splitAndStrip(rest, "{",) 
    255             modifiers, rest = _splitAndStrip(rest, "}") 
     258            value, rest = _split_and_strip(rest, "{",) 
     259            modifiers, rest = _split_and_strip(rest, "}") 
    256260        if "!" in rest: 
    257261            if value is None: 
    258                 value, comment = _splitAndStrip(rest, "!") 
     262                value, comment = _split_and_strip(rest, "!") 
    259263            else: 
    260                 _, comment = _splitAndStrip(rest, "!") 
     264                _, comment = _split_and_strip(rest, "!") 
    261265        if value is None: 
    262266            value = rest 
     
    266270             
    267271        return tag, value, modifiers, comment 
     272     
     273    _RE_TAG_VALUE = re.compile(r"^(?P<tag>.+?[^\\])\s*:\s*(?P<value>.+?)\s*(?P<modifiers>[^\\]{.+?[^\\]})?\s*(?P<comment>[^\\]!.*)?$") 
     274    _RE_VALUE = re.compile(r"^\s*(?P<value>.+?)\s*(?P<modifiers>[^\\]{.+?[^\\]})?\s*(?P<comment>[^\\]!.*)?$") 
     275     
     276    @classmethod 
     277    def parse_tag_value(cls, tag_value_pair, arg=None): 
     278        """ Parse and return a four-tuple containing a tag, value, a list of modifier pairs, comment. 
     279        If no modifiers or comments are present the corresponding entries will be None. 
     280         
     281        Example:: 
     282            >>> OBOObject.parse_tag_value("foo: bar {modifier=frob} ! Comment") 
     283            ('foo', 'bar', 'modifier=frob', 'Comment') 
     284            >>> OBOObject.parse_tag_value("foo: bar") 
     285            ('foo', 'bar', None, None) 
     286            >>> #  Can also pass tag, value pair already split    
     287            >>> OBOObject.parse_tag_value("foo", "bar {modifier=frob} ! Comment") 
     288            ('foo', 'bar', 'modifier=frob', 'Comment') 
     289             
     290        .. warning: This function assumes comment an modifiers are prefixed 
     291            with a whitespace i.e. 'tag: bla! comment' will be parsed incorrectly! 
     292        """ 
     293        if arg is not None: # tag_value_pair is actually a tag only 
     294            tag = tag_value_pair 
     295            value, modifiers, comment =  cls._RE_VALUE.findall(arg)[0] 
     296        else: 
     297            tag, value, modifiers, comment = cls._RE_TAG_VALUE.findall(tag_value_pair)[0] 
     298        none_if_empyt = lambda val: None if not val.strip() else val.strip() 
     299        modifiers = modifiers.strip(" {}") 
     300        comment = comment.lstrip(" !") 
     301        return (none_if_empyt(tag), none_if_empyt(value), 
     302                none_if_empyt(modifiers), none_if_empyt(comment)) 
    268303          
    269     def GetRelatedObjects(self): 
    270         """ Obsolete. Use `relatedObjects()` instead 
    271         """ 
    272         return self.relatedObjects() 
    273      
    274     def relatedObjects(self): 
     304    def related_objects(self): 
    275305        """ Return a list of tuple pairs where the first element is relationship (typedef id) 
    276306        is and the second object id whom the relationship applies to. 
    277307        """ 
    278         result = [(typeId, id) for typeId in ["is_a"] for id in self.values.get(typeId, [])] ##TODO add other defined Typedef ids 
     308        result = [(type_id, id) for type_id in ["is_a"] for id in self.values.get(type_id, [])] ##TODO add other defined Typedef ids 
    279309        result = result + [tuple(r.split(None, 1)) for r in self.values.get("relationship", [])] 
    280310        return result 
     
    283313        """ Return a string representation of the object in OBO format 
    284314        """ 
    285         return self.formatStanza() 
     315        return self.format_stanza() 
    286316 
    287317    def __iter__(self): 
    288318        """ Iterates over sub terms 
    289319        """ 
    290         for typeId, id in self.relatedObjects(): 
    291             yield (typeId, id) 
     320        for type_id, id in self.related_objects(): 
     321            yield (type_id, id) 
     322         
    292323         
    293324class Term(OBOObject): 
     
    303334        OBOObject.__init__(self, "Instance", *args, **kwargs) 
    304335 
     336 
    305337import re 
    306338 
    307 class OBOOntology(object): 
    308     _RE_TERM = re.compile(r"\[.+?\].*?\n\n", re.DOTALL) 
    309     _RE_HEADER = re.compile(r"^[^[].*?\n\[", re.DOTALL) 
    310     BUILTINS = builtinOBOObjects 
    311      
    312     def __init__(self): 
    313         """ Init an empty Ontology. 
    314          
    315         .. note:: Use parseOBOFile to load from a file 
    316          
    317         """ 
    318         self.objects = [] 
    319         self.headerTags = [] 
    320         self.id2Term = {} 
    321          
    322     def addObject(self, object): 
    323         """ Add OBOObject instance to  this ontology. 
    324         """ 
    325         if object.id in self.id2Term: 
    326             raise ValueError("OBOObject with id: %s already in the ontology" % object.id) 
    327         self.objects.append(object) 
    328         self.id2Term[object.id] = object 
    329          
    330     def addHeaderTag(self, tag, value): 
    331         """ Add header tag, value pair to this ontology 
    332         """ 
    333         self.headerTags.append((tag, value)) 
    334          
    335 #    @classmethod 
    336 #    def parseOBOFile(cls, file): 
    337 #        """ Parse the .obo file and return an OBOOntology instance 
    338 #        Example:: 
    339 #            >>> OBOOntology.parseOBOFile(open("dictyostelium_anatomy.obo", "rb")) 
    340 #            <obiOntology.OBOOntology object at ...> 
    341 #        """  
    342 #        ontology = OBOOntology() 
    343 #        data = file.read() 
    344 #         
    345 #        header = data[:data.index("\n[")] 
    346 #        for line in header.splitlines(): 
    347 #            if line.strip(): 
    348 #                ontology.addHeaderTag(*line.split(":", 1)) 
    349 #         
    350 #        imports = [value for  tag, value in ontology.headerTags if tag == "import"] 
    351 #         
    352 #        terms = cls.BUILTINS + cls._RE_TERM.findall(data) 
    353 #        for term in terms: 
    354 #            term = OBOObject.parseStanza(term) 
    355 #            ontology.addObject(term) 
    356 #             
    357 #        while imports: 
    358 #            url = imports.pop(0) 
    359 #            imported = self.parseOBOFile(open(url, "rb")) 
    360 #            ontology.update(imported) 
    361 #        return ontology 
    362      
    363     @classmethod 
    364     def parseOBOFile(cls, file): 
    365         """ Parse the .obo file and return an OBOOntology instance 
    366         Example:: 
    367             >>> OBOOntology.parseOBOFile(open("dictyostelium_anatomy.obo", "rb")) 
    368             <obiOntology.OBOOntology object at ...> 
    369         """  
    370         ontology = OBOOntology() 
    371         data = file.read() 
     339class OBOParser(object): 
     340    r""" A simple parser for .obo files (inspired by xml.dom.pulldom) 
     341     
     342    Example:: 
     343        >>> from StringIO import StringIO 
     344        >>> file = StringIO("header_tag: header_value\n[Term]\nid: FOO { modifier=bar } ! comment\n\n")  
     345        >>> parser = OBOParser(file) 
     346        >>> for event, value in parser: 
     347        ...     print event, value 
     348        ...      
     349        HEADER_TAG ['header_tag', ' header_value'] 
     350        START_STANZA Term 
     351        TAG_VALUE ('id', 'FOO', 'modifier=bar', 'comment') 
     352        CLOSE_STANZA None 
     353                 
     354    """ 
     355    def __init__(self, file): 
     356        self.file = file 
     357         
     358    def parse(self, progress_callback=None): 
     359        """ Parse the file and yield parse events. 
     360         
     361        .. TODO: List events 
     362        """ 
     363        data = self.file.read() 
    372364        header = data[: data.index("\n[")] 
    373365        body = data[data.index("\n[") + 1:] 
    374366        for line in header.splitlines(): 
    375367            if line.strip(): 
    376                 ontology.addHeaderTag(*line.split(":", 1)) 
     368                yield "HEADER_TAG", line.split(":", 1) 
    377369                 
    378370        current = None 
     
    380372        startswith = str.startswith 
    381373        endswith = str.endswith 
    382         parseTagValue = OBOObject.parseTagValue 
    383          
    384         builtins = "\n\n".join(cls.BUILTINS) 
    385         for line in itertools.chain(builtins.splitlines(), body.splitlines()): 
     374        parse_tag_value = OBOObject.parse_tag_value 
     375         
     376        for line in body.splitlines(): 
    386377#            line = line.strip() 
    387378            if startswith(line, "[") and endswith(line, "]"): 
    388                 current = OBOObject(line.strip("[]")) 
     379                yield "START_STANZA", line.strip("[]") 
     380                current = line 
    389381            elif startswith(line, "!"): 
    390                 pass #  comment 
     382                yield "COMMENT", line[1:] 
    391383            elif line: 
    392                 current.addTag(*parseTagValue(line)) 
     384                yield "TAG_VALUE", parse_tag_value(line) 
    393385            else: #  empty line is the end of a term 
    394                 ontology.addObject(current) 
    395         if current.id not in ontology: 
    396             ontology.addObject(current) 
    397         imports = [value for  tag, value in ontology.headerTags if tag == "import"] 
     386                yield "CLOSE_STANZA", None 
     387                current = None 
     388        if current is not None: 
     389            yield "CLOSE_STANZA", None 
     390     
     391    def __iter__(self): 
     392        """ Iterate over parse events (same as parse()) 
     393        """ 
     394        return self.parse()  
     395         
     396         
     397class OBOOntology(object): 
     398    BUILTINS = BUILTIN_OBO_OBJECTS 
     399     
     400    def __init__(self, file=None): 
     401        """ Init an ontology instance from a file like object (.obo format) 
     402         
     403        """ 
     404        self.objects = [] 
     405        self.header_tags = [] 
     406        self.id2term = {} 
     407        self.alt2id = {} 
     408        self._resolved_imports = [] 
     409        self._invalid_cache_flag = False 
     410        self._related_to = {} 
     411         
     412        # First load the built in OBO objects 
     413        builtins = StringIO("\n" + "\n\n".join(self.BUILTINS) + "\n") 
     414        self.load(builtins) 
     415        if file: 
     416            self.load(file) 
     417         
     418    def add_object(self, object): 
     419        """ Add OBOObject instance to this ontology. 
     420        """ 
     421        if object.id in self.id2term: 
     422            raise ValueError("OBOObject with id: %s already in the ontology" % object.id) 
     423        self.objects.append(object) 
     424        self.id2term[object.id] = object 
     425        self._invalid_cache_flag = True 
     426         
     427    def add_header_tag(self, tag, value): 
     428        """ Add header tag, value pair to this ontology 
     429        """ 
     430        self.header_tags.append((tag, value)) 
     431     
     432    def load(self, file, progress_callback=None): 
     433        """ Load terms from a file. 
     434        """ 
     435        if isinstance(file, basestring): 
     436            file = open(file, "rb") 
     437        parser = OBOParser(file) 
     438        current = None 
     439        for event, value in parser.parse(progress_callback=progress_callback): 
     440            if event == "TAG_VALUE": 
     441                current.add_tag(*value) 
     442            elif event == "START_STANZA": 
     443                current = OBOObject(value) 
     444            elif event == "CLOSE_STANZA": 
     445                self.add_object(current) 
     446                current = None 
     447            elif event == "HEADER_TAG": 
     448                self.add_header_tag(*value) 
     449            elif event != "COMMENT": 
     450                raise Exception("Parse Error! Unknown parse event {0}".format(event)) 
     451             
     452        imports = [value for tag, value, in self.header_tags if tag == "import"] 
    398453         
    399454        while imports: 
    400455            url = imports.pop(0) 
    401             imported = self.parseOBOFile(open(url, "rb")) 
    402             ontology.update(imported) 
    403         return ontology 
    404          
     456            if uri not in self._resolved_imports: 
     457                imported = self.parse_file(open(url, "rb")) 
     458                ontology.update(imported) 
     459                self._resolved_imports.append(uri) 
     460                 
     461    def dump(self, file): 
     462        """ Dump the contents of the ontology to a .obo `file`. 
     463        """ 
     464        if isinstance(file, basestring): 
     465            file = open(file, "wb") 
     466             
     467        for key, value in self.header_tags: 
     468            file.write(key + ":" + value + "\n") 
     469        for object in self.objects: 
     470            file.write("\n") 
     471            file.write(object.format_stanza()) 
     472            file.write("\n") 
    405473     
    406474    def update(self, other): 
     
    414482                    pass  
    415483            else: 
    416                 self.addObject(term) 
     484                self.add_object(term) 
     485        self._invalid_cache_flag = True 
     486         
     487    def _cache_validate(self, force=False): 
     488        """ Update the relations cache if `self._invalid_cache` flag is set.  
     489        """ 
     490        if self._invalid_cache_flag or force: 
     491            self._cache_relations() 
     492             
     493    def _cache_relations(self): 
     494        """ Collect all relations from parent to a child and store it in 
     495        `self._related_to` member. 
     496         
     497        """ 
     498        related_to = defaultdict(list) 
     499        for obj in self.objects: 
     500            for rel_type, id in self.related_terms(obj): 
     501                term = self.term(id) 
     502                related_to[term].append((rel_type, obj)) 
    417503                 
    418     def _postLoadProcess(self): 
    419         for obj in self.objects: 
    420             pass 
    421      
     504        self._related_to = related_to 
     505        self._invalid_cache_flag = False 
     506         
    422507    def term(self, id): 
    423508        """ Return the OBOObject associated with this id. 
    424509        """ 
    425         if id in self.id2Term: 
    426             return self.id2Term[id] 
    427         else: 
    428             raise ValueError("Unknown term id: %s" % id) 
     510        if isinstance(id, basestring): 
     511            if id in self.id2term: 
     512                return self.id2term[id] 
     513            elif id in self.alt2id: 
     514                return self.id2term[self.alt2id[id]] 
     515            else: 
     516                raise ValueError("Unknown term id: %s" % id) 
     517        elif isinstance(id, OBOObject): 
     518            return id 
    429519         
    430520    def terms(self): 
    431521        """ Return all `Term` instances in the ontology. 
    432522        """ 
    433         return [obj for obj in self.objects if obj.stanzaType == "Term"] 
     523        return [obj for obj in self.objects if obj.stanza_type == "Term"] 
    434524     
    435525    def typedefs(self): 
    436526        """ Return all `Typedef` instances in the ontology. 
    437527        """ 
    438         return [obj for obj in self.objects if obj.stanzaType == "Typedef"] 
     528        return [obj for obj in self.objects if obj.stanza_type == "Typedef"] 
    439529     
    440530    def instances(self): 
    441531        """ Return all `Instance` instances in the ontology. 
    442532        """ 
    443         return [obj for obj in self.objects if obj.stanzaType == "Instance"] 
    444          
    445     def relatedTerms(self, term): 
     533        return [obj for obj in self.objects if obj.stanza_type == "Instance"] 
     534         
     535    def related_terms(self, term): 
    446536        """ Return a list of (rel_type, term_id) tuples where rel_type is 
    447537        relationship type (e.g. 'is_a', 'has_part', ...) and term_id is the 
    448538        id of the term in the relationship. 
     539         
    449540        """ 
    450541        term = self.term(term) if not isinstance(term, OBOObject) else term 
     
    455546        return related 
    456547             
    457     def toNetwork(self): 
    458         """ Return a orngNetwork instance constructed from this ontology 
    459         """ 
    460         edgeTypes = self.edgeTypes() 
     548    def to_network(self): 
     549        """ Return an Orange.network.Network instance constructed from 
     550        this ontology. 
     551         
     552        """ 
     553        edge_types = self.edge_types() 
    461554        terms = self.terms() 
    462555        import orngNetwork, orange 
    463556         
    464         network = orngNetwork.Network(len(terms), True, len(edgeTypes)) 
     557        network = orngNetwork.Network(len(terms), True, len(edge_types)) 
    465558        network.objects = dict([(term.id, i) for i, term in enumerate(terms)]) 
    466559         
    467560        edges = defaultdict(set) 
    468561        for term in self.terms(): 
    469 #            related = term.relatedTerms() 
    470             related = self.relatedTerms(term) 
     562            related = self.related_terms(term) 
    471563            for relType, relTerm in related: 
    472564                edges[(term.id, relTerm)].add(relType) 
     
    474566        edgeitems = edges.items() 
    475567        for (src, dst), eTypes in edgeitems: 
    476             network[src, dst] = [1 if e in eTypes else 0 for e in edgeTypes] 
     568            network[src, dst] = [1 if e in eTypes else 0 for e in edge_types] 
    477569             
    478570        domain = orange.Domain([orange.StringVariable("id"), 
     
    489581        domain = orange.Domain([orange.FloatVariable("u"), 
    490582                                orange.FloatVariable("v"), 
    491                                 orange.EnumVariable("relationship", values=list(edgeTypes)) 
     583                                orange.EnumVariable("relationship", values=list(edge_types)) 
    492584                                ], False) 
    493585         
     
    502594        network.optimization = None 
    503595        return network 
    504          
    505     def edgeTypes(self): 
     596     
     597    def to_networkx(self): 
     598        """ Return a NetworkX graph of this ontology 
     599        """ 
     600        raise NotImplementedError 
     601         
     602    def edge_types(self): 
    506603        """ Return a list of all edge types in the ontology 
    507604        """ 
    508         return [obj.id for obj in self.objects if obj.stanzaType == "Typedef"] 
    509          
    510     def extractSuperGraph(self, terms): 
    511         """ Return all super terms of terms up to the most general one. 
    512         """ 
    513         terms = [terms] if type(terms) == str else terms 
     605        return [obj.id for obj in self.objects if obj.stanza_type == "Typedef"] 
     606     
     607    def parent_edges(self, term): 
     608        """ Return a list of (rel_type, parent_term) tuples  
     609        """ 
     610        term = self.term(term) 
     611        parents = [] 
     612        for rel_type, parent in self.related_terms(term): 
     613            parents.append((rel_type, self.term(parent))) 
     614        return parents 
     615         
     616    def child_edges(self, term): 
     617        """ Return a list of (rel_type, source_term) tuples 
     618        """ 
     619        self._cache_validate() 
     620        term = self.term(term) 
     621        return self._related_to.get(term, []) 
     622         
     623         
     624    def super_terms(self, term): 
     625        """ Return a set of all super terms of `term` up to the most general one. 
     626        """ 
     627        terms = self.parent_terms(term) 
    514628        visited = set() 
    515629        queue = set(terms) 
     
    517631            term = queue.pop() 
    518632            visited.add(term) 
    519             queue.update(set(id for typeId, id in self[term].related) - visited) 
     633            queue.update(self.parent_terms(term) - visited) 
    520634        return visited 
     635     
     636    def sub_terms(self, term): 
     637        """ Return a set of all sub terms for `term`. 
     638        """ 
     639        terms = self.child_terms(term) 
     640        visited = set() 
     641        queue = set(terms) 
     642        while queue: 
     643            term = queue.pop() 
     644            visited.add(term) 
     645            queue.update(self.child_terms(term) - visited) 
     646        return visited 
     647     
     648    def child_terms(self, term): 
     649        """ Return a set of all child terms for this `term`. 
     650        """ 
     651        self._cache_validate() 
     652        term = self.term(term) 
     653        children = [] 
     654        for rel_type, term in self.child_edges(term): 
     655            children.append(term) 
     656        return set(children) 
     657         
     658     
     659    def parent_terms(self, term): 
     660        """ Return a set of all parent terms for this `term` 
     661        """ 
     662        term = self.term(term) 
     663        parents = [] 
     664        for rel_type, id in self.parent_edges(term): #term.related_objects(): 
     665            parents.append(self.term(id)) 
     666        return set(parents) 
     667     
     668    def relations(self): 
     669        """ Return a list of all relations in the ontology. 
     670        """ 
     671        relations = [] 
     672        for obj in self.objects: 
     673            for type_id, id in  obj.related: 
     674                target_term = self.term(id) 
     675            relations.append((obj, type_id, target_term)) 
     676        return relations 
    521677     
    522678    def __len__(self): 
     
    527683     
    528684    def __contains__(self, obj): 
    529         return obj in self.id2Term 
    530      
    531 def foundryOntologies(): 
     685        if isinstance(obj, basestring): 
     686            return obj in self.id2term 
     687        else: 
     688            return obj in self.objects 
     689     
     690    def __getitem__(self, key): 
     691        return self.id2term[key] 
     692     
     693    def has_key(self, key): 
     694        return self.id2tem.has_key(key) 
     695     
     696     
     697def load(file): 
     698    """ Load an ontology from a .obo file 
     699    """ 
     700    return OBOOntology(file) 
     701     
     702     
     703def foundry_ontologies(): 
    532704    """ List ontologies available from the OBOFoundry website 
    533705    (`http://www.obofoundry.org/`_)  
    534706    Example:: 
    535         >>> foundryOntologies() 
     707        >>> foundry_ontologies() 
    536708        [('Biological process', 'http://obo.cvs.sourceforge.net/*checkout*/obo/obo/ontology/genomic-proteomic/gene_ontology_edit.obo'), ... 
    537709     
     
    549721id: FOO:001 
    550722name: bar 
    551     ''' 
    552     term = OBOObject.parseStanza(stanza) 
     723''' 
     724    from StringIO import StringIO 
     725    seinfeld = StringIO(""" 
     726[Typedef] 
     727id: parent 
     728 
     729[Typedef] 
     730id: child 
     731inverse_of: parent ! not actually used yet 
     732 
     733[Term] 
     734id: 001 
     735name: George 
     736 
     737[Term] 
     738id: 002 
     739name: Estelle 
     740relationship: parent 001 ! George 
     741 
     742[Term] 
     743id: 003 
     744name: Frank 
     745relationship: parent 001 ! George 
     746 
     747""") # TODO: fill the ontology with all characters 
     748    term = OBOObject.parse_stanza(stanza) 
     749     
     750    seinfeld = OBOOntology(seinfeld) 
     751    print seinfeld.child_edges("001") 
     752     
    553753    doctest.testmod(extraglobs={"stanza": stanza, "term": term}, optionflags=doctest.ELLIPSIS) 
    554754         
Note: See TracChangeset for help on using the changeset viewer.