Changeset 1742:fa3a9e4af7e6 in orange-bioinformatics


Ignore:
Timestamp:
04/12/13 12:24:17 (12 months ago)
Author:
Ales Erjavec <ales.erjavec@…>
Branch:
default
Message:

Code style and docstring fixes.

Files:
3 added
2 edited

Legend:

Unmodified
Added
Removed
  • _bioinformatics/obiOntology.py

    r1636 r1742  
    1 """\ 
     1""" 
     2=========== 
    23obiOntology 
    3 ----------- 
    4  
    5 Python module for manipulating OBO Ontology files (http://www.obofoundry.org/) 
    6  
    7 You can construct an ontology from scratch with custom terms :: 
     4=========== 
     5 
     6This module provides an interface for parsing, creating and manipulating of 
     7OBO ontologies (http://www.obofoundry.org/) 
     8 
     9Construct an ontology from scratch with custom terms :: 
    810 
    911    >>> term = OBOObject("Term", id="foo:bar", name="Foo bar") 
     
    1214    id: foo:bar 
    1315    name: Foo bar 
    14      
     16 
    1517    >>> ontology = OBOOntology() 
    1618    >>> ontology.add_object(term) 
     
    2628    name: Foo bar 
    2729    <BLANKLINE> 
    28      
    29 To load an ontology from a file pass the file or filename to the OBOOntology 
    30 constructor or call its load method :: 
     30 
     31To load an ontology from a file pass the file or filename to the 
     32:class:`OBOOntology` constructor or call its load method :: 
    3133 
    3234    >>> buffer.seek(0) # rewind 
    3335    >>> ontology = OBOOntology(buffer) 
    34     >>> # Or 
     36    >>> # Or equivalently 
    3537    >>> buffer.seek(0) # rewind 
    3638    >>> ontology = OBOOntology() 
    3739    >>> ontology.load(buffer) 
    38      
    39 See OBOOntology docstring for more details. 
     40 
     41 
     42See http://www.geneontology.org/GO.format.obo-1_2.shtml for the definition 
     43of the .obo file format. 
    4044 
    4145""" 
    4246 
    43 from itertools import chain 
     47import re 
     48import urllib2 
     49import warnings 
    4450from collections import defaultdict 
    4551from StringIO import StringIO 
    46 import itertools 
    47 import re 
    48  
     52 
     53 
     54#: These are builtin OBO objects present in any ontology by default. 
    4955BUILTIN_OBO_OBJECTS = [ 
    5056"""[Typedef] 
     
    5359range: OBO:TERM_OR_TYPE 
    5460domain: OBO:TERM_OR_TYPE 
    55 definition: The basic subclassing relationship [OBO:defs]""" 
    56 , 
     61definition: The basic subclassing relationship [OBO:defs]""", 
     62 
    5763"""[Typedef] 
    5864id: disjoint_from 
     
    6066range: OBO:TERM 
    6167domain: OBO:TERM 
    62 definition: Indicates that two classes are disjoint [OBO:defs]""" 
    63 , 
     68definition: Indicates that two classes are disjoint [OBO:defs]""", 
     69 
    6470"""[Typedef] 
    6571id: instance_of 
     
    6773range: OBO:TERM 
    6874domain: OBO:INSTANCE 
    69 definition: Indicates the type of an instance [OBO:defs]""" 
    70 , 
     75definition: Indicates the type of an instance [OBO:defs]""", 
     76 
    7177"""[Typedef] 
    7278id: inverse_of 
     
    7480range: OBO:TYPE 
    7581domain: OBO:TYPE 
    76 definition: Indicates that one relationship type is the inverse of another [OBO:defs]""" 
    77 , 
     82definition: Indicates that one relationship type is the inverse of another [OBO:defs]""", 
     83 
    7884"""[Typedef] 
    7985id: union_of 
     
    8187range: OBO:TERM 
    8288domain: OBO:TERM 
    83 definition: Indicates that a term is the union of several others [OBO:defs]""" 
    84 , 
     89definition: Indicates that a term is the union of several others [OBO:defs]""", 
     90 
    8591"""[Typedef] 
    8692id: intersection_of 
     
    9096definition: Indicates that a term is the intersection of several others [OBO:defs]""" 
    9197] 
    92      
     98 
     99 
    93100def _split_and_strip(string, sep): 
     101    """ 
     102    Split the `string` by separator `sep` in to two parts and strip 
     103    any whitespace between the inner parts. 
     104 
     105    """ 
    94106    head, tail = string.split(sep, 1) 
    95107    return head.rstrip(" "), tail.lstrip(" ") 
     
    97109 
    98110class OBOObject(object): 
    99     """ Represents a generic OBO object (e.g. Term, Typedef, Instance, ...) 
     111    """ 
     112    Represents a generic OBO object (e.g. Term, Typedef, Instance, ...) 
    100113    Example:: 
     114 
    101115        >>> term = OBOObject(stanza_type="Term", id="FOO:001", name="bar") 
     116 
     117        >>> term = OBOObject( 
     118        ...     stanza_type="Term", 
     119        ...     id="FOO:001", 
     120        ...     name="bar", 
     121        ...     def_="Example definition { modifier=frob } ! Comment" 
     122        ... ) 
     123        ... 
     124 
     125    An alternative way to specify tags in the constructor:: 
     126 
     127        >>> term = OBOObject(stanza_type="Term", id="FOO:001", name="bar", 
     128        ...                  def_=("Example definition", 
     129        ...                        [("modifier", "frob")], 
     130        ...                        "Comment")) 
     131        ... 
     132 
     133    .. note:: 
     134        Note the use of ``def_`` to define the 'def' tag. This is to 
     135        avoid the name clash with the python's ``def`` keyword. 
     136 
    102137    """ 
    103138    def __init__(self, stanza_type="Term", **kwargs): 
    104         """ Init from keyword arguments. 
    105         Example:: 
    106             >>> term = OBOObject(stanza_type="Term", id="FOO:001", name="bar", def_="Example definition { modifier=frob } ! Comment") 
    107             >>> term = OBOObject(stanza_type="Term", id="FOO:001", name="bar", def_=("Example definition", [("modifier", "frob")], "Comment")) 
    108             >>> term = OBOObject(stanza_type="Term", id="FOO:001", name="bar", def_=("Example definition", [("modifier", "frob")])) # without the comment 
    109             >>> term = OBOObject(stanza_type="Term", id="FOO:001", name="bar", def_=("Example definition",)) # without the modifiers and comment 
     139        """ 
     140        Initialize from keyword arguments. 
    110141        """ 
    111142        self.stanza_type = stanza_type 
    112          
     143 
    113144        self.modifiers = [] 
    114145        self.comments = [] 
    115146        self.tag_values = [] 
    116147        self.values = {} 
    117          
    118         sorted_tags = sorted(kwargs.iteritems(), key=lambda key_val: chr(1) if key_val[0] == "id" else key_val[0]) 
     148 
     149        sorted_tags = sorted( 
     150            kwargs.iteritems(), 
     151            key=lambda key_val: chr(1) if key_val[0] == "id" else key_val[0] 
     152        ) 
     153 
    119154        for tag, value in sorted_tags: 
    120155            if isinstance(value, basestring): 
    121                 tag, value, modifiers, comment = self.parse_tag_value(self.name_demangle(tag), value) 
     156                tag, value, modifiers, comment = \ 
     157                    self.parse_tag_value(self.name_demangle(tag), value) 
    122158            elif isinstance(value, tuple): 
    123                 tag, value, modifiers, comment = ((self.name_demangle(tag),) + value + (None, None))[:4] 
     159                tag, value, modifiers, comment = \ 
     160                    ((self.name_demangle(tag),) + value + (None, None))[:4] 
    124161            self.add_tag(tag, value, modifiers, comment) 
    125          
     162 
    126163        self.related = set() 
    127 #        self.related_to = set() 
    128              
     164 
    129165    @property 
    130166    def is_annonymous(self): 
     167        """ 
     168        Is this object anonymous. 
     169        """ 
    131170        value = self.get_value("is_annonymous") 
    132171        return bool(value) 
    133      
     172 
    134173    def name_mangle(self, tag): 
    135         """ Mangle tag name if it conflicts with python keyword 
     174        """ 
     175        Mangle tag name if it conflicts with python keyword. 
     176 
    136177        Example:: 
     178 
    137179            >>> term.name_mangle("def"), term.name_mangle("class") 
    138180            ('def_', 'class_') 
     181 
    139182        """ 
    140183        if tag in ["def", "class", "in", "not"]: 
     
    142185        else: 
    143186            return tag 
    144          
     187 
    145188    def name_demangle(self, tag): 
    146         """ Reverse of name_mangle 
     189        """ 
     190        Reverse of `name_mangle`. 
    147191        """ 
    148192        if tag in ["def_", "class_", "in_", "not_"]: 
     
    150194        else: 
    151195            return tag 
    152          
     196 
    153197    def add_tag(self, tag, value, modifiers=None, comment=None): 
    154         """ Add `tag`, `value` pair to the object with optional modifiers and 
     198        """ 
     199        Add `tag`, `value` pair to the object with optional modifiers and 
    155200        comment. 
     201 
    156202        Example:: 
     203 
    157204            >>> term = OBOObject("Term") 
    158205            >>> term.add_tag("id", "FOO:002", comment="This is an id") 
     
    160207            [Term] 
    161208            id: FOO:002 ! This is an id 
    162               
    163         """ 
    164         tag = intern(tag) # a small speed and memory benefit 
     209 
     210        """ 
     211        tag = intern(tag)  # a small speed and memory benefit 
    165212        self.tag_values.append((tag, value)) 
    166213        self.modifiers.append(modifiers) 
    167214        self.comments.append(comment) 
    168215        self.values.setdefault(tag, []).append(value) 
    169          
     216 
    170217        #  TODO: fix multiple tags grouping 
    171218        if hasattr(self, tag): 
     
    176223        else: 
    177224            setattr(self, self.name_mangle(tag), value) 
    178              
     225 
    179226    def update(self, other): 
    180         """ Update the term with tag value pairs from `other`  
    181         (a OBOObject instance). The tag value pairs are appended 
    182         to the end except for the `id` tag. 
    183         """  
    184         for (tag, value), modifiers, comment in zip(other.tag_values, other.modifiers, other.comments): 
     227        """ 
     228        Update the term with tag value pairs from `other` 
     229        (:class:`OBOObject`). The tag value pairs are appended to the 
     230        end except for the `id` tag. 
     231 
     232        """ 
     233        for (tag, value), modifiers, comment in \ 
     234                zip(other.tag_values, other.modifiers, other.comments): 
    185235            if tag != "id": 
    186236                self.add_tag(tag, value, modifiers, comment) 
    187          
     237 
    188238    def get_value(self, tag, group=True): 
    189239        if group: 
     
    196246            else: 
    197247                raise ValueError("No value for tag: %s" % tag) 
    198          
     248 
    199249    def tag_count(self): 
    200         """ Retrun the number of tags in this object 
     250        """ 
     251        Return the number of tags in this object. 
    201252        """ 
    202253        return len(self.tag_values) 
    203      
     254 
    204255    def tags(self): 
    205         """ Retrun an iterator over the (tag, value) pairs. 
     256        """ 
     257        Return an iterator over the (tag, value) pairs. 
    206258        """ 
    207259        for i in range(self.tag_count()): 
    208260            yield self.tag_values[i] + (self.modifiers[i], self.comments[i]) 
    209          
     261 
    210262    def format_single_tag(self, index): 
    211         """Return a formated string representing index-th tag pair value 
     263        """ 
     264        Return a formated string representing index-th tag pair value 
     265 
    212266        Example:: 
    213             >>> term = OBOObject("Term", id="FOO:001", name="bar", def_="Example definition {modifier=frob} ! Comment") 
     267 
     268            >>> term = OBOObject( 
     269            ...     "Term", id="FOO:001", name="bar", 
     270            ...      def_="Example definition {modifier=frob} ! Comment") 
     271            ... 
    214272            >>> term.format_single_tag(0) 
    215273            'id: FOO:001' 
    216274            >>> term.format_single_tag(1) 
    217275            'def: Example definition { modifier=frob } ! Comment' 
     276 
     277        .. 
     278            Why by index, and not by tag? 
     279 
    218280        """ 
    219281        tag, value = self.tag_values[index] 
     
    226288            res.append("! " + comment) 
    227289        return " ".join(res) 
    228      
     290 
    229291    def format_stanza(self): 
    230         """ Return a string stanza representation of this object  
     292        """ 
     293        Return a string stanza representation of this object. 
    231294        """ 
    232295        stanza = ["[%s]" % self.stanza_type] 
     
    234297            stanza.append(self.format_single_tag(i)) 
    235298        return "\n".join(stanza) 
    236              
    237     @classmethod      
     299 
     300    @classmethod 
    238301    def parse_stanza(cls, stanza): 
    239         r""" Parse and return an OBOObject instance from a single stanza. 
     302        r""" 
     303        Parse and return an OBOObject instance from a stanza string. 
     304 
    240305        Example:: 
     306 
    241307            >>> term = OBOObject.parse_stanza("[Term]\nid: FOO:001\nname:bar") 
    242308            >>> print term.id, term.name 
    243309            FOO:001 bar 
    244              
     310 
    245311        """ 
    246312        lines = stanza.splitlines() 
     
    250316            if ":" in line: 
    251317                tag_values.append(cls.parse_tag_value(line)) 
    252          
     318 
    253319        obo = OBOObject(stanza_type) 
    254         for i, (tag, value, modifiers, comment) in enumerate(tag_values): 
    255 #            print tag, value, modifiers, comment 
     320        for tag, value, modifiers, comment in tag_values: 
    256321            obo.add_tag(tag, value, modifiers, comment) 
    257322        return obo 
    258      
    259          
     323 
    260324    @classmethod 
    261325    def parse_tag_value_1(cls, tag_value_pair, *args): 
    262         """ Parse and return a four-tuple containing a tag, value, a list of modifier pairs, comment. 
    263         If no modifiers or comments are present the corresponding entries will be None. 
    264          
     326        """ 
     327        Parse and return a four-tuple containing a tag, value, a 
     328        list of modifier pairs, comment. If no modifiers or comments 
     329        are present the corresponding entries will be ``None``. 
     330 
    265331        Example:: 
     332 
    266333            >>> OBOObject.parse_tag_value("foo: bar {modifier=frob} ! Comment") 
    267334            ('foo', 'bar', 'modifier=frob', 'Comment') 
    268335            >>> OBOObject.parse_tag_value("foo: bar") 
    269336            ('foo', 'bar', None, None) 
    270             >>> #  Can also pass tag, value pair already split    
     337            >>> # Can also pass tag, value pair already split 
    271338            >>> OBOObject.parse_tag_value("foo", "bar {modifier=frob} ! Comment") 
    272339            ('foo', 'bar', 'modifier=frob', 'Comment') 
     340 
    273341        """ 
    274342        if args and ":" not in tag_value_pair: 
     
    277345            tag, rest = _split_and_strip(tag_value_pair, ":") 
    278346        value, modifiers, comment = None, None, None 
    279          
     347 
    280348        if "{" in rest: 
    281349            value, rest = _split_and_strip(rest, "{",) 
     
    288356        if value is None: 
    289357            value = rest 
    290              
     358 
    291359        if modifiers is not None: 
    292             modifiers = modifiers #TODO: split modifiers in a list 
    293              
     360            modifiers = modifiers  # TODO: split modifiers in a list 
     361 
    294362        return tag, value, modifiers, comment 
    295      
     363 
    296364    _RE_TAG_VALUE = re.compile(r"^(?P<tag>.+?[^\\])\s*:\s*(?P<value>.+?)\s*(?P<modifiers>[^\\]{.+?[^\\]})?\s*(?P<comment>[^\\]!.*)?$") 
    297365    _RE_VALUE = re.compile(r"^\s*(?P<value>.+?)\s*(?P<modifiers>[^\\]{.+?[^\\]})?\s*(?P<comment>[^\\]!.*)?$") 
    298      
     366 
    299367    @classmethod 
    300368    def parse_tag_value(cls, tag_value_pair, arg=None): 
    301         """ Parse and return a four-tuple containing a tag, value, a list of modifier pairs, comment. 
    302         If no modifiers or comments are present the corresponding entries will be None. 
    303          
     369        """ 
     370        Parse and return a four-tuple containing a tag, value, a list 
     371        of modifier pairs, comment. If no modifiers or comments are 
     372        present the corresponding entries will be None. 
     373 
    304374        Example:: 
    305375            >>> OBOObject.parse_tag_value("foo: bar {modifier=frob} ! Comment") 
     
    307377            >>> OBOObject.parse_tag_value("foo: bar") 
    308378            ('foo', 'bar', None, None) 
    309             >>> #  Can also pass tag, value pair already split    
     379            >>> #  Can also pass tag, value pair already split 
    310380            >>> OBOObject.parse_tag_value("foo", "bar {modifier=frob} ! Comment") 
    311381            ('foo', 'bar', 'modifier=frob', 'Comment') 
    312              
     382 
    313383        .. warning: This function assumes comment an modifiers are prefixed 
    314             with a whitespace i.e. 'tag: bla! comment' will be parsed incorrectly! 
    315         """ 
    316         if arg is not None: # tag_value_pair is actually a tag only 
     384            with a whitespace i.e. 'tag: bla! comment' will be parsed 
     385            incorrectly! 
     386 
     387        """ 
     388        if arg is not None:  # tag_value_pair is actually a tag only 
    317389            tag = tag_value_pair 
    318             value, modifiers, comment =  cls._RE_VALUE.findall(arg)[0] 
     390            value, modifiers, comment = cls._RE_VALUE.findall(arg)[0] 
    319391        else: 
    320             tag, value, modifiers, comment = cls._RE_TAG_VALUE.findall(tag_value_pair)[0] 
     392            tag, value, modifiers, comment = \ 
     393                cls._RE_TAG_VALUE.findall(tag_value_pair)[0] 
    321394        none_if_empyt = lambda val: None if not val.strip() else val.strip() 
    322395        modifiers = modifiers.strip(" {}") 
     
    324397        return (none_if_empyt(tag), none_if_empyt(value), 
    325398                none_if_empyt(modifiers), none_if_empyt(comment)) 
    326           
     399 
    327400    def related_objects(self): 
    328         """ Return a list of tuple pairs where the first element is relationship (typedef id) 
    329         is and the second object id whom the relationship applies to. 
    330         """ 
    331         result = [(type_id, id) for type_id in ["is_a"] for id in self.values.get(type_id, [])] ##TODO add other defined Typedef ids 
    332         result = result + [tuple(r.split(None, 1)) for r in self.values.get("relationship", [])] 
     401        """ 
     402        Return a list of tuple pairs where the first element is 
     403        relationship (typedef id) and the second object id whom the 
     404        relationship applies to. 
     405 
     406        """ 
     407        result = [(type_id, id) 
     408                  for type_id in ["is_a"]  # TODO add other defined Typedef ids 
     409                  for id in self.values.get(type_id, [])] 
     410 
     411        result = result + [tuple(r.split(None, 1)) 
     412                           for r in self.values.get("relationship", [])] 
    333413        return result 
    334414 
    335415    def __repr__(self): 
    336         """ Return a string representation of the object in OBO format 
     416        """ 
     417        Return a string representation of the object in OBO format 
    337418        """ 
    338419        return self.format_stanza() 
    339420 
    340421    def __iter__(self): 
    341         """ Iterates over sub terms 
    342         """ 
    343         for type_id, id in self.related_objects(): 
    344             yield (type_id, id) 
    345          
    346          
     422        """ 
     423        Iterates over sub terms 
     424        """ 
     425        return iter(self.related_objects()) 
     426 
     427 
    347428class Term(OBOObject): 
     429    """ 
     430    A 'Term' object in the ontology. 
     431    """ 
    348432    def __init__(self, *args, **kwargs): 
    349433        OBOObject.__init__(self, "Term", *args, **kwargs) 
    350434 
     435 
    351436class Typedef(OBOObject): 
     437    """ 
     438    A 'Typedef' object in the ontology. 
     439    """ 
    352440    def __init__(self, *args, **kwargs): 
    353441        OBOObject.__init__(self, "Typedef", *args, **kwargs) 
    354442 
     443 
    355444class Instance(OBOObject): 
     445    """ 
     446    An 'Instance' object in the ontology 
     447    """ 
    356448    def __init__(self, *args, **kwargs): 
    357449        OBOObject.__init__(self, "Instance", *args, **kwargs) 
    358450 
    359451 
    360 import re 
    361  
    362452class OBOParser(object): 
    363453    r""" A simple parser for .obo files (inspired by xml.dom.pulldom) 
    364      
     454 
    365455    Example:: 
     456 
    366457        >>> from StringIO import StringIO 
    367         >>> file = StringIO("header_tag: header_value\n[Term]\nid: FOO { modifier=bar } ! comment\n\n")  
     458        >>> file = StringIO("header_tag: header_value\n[Term]\nid: " 
     459        ...                 "FOO { modifier=bar } ! comment\n\n") 
     460        ... 
    368461        >>> parser = OBOParser(file) 
    369462        >>> for event, value in parser: 
    370463        ...     print event, value 
    371         ...      
     464        ... 
    372465        HEADER_TAG ['header_tag', 'header_value'] 
    373466        START_STANZA Term 
    374467        TAG_VALUE ('id', 'FOO', 'modifier=bar', 'comment') 
    375468        CLOSE_STANZA None 
    376                  
     469 
    377470    """ 
    378471    def __init__(self, file): 
    379472        self.file = file 
    380          
     473 
    381474    def parse(self, progress_callback=None): 
    382         """ Parse the file and yield parse events. 
    383          
    384         .. TODO: List events and values 
     475        """ 
     476        Parse the file and yield parse events. 
     477 
     478        .. todo List events and values 
     479 
    385480        """ 
    386481        data = self.file.read() 
     
    390485            if line.strip(): 
    391486                yield "HEADER_TAG", line.split(": ", 1) 
    392                  
     487 
    393488        current = None 
    394489        #  For speed make these functions local 
     
    396491        endswith = str.endswith 
    397492        parse_tag_value = OBOObject.parse_tag_value 
    398          
     493 
    399494        for line in body.splitlines(): 
    400 #            line = line.strip() 
    401495            if startswith(line, "[") and endswith(line, "]"): 
    402496                yield "START_STANZA", line.strip("[]") 
     
    406500            elif line: 
    407501                yield "TAG_VALUE", parse_tag_value(line) 
    408             else: # empty line is the end of a term 
     502            else:  # empty line is the end of a term 
    409503                yield "CLOSE_STANZA", None 
    410504                current = None 
    411505        if current is not None: 
    412506            yield "CLOSE_STANZA", None 
    413      
     507 
    414508    def __iter__(self): 
    415         """ Iterate over parse events (same as parse()) 
    416         """ 
    417         return self.parse()  
    418          
    419          
     509        """ 
     510        Iterate over parse events (same as parse()) 
     511        """ 
     512        return self.parse() 
     513 
     514 
    420515class OBOOntology(object): 
    421     """ The main ontology object. 
    422     """ 
    423      
     516    """ 
     517    An class for representing OBO ontologies. 
     518 
     519    :param file-like file: 
     520        A optional file like object describing the ontology in obo format. 
     521 
     522    """ 
     523 
    424524    BUILTINS = BUILTIN_OBO_OBJECTS 
     525 
    425526    def __init__(self, file=None): 
    426         """ Init an ontology instance from a file like object (.obo format) 
    427          
     527        """ 
     528        Initialize an ontology instance from a file like object (.obo format) 
    428529        """ 
    429530        self.objects = [] 
     
    434535        self._invalid_cache_flag = False 
    435536        self._related_to = {} 
    436          
     537 
    437538        # First load the built in OBO objects 
    438539        builtins = StringIO("\n" + "\n\n".join(self.BUILTINS) + "\n") 
     
    440541        if file: 
    441542            self.load(file) 
    442          
     543 
    443544    def add_object(self, object): 
    444         """ Add OBOObject instance to this ontology. 
     545        """ 
     546        Add an :class:`OBOObject` instance to this ontology. 
    445547        """ 
    446548        if object.id in self.id2term: 
    447             raise ValueError("OBOObject with id: %s already in the ontology" % object.id) 
     549            raise ValueError("OBOObject with id: %s already in " 
     550                             "the ontology" % object.id) 
    448551        self.objects.append(object) 
    449552        self.id2term[object.id] = object 
    450553        self._invalid_cache_flag = True 
    451          
     554 
    452555    def add_header_tag(self, tag, value): 
    453         """ Add header tag, value pair to this ontology 
     556        """ 
     557        Add header tag, value pair to this ontology. 
    454558        """ 
    455559        self.header_tags.append((tag, value)) 
    456      
     560 
    457561    def load(self, file, progress_callback=None): 
    458         """ Load terms from a file. 
     562        """ 
     563        Load terms from a file. 
     564 
     565        :param file-like file: 
     566            A file-like like object describing the ontology in obo format. 
     567        :param function progress_callback: 
     568            An optional function callback to report on the progress. 
     569 
    459570        """ 
    460571        if isinstance(file, basestring): 
    461572            file = open(file, "rb") 
     573 
    462574        parser = OBOParser(file) 
    463575        current = None 
     
    473585                self.add_header_tag(*value) 
    474586            elif event != "COMMENT": 
    475                 raise Exception("Parse Error! Unknown parse event {0}".format(event)) 
    476              
    477         imports = [value for tag, value, in self.header_tags if tag == "import"] 
    478          
    479         while imports: 
    480             url = imports.pop(0) 
    481             if uri not in self._resolved_imports: 
    482                 imported = self.parse_file(open(url, "rb")) 
    483                 ontology.update(imported) 
    484                 self._resolved_imports.append(uri) 
    485                  
     587                raise Exception("Parse Error! Unknown parse " 
     588                                "event {0}".format(event)) 
     589 
     590        imports = [value for tag, value in self.header_tags 
     591                   if tag == "import"] 
     592 
     593        if imports: 
     594            warnings.warn("Import header tags are not supported") 
     595 
     596#        while imports: 
     597#            url = imports.pop(0) 
     598#            if uri not in self._resolved_imports: 
     599#                imported = self.parse_file(open(url, "rb")) 
     600#                ontology.update(imported) 
     601#                self._resolved_imports.append(uri) 
     602 
    486603    def dump(self, file): 
    487         """ Dump the contents of the ontology to a .obo `file`. 
     604        """ 
     605        Dump the contents of the ontology to a `file` in .obo format. 
     606 
     607        :param file-like file: 
     608            A file like object. 
     609 
    488610        """ 
    489611        if isinstance(file, basestring): 
    490612            file = open(file, "wb") 
    491              
     613 
    492614        for key, value in self.header_tags: 
    493615            file.write(key + ": " + value + "\n") 
    494              
     616 
    495617        # Skip the builtins 
    496618        for object in self.objects[len(self.BUILTINS):]: 
     
    498620            file.write(object.format_stanza()) 
    499621            file.write("\n") 
    500      
     622 
    501623    def update(self, other): 
    502         """ Update this ontology with the terms from another.  
     624        """ 
     625        Update this ontology with the terms from `other`. 
    503626        """ 
    504627        for term in other: 
     
    506629                if not term.is_annonymous: 
    507630                    self.term(term.id).update(term) 
    508                 else: # Do nothing 
    509                     pass  
     631                else:  # Do nothing 
     632                    pass 
    510633            else: 
    511634                self.add_object(term) 
    512635        self._invalid_cache_flag = True 
    513          
     636 
    514637    def _cache_validate(self, force=False): 
    515         """ Update the relations cache if `self._invalid_cache` flag is set.  
     638        """ 
     639        Update the relations cache if `self._invalid_cache` flag is set. 
    516640        """ 
    517641        if self._invalid_cache_flag or force: 
    518642            self._cache_relations() 
    519              
     643 
    520644    def _cache_relations(self): 
    521         """ Collect all relations from parent to a child and store it in 
     645        """ 
     646        Collect all relations from parent to a child and store it in 
    522647        `self._related_to` member. 
    523          
     648 
    524649        """ 
    525650        related_to = defaultdict(list) 
     
    528653                term = self.term(id) 
    529654                related_to[term].append((rel_type, obj)) 
    530                  
     655 
    531656        self._related_to = related_to 
    532657        self._invalid_cache_flag = False 
    533          
     658 
    534659    def term(self, id): 
    535         """ Return the OBOObject associated with this id. 
     660        """ 
     661        Return the :class:`OBOObject` associated with this id. 
     662 
     663        :param str id: 
     664            Term id string. 
     665 
    536666        """ 
    537667        if isinstance(id, basestring): 
     
    544674        elif isinstance(id, OBOObject): 
    545675            return id 
    546          
     676 
    547677    def terms(self): 
    548         """ Return all `Term` instances in the ontology. 
     678        """ 
     679        Return all :class:`Term` instances in the ontology. 
    549680        """ 
    550681        return [obj for obj in self.objects if obj.stanza_type == "Term"] 
    551      
     682 
    552683    def term_by_name(self, name): 
    553         """ Return the term with name ``name``.  
     684        """ 
     685        Return the term with name `name`. 
    554686        """ 
    555687        terms = [t for t in self.terms() if t.name == name] 
     
    557689            raise ValueError("Unknown term name: %r" % name) 
    558690        return terms[0] 
    559      
     691 
    560692    def typedefs(self): 
    561         """ Return all `Typedef` instances in the ontology. 
     693        """ 
     694        Return all :class:`Typedef` instances in the ontology. 
    562695        """ 
    563696        return [obj for obj in self.objects if obj.stanza_type == "Typedef"] 
    564      
     697 
    565698    def instances(self): 
    566         """ Return all `Instance` instances in the ontology. 
     699        """ 
     700        Return all :class:`Instance` instances in the ontology. 
    567701        """ 
    568702        return [obj for obj in self.objects if obj.stanza_type == "Instance"] 
    569          
     703 
    570704    def related_terms(self, term): 
    571         """ Return a list of (rel_type, term_id) tuples where rel_type is 
    572         relationship type (e.g. 'is_a', 'has_part', ...) and term_id is the 
    573         id of the term in the relationship. 
    574          
     705        """ 
     706        Return a list of (`rel_type`, `term_id`) tuples where `rel_type` is 
     707        relationship type (e.g. 'is_a', 'has_part', ...) and `term_id` is 
     708        the id of the term in the relationship. 
     709 
    575710        """ 
    576711        term = self.term(term) if not isinstance(term, OBOObject) else term 
    577         related = [(tag, value) for tag in ["is_a"] for value in term.values.get(tag, [])] #TODO: add other typedef ids 
     712        related = [(tag, value) 
     713                   for tag in ["is_a"]  # TODO: add other typedef ids 
     714                   for value in term.values.get(tag, [])] 
    578715        relationships = term.values.get("relationship", []) 
    579716        for rel in relationships: 
    580717            related.append(tuple(rel.split(None, 1))) 
    581718        return related 
    582          
     719 
    583720    def edge_types(self): 
    584         """ Return a list of all edge types in the ontology 
     721        """ 
     722        Return a list of all edge types in the ontology. 
    585723        """ 
    586724        return [obj.id for obj in self.objects if obj.stanza_type == "Typedef"] 
    587      
     725 
    588726    def parent_edges(self, term): 
    589         """ Return a list of (rel_type, parent_term) tuples  
     727        """ 
     728        Return a list of (rel_type, parent_term) tuples. 
    590729        """ 
    591730        term = self.term(term) 
     
    594733            parents.append((rel_type, self.term(parent))) 
    595734        return parents 
    596          
     735 
    597736    def child_edges(self, term): 
    598         """ Return a list of (rel_type, source_term) tuples 
     737        """ 
     738        Return a list of (rel_type, source_term) tuples. 
    599739        """ 
    600740        self._cache_validate() 
    601741        term = self.term(term) 
    602742        return self._related_to.get(term, []) 
    603          
    604          
     743 
    605744    def super_terms(self, term): 
    606         """ Return a set of all super terms of `term` up to the most general one. 
     745        """ 
     746        Return a set of all super terms of `term` up to the most general one. 
    607747        """ 
    608748        terms = self.parent_terms(term) 
     
    614754            queue.update(self.parent_terms(term) - visited) 
    615755        return visited 
    616      
     756 
    617757    def sub_terms(self, term): 
    618         """ Return a set of all sub terms for `term`. 
     758        """ 
     759        Return a set of all sub terms for `term`. 
    619760        """ 
    620761        terms = self.child_terms(term) 
     
    626767            queue.update(self.child_terms(term) - visited) 
    627768        return visited 
    628      
     769 
    629770    def child_terms(self, term): 
    630         """ Return a set of all child terms for this `term`. 
     771        """ 
     772        Return a set of all child terms for this `term`. 
    631773        """ 
    632774        self._cache_validate() 
     
    636778            children.append(term) 
    637779        return set(children) 
    638          
     780 
    639781    def parent_terms(self, term): 
    640         """ Return a set of all parent terms for this `term` 
     782        """ 
     783        Return a set of all parent terms for this `term` 
    641784        """ 
    642785        term = self.term(term) 
    643786        parents = [] 
    644         for rel_type, id in self.parent_edges(term): #term.related_objects(): 
     787        for rel_type, id in self.parent_edges(term): 
    645788            parents.append(self.term(id)) 
    646789        return set(parents) 
    647      
     790 
    648791    def relations(self): 
    649         """ Return a list of all relations in the ontology. 
     792        """ 
     793        Return a list of all relations in the ontology. 
    650794        """ 
    651795        relations = [] 
     
    655799            relations.append((obj, type_id, target_term)) 
    656800        return relations 
    657      
     801 
    658802    def __len__(self): 
    659803        return len(self.objects) 
    660      
     804 
    661805    def __iter__(self): 
    662806        return iter(self.objects) 
    663      
     807 
    664808    def __contains__(self, obj): 
    665809        if isinstance(obj, basestring): 
     
    667811        else: 
    668812            return obj in self.objects 
    669      
     813 
    670814    def __getitem__(self, key): 
    671815        return self.id2term[key] 
    672      
     816 
    673817    def has_key(self, key): 
    674         return self.id2tem.has_key(key) 
    675      
     818        return key in self.id2term 
     819 
    676820    def traverse_bf(self, term): 
    677         """ BF traverse of the ontology down from term. 
     821        """ 
     822        BF traverse of the ontology down from term. 
    678823        """ 
    679824        queue = list(self.child_terms(term)) 
     
    682827            queue.extend(self.child_terms(term)) 
    683828            yield term 
    684                      
     829 
    685830    def traverse_df(self, term, depth=1e30): 
    686         """ DF traverse of the ontology down from term. 
     831        """ 
     832        DF traverse of the ontology down from term. 
    687833        """ 
    688834        if depth >= 1: 
    689835            for child in self.child_terms(term): 
    690836                yield child 
    691                 for t in self.traverse_df(child, depth-1): 
     837                for t in self.traverse_df(child, depth - 1): 
    692838                    yield t 
    693          
    694      
     839 
    695840    def to_network(self, terms=None): 
    696         """ Return an Orange.network.Network instance constructed from 
     841        """ 
     842        Return an Orange.network.Network instance constructed from 
    697843        this ontology. 
    698          
     844 
    699845        """ 
    700846        edge_types = self.edge_types() 
     
    702848        from Orange.orng import orngNetwork 
    703849        import orange 
    704          
     850 
    705851        network = orngNetwork.Network(len(terms), True, len(edge_types)) 
    706852        network.objects = dict([(term.id, i) for i, term in enumerate(terms)]) 
    707          
     853 
    708854        edges = defaultdict(set) 
    709855        for term in self.terms(): 
     
    711857            for relType, relTerm in related: 
    712858                edges[(term.id, relTerm)].add(relType) 
    713                  
     859 
    714860        edgeitems = edges.items() 
    715861        for (src, dst), eTypes in edgeitems: 
    716862            network[src, dst] = [1 if e in eTypes else 0 for e in edge_types] 
    717              
     863 
    718864        domain = orange.Domain([orange.StringVariable("id"), 
    719865                                orange.StringVariable("name"), 
    720866                                orange.StringVariable("def"), 
    721867                                ], False) 
    722          
     868 
    723869        items = orange.ExampleTable(domain) 
    724870        for term in terms: 
    725871            ex = orange.Example(domain, [term.id, term.name, term.values.get("def", [""])[0]]) 
    726872            items.append(ex) 
    727          
     873 
    728874        relationships = set([", ".join(sorted(eTypes)) for (_, _), eTypes in edgeitems]) 
    729875        domain = orange.Domain([orange.FloatVariable("u"), 
     
    731877                                orange.EnumVariable("relationship", values=list(edge_types)) 
    732878                                ], False) 
    733          
     879 
    734880        id2index = dict([(term.id, i + 1) for i, term in enumerate(terms)]) 
    735881        links = orange.ExampleTable(domain) 
     
    737883            ex = orange.Example(domain, [id2index[src], id2index[dst], eTypes.pop()]) 
    738884            links.append(ex) 
    739              
     885 
    740886        network.items = items 
    741887        network.links = links 
    742888        network.optimization = None 
    743889        return network 
    744      
     890 
    745891    def to_networkx(self, terms=None): 
    746         """ Return a NetworkX graph of this ontology 
     892        """ 
     893        Return a NetworkX graph of this ontology. 
    747894        """ 
    748895        import networkx 
    749896        graph = networkx.Graph() 
    750          
     897 
    751898        edge_types = self.edge_types() 
    752          
     899 
    753900        edge_colors = {"is_a": "red"} 
    754          
     901 
    755902        if terms is None: 
    756903            terms = self.terms() 
     
    759906            super_terms = [self.super_terms(term) for term in terms] 
    760907            terms = reduce(set.union, super_terms, set(terms)) 
    761              
     908 
    762909        for term in terms: 
    763910            graph.add_node(term.id, name=term.name) 
    764              
     911 
    765912        for term in terms: 
    766913            for rel_type, rel_term in self.related_terms(term): 
    767914                rel_term = self.term(rel_term) 
    768915                if rel_term in terms: 
    769                     graph.add_edge(term.id, rel_term.id, label=rel_type, color=edge_colors.get(rel_type, "blue")) 
    770                      
     916                    graph.add_edge(term.id, rel_term.id, label=rel_type, 
     917                                   color=edge_colors.get(rel_type, "blue")) 
     918 
    771919        return graph 
    772      
     920 
    773921    def to_graphviz(self, terms=None): 
    774         """ Return an pygraphviz.AGraph representation of the ontology in. 
     922        """ 
     923        Return an pygraphviz.AGraph representation of the ontology. 
    775924        If `terms` is not `None` it must be a list of terms in the ontology. 
    776925        The graph will in this case contain only the super graph of those 
    777         terms.   
    778          
     926        terms. 
     927 
    779928        """ 
    780929        import pygraphviz as pgv 
    781930        graph = pgv.AGraph(directed=True, name="ontology") 
    782          
     931 
    783932        edge_types = self.edge_types() 
    784          
     933 
    785934        edge_colors = {"is_a": "red"} 
    786          
     935 
    787936        if terms is None: 
    788937            terms = self.terms() 
     
    791940            super_terms = [self.super_terms(term) for term in terms] 
    792941            terms = reduce(set.union, super_terms, set(terms)) 
    793              
     942 
    794943        for term in terms: 
    795944            graph.add_node(term.id, name=term.name) 
    796              
     945 
    797946        for term in terms: 
    798947            for rel_type, rel_term in self.related_terms(term): 
    799948                rel_term = self.term(rel_term) 
    800949                if rel_term in terms: 
    801                     graph.add_edge(term.id, rel_term.id, label=rel_type, color=edge_colors.get(rel_type, "blue")) 
    802                      
     950                    graph.add_edge(term.id, rel_term.id, label=rel_type, 
     951                                   color=edge_colors.get(rel_type, "blue")) 
     952 
    803953        return graph 
    804      
    805      
     954 
     955 
    806956def load(file): 
    807     """ Load an ontology from a .obo file 
     957    """ 
     958    Load an ontology from a .obo file 
    808959    """ 
    809960    return OBOOntology(file) 
    810      
    811      
     961 
     962 
    812963def foundry_ontologies(): 
    813     """ List ontologies available from the OBOFoundry website 
    814     (`http://www.obofoundry.org/`_)  
    815     Example:: 
    816         >>> foundry_ontologies() 
    817         [('Biological process', 'http://obo.cvs.sourceforge.net/*checkout*/obo/obo/ontology/genomic-proteomic/gene_ontology_edit.obo'), ... 
    818      
    819     """ 
    820     import urllib2, re 
     964    """ 
     965    Return a list of ontologies available from the OBOFoundry `website 
     966    <http://www.obofoundry.org/>`_. The list contains a tuples of the form 
     967    `(title, url)` for instance 
     968    ``('Biological process', 'http://purl.obolibrary.org/obo/go.obo')`` 
     969 
     970    """ 
    821971    stream = urllib2.urlopen("http://www.obofoundry.org/") 
    822972    text = stream.read() 
    823973    pattern = r'<td class=".+?">\s*<a href=".+?">(.+?)</a>\s*</td>\s*<td class=".+?">.*?</td>\s*<td class=".+?">.*?</td>\s*?<td class=".+?">\s*<a href="(.+?obo)">.+?</a>' 
    824974    return re.findall(pattern, text) 
    825      
    826      
     975 
     976 
    827977if __name__ == "__main__": 
    828978    import doctest 
     
    831981name: bar 
    832982''' 
    833     from StringIO import StringIO 
    834983    seinfeld = StringIO(""" 
    835984[Typedef] 
     
    8541003relationship: parent 001 ! George 
    8551004 
    856 """) # TODO: fill the ontology with all characters 
     1005""")  # TODO: fill the ontology with all characters 
    8571006    term = OBOObject.parse_stanza(stanza) 
    858      
     1007 
    8591008    seinfeld = OBOOntology(seinfeld) 
    8601009    print seinfeld.child_edges("001") 
    861      
    862     doctest.testmod(extraglobs={"stanza": stanza, "term": term}, optionflags=doctest.ELLIPSIS) 
    863          
     1010 
     1011    doctest.testmod(extraglobs={"stanza": stanza, "term": term}, 
     1012                    optionflags=doctest.ELLIPSIS) 
  • docs/rst/index.rst

    r1736 r1742  
    2626 
    2727   reference/kegg.rst 
     28   reference/ontology.rst 
    2829 
    2930Installation 
Note: See TracChangeset for help on using the changeset viewer.