source: orange-bioinformatics/Orange/bioinformatics/obiOntology.py @ 1632:9cf919d0f343

Revision 1632:9cf919d0f343, 29.4 KB checked in by mitar, 2 years ago (diff)

Fixing imports.

Line 
1"""\
2obiOntology
3-----------
4
5Python module for manipulating OBO Ontology files (http://www.obofoundry.org/)
6
7You can construct an ontology from scratch with custom terms ::
8
9    >>> term = OBOObject("Term", id="foo:bar", name="Foo bar")
10    >>> print term
11    [Term]
12    id: foo:bar
13    name: Foo bar
14   
15    >>> ontology = OBOOntology()
16    >>> ontology.add_object(term)
17    >>> ontology.add_header_tag("created-by", "ales") # add a header tag
18    >>> from StringIO import StringIO
19    >>> buffer = StringIO()
20    >>> ontology.dump(buffer) # Save the ontology to a file like object
21    >>> print buffer.getvalue() # Print the contents of the buffer
22    created-by: ales
23    <BLANKLINE>
24    [Term]
25    id: foo:bar
26    name: Foo bar
27    <BLANKLINE>
28   
29To load an ontology from a file pass the file or filename to the OBOOntology
30constructor or call its load method ::
31
32    >>> buffer.seek(0) # rewind
33    >>> ontology = OBOOntology(buffer)
34    >>> # Or
35    >>> buffer.seek(0) # rewind
36    >>> ontology = OBOOntology()
37    >>> ontology.load(buffer)
38   
39See OBOOntology docstring for more details.
40
41"""
42
43from itertools import chain
44from collections import defaultdict
45from StringIO import StringIO
46import itertools
47import re
48
49BUILTIN_OBO_OBJECTS = [
50"""[Typedef]
51id: is_a
52name: is_a
53range: OBO:TERM_OR_TYPE
54domain: OBO:TERM_OR_TYPE
55definition: The basic subclassing relationship [OBO:defs]"""
56,
57"""[Typedef]
58id: disjoint_from
59name: disjoint_from
60range: OBO:TERM
61domain: OBO:TERM
62definition: Indicates that two classes are disjoint [OBO:defs]"""
63,
64"""[Typedef]
65id: instance_of
66name: instance_of
67range: OBO:TERM
68domain: OBO:INSTANCE
69definition: Indicates the type of an instance [OBO:defs]"""
70,
71"""[Typedef]
72id: inverse_of
73name: inverse_of
74range: OBO:TYPE
75domain: OBO:TYPE
76definition: Indicates that one relationship type is the inverse of another [OBO:defs]"""
77,
78"""[Typedef]
79id: union_of
80name: union_of
81range: OBO:TERM
82domain: OBO:TERM
83definition: Indicates that a term is the union of several others [OBO:defs]"""
84,
85"""[Typedef]
86id: intersection_of
87name: intersection_of
88range: OBO:TERM
89domain: OBO:TERM
90definition: Indicates that a term is the intersection of several others [OBO:defs]"""
91]
92   
93def _split_and_strip(string, sep):
94    head, tail = string.split(sep, 1)
95    return head.rstrip(" "), tail.lstrip(" ")
96
97
98class OBOObject(object):
99    """ Represents a generic OBO object (e.g. Term, Typedef, Instance, ...)
100    Example::
101        >>> term = OBOObject(stanza_type="Term", id="FOO:001", name="bar")
102    """
103    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
110        """
111        self.stanza_type = stanza_type
112       
113        self.modifiers = []
114        self.comments = []
115        self.tag_values = []
116        self.values = {}
117       
118        sorted_tags = sorted(kwargs.iteritems(), key=lambda key_val: chr(1) if key_val[0] == "id" else key_val[0])
119        for tag, value in sorted_tags:
120            if isinstance(value, basestring):
121                tag, value, modifiers, comment = self.parse_tag_value(self.name_demangle(tag), value)
122            elif isinstance(value, tuple):
123                tag, value, modifiers, comment = ((self.name_demangle(tag),) + value + (None, None))[:4]
124            self.add_tag(tag, value, modifiers, comment)
125       
126        self.related = set()
127#        self.related_to = set()
128           
129    @property
130    def is_annonymous(self):
131        value = self.get_value("is_annonymous")
132        return bool(value)
133   
134    def name_mangle(self, tag):
135        """ Mangle tag name if it conflicts with python keyword
136        Example::
137            >>> term.name_mangle("def"), term.name_mangle("class")
138            ('def_', 'class_')
139        """
140        if tag in ["def", "class", "in", "not"]:
141            return tag + "_"
142        else:
143            return tag
144       
145    def name_demangle(self, tag):
146        """ Reverse of name_mangle
147        """
148        if tag in ["def_", "class_", "in_", "not_"]:
149            return tag[:-1]
150        else:
151            return tag
152       
153    def add_tag(self, tag, value, modifiers=None, comment=None):
154        """ Add `tag`, `value` pair to the object with optional modifiers and
155        comment.
156        Example::
157            >>> term = OBOObject("Term")
158            >>> term.add_tag("id", "FOO:002", comment="This is an id")
159            >>> print term
160            [Term]
161            id: FOO:002 ! This is an id
162             
163        """
164        tag = intern(tag) # a small speed and memory benefit
165        self.tag_values.append((tag, value))
166        self.modifiers.append(modifiers)
167        self.comments.append(comment)
168        self.values.setdefault(tag, []).append(value)
169       
170        #  TODO: fix multiple tags grouping
171        if hasattr(self, tag):
172            if isinstance(getattr(self, tag), list):
173                getattr(self, tag).append(value)
174            else:
175                setattr(self, tag, [getattr(self, tag)] + [value])
176        else:
177            setattr(self, self.name_mangle(tag), value)
178           
179    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):
185            if tag != "id":
186                self.add_tag(tag, value, modifiers, comment)
187       
188    def get_value(self, tag, group=True):
189        if group:
190            pairs = [pair for pair in self.tag_values if pair[0] == tag]
191            return pairs
192        else:
193            tag = self.name_mangle(tag)
194            if tag in self.__dict__:
195                return self.__dict__[tag]
196            else:
197                raise ValueError("No value for tag: %s" % tag)
198       
199    def tag_count(self):
200        """ Retrun the number of tags in this object
201        """
202        return len(self.tag_values)
203   
204    def tags(self):
205        """ Retrun an iterator over the (tag, value) pairs.
206        """
207        for i in range(self.tag_count()):
208            yield self.tag_values[i] + (self.modifiers[i], self.comments[i])
209       
210    def format_single_tag(self, index):
211        """Return a formated string representing index-th tag pair value
212        Example::
213            >>> term = OBOObject("Term", id="FOO:001", name="bar", def_="Example definition {modifier=frob} ! Comment")
214            >>> term.format_single_tag(0)
215            'id: FOO:001'
216            >>> term.format_single_tag(1)
217            'def: Example definition { modifier=frob } ! Comment'
218        """
219        tag, value = self.tag_values[index]
220        modifiers = self.modifiers[index]
221        comment = self.comments[index]
222        res = ["%s: %s" % (tag, value)]
223        if modifiers:
224            res.append("{ %s }" % modifiers)
225        if comment:
226            res.append("! " + comment)
227        return " ".join(res)
228   
229    def format_stanza(self):
230        """ Return a string stanza representation of this object
231        """
232        stanza = ["[%s]" % self.stanza_type]
233        for i in range(self.tag_count()):
234            stanza.append(self.format_single_tag(i))
235        return "\n".join(stanza)
236           
237    @classmethod     
238    def parse_stanza(cls, stanza):
239        r""" Parse and return an OBOObject instance from a single stanza.
240        Example::
241            >>> term = OBOObject.parse_stanza("[Term]\nid: FOO:001\nname:bar")
242            >>> print term.id, term.name
243            FOO:001 bar
244           
245        """
246        lines = stanza.splitlines()
247        stanza_type = lines[0].strip("[]")
248        tag_values = []
249        for line in lines[1:]:
250            if ":" in line:
251                tag_values.append(cls.parse_tag_value(line))
252       
253        obo = OBOObject(stanza_type)
254        for i, (tag, value, modifiers, comment) in enumerate(tag_values):
255#            print tag, value, modifiers, comment
256            obo.add_tag(tag, value, modifiers, comment)
257        return obo
258   
259       
260    @classmethod
261    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       
265        Example::
266            >>> OBOObject.parse_tag_value("foo: bar {modifier=frob} ! Comment")
267            ('foo', 'bar', 'modifier=frob', 'Comment')
268            >>> OBOObject.parse_tag_value("foo: bar")
269            ('foo', 'bar', None, None)
270            >>> #  Can also pass tag, value pair already split   
271            >>> OBOObject.parse_tag_value("foo", "bar {modifier=frob} ! Comment")
272            ('foo', 'bar', 'modifier=frob', 'Comment')
273        """
274        if args and ":" not in tag_value_pair:
275            tag, rest = tag_value_pair, args[0]
276        else:
277            tag, rest = _split_and_strip(tag_value_pair, ":")
278        value, modifiers, comment = None, None, None
279       
280        if "{" in rest:
281            value, rest = _split_and_strip(rest, "{",)
282            modifiers, rest = _split_and_strip(rest, "}")
283        if "!" in rest:
284            if value is None:
285                value, comment = _split_and_strip(rest, "!")
286            else:
287                _, comment = _split_and_strip(rest, "!")
288        if value is None:
289            value = rest
290           
291        if modifiers is not None:
292            modifiers = modifiers #TODO: split modifiers in a list
293           
294        return tag, value, modifiers, comment
295   
296    _RE_TAG_VALUE = re.compile(r"^(?P<tag>.+?[^\\])\s*:\s*(?P<value>.+?)\s*(?P<modifiers>[^\\]{.+?[^\\]})?\s*(?P<comment>[^\\]!.*)?$")
297    _RE_VALUE = re.compile(r"^\s*(?P<value>.+?)\s*(?P<modifiers>[^\\]{.+?[^\\]})?\s*(?P<comment>[^\\]!.*)?$")
298   
299    @classmethod
300    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       
304        Example::
305            >>> OBOObject.parse_tag_value("foo: bar {modifier=frob} ! Comment")
306            ('foo', 'bar', 'modifier=frob', 'Comment')
307            >>> OBOObject.parse_tag_value("foo: bar")
308            ('foo', 'bar', None, None)
309            >>> #  Can also pass tag, value pair already split   
310            >>> OBOObject.parse_tag_value("foo", "bar {modifier=frob} ! Comment")
311            ('foo', 'bar', 'modifier=frob', 'Comment')
312           
313        .. 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
317            tag = tag_value_pair
318            value, modifiers, comment =  cls._RE_VALUE.findall(arg)[0]
319        else:
320            tag, value, modifiers, comment = cls._RE_TAG_VALUE.findall(tag_value_pair)[0]
321        none_if_empyt = lambda val: None if not val.strip() else val.strip()
322        modifiers = modifiers.strip(" {}")
323        comment = comment.lstrip(" !")
324        return (none_if_empyt(tag), none_if_empyt(value),
325                none_if_empyt(modifiers), none_if_empyt(comment))
326         
327    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", [])]
333        return result
334
335    def __repr__(self):
336        """ Return a string representation of the object in OBO format
337        """
338        return self.format_stanza()
339
340    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       
347class Term(OBOObject):
348    def __init__(self, *args, **kwargs):
349        OBOObject.__init__(self, "Term", *args, **kwargs)
350
351class Typedef(OBOObject):
352    def __init__(self, *args, **kwargs):
353        OBOObject.__init__(self, "Typedef", *args, **kwargs)
354
355class Instance(OBOObject):
356    def __init__(self, *args, **kwargs):
357        OBOObject.__init__(self, "Instance", *args, **kwargs)
358
359
360import re
361
362class OBOParser(object):
363    r""" A simple parser for .obo files (inspired by xml.dom.pulldom)
364   
365    Example::
366        >>> from StringIO import StringIO
367        >>> file = StringIO("header_tag: header_value\n[Term]\nid: FOO { modifier=bar } ! comment\n\n")
368        >>> parser = OBOParser(file)
369        >>> for event, value in parser:
370        ...     print event, value
371        ...     
372        HEADER_TAG ['header_tag', 'header_value']
373        START_STANZA Term
374        TAG_VALUE ('id', 'FOO', 'modifier=bar', 'comment')
375        CLOSE_STANZA None
376               
377    """
378    def __init__(self, file):
379        self.file = file
380       
381    def parse(self, progress_callback=None):
382        """ Parse the file and yield parse events.
383       
384        .. TODO: List events and values
385        """
386        data = self.file.read()
387        header = data[: data.index("\n[")]
388        body = data[data.index("\n[") + 1:]
389        for line in header.splitlines():
390            if line.strip():
391                yield "HEADER_TAG", line.split(": ", 1)
392               
393        current = None
394        #  For speed make these functions local
395        startswith = str.startswith
396        endswith = str.endswith
397        parse_tag_value = OBOObject.parse_tag_value
398       
399        for line in body.splitlines():
400#            line = line.strip()
401            if startswith(line, "[") and endswith(line, "]"):
402                yield "START_STANZA", line.strip("[]")
403                current = line
404            elif startswith(line, "!"):
405                yield "COMMENT", line[1:]
406            elif line:
407                yield "TAG_VALUE", parse_tag_value(line)
408            else: #  empty line is the end of a term
409                yield "CLOSE_STANZA", None
410                current = None
411        if current is not None:
412            yield "CLOSE_STANZA", None
413   
414    def __iter__(self):
415        """ Iterate over parse events (same as parse())
416        """
417        return self.parse() 
418       
419       
420class OBOOntology(object):
421    """ The main ontology object.
422    """
423   
424    BUILTINS = BUILTIN_OBO_OBJECTS
425    def __init__(self, file=None):
426        """ Init an ontology instance from a file like object (.obo format)
427       
428        """
429        self.objects = []
430        self.header_tags = []
431        self.id2term = {}
432        self.alt2id = {}
433        self._resolved_imports = []
434        self._invalid_cache_flag = False
435        self._related_to = {}
436       
437        # First load the built in OBO objects
438        builtins = StringIO("\n" + "\n\n".join(self.BUILTINS) + "\n")
439        self.load(builtins)
440        if file:
441            self.load(file)
442       
443    def add_object(self, object):
444        """ Add OBOObject instance to this ontology.
445        """
446        if object.id in self.id2term:
447            raise ValueError("OBOObject with id: %s already in the ontology" % object.id)
448        self.objects.append(object)
449        self.id2term[object.id] = object
450        self._invalid_cache_flag = True
451       
452    def add_header_tag(self, tag, value):
453        """ Add header tag, value pair to this ontology
454        """
455        self.header_tags.append((tag, value))
456   
457    def load(self, file, progress_callback=None):
458        """ Load terms from a file.
459        """
460        if isinstance(file, basestring):
461            file = open(file, "rb")
462        parser = OBOParser(file)
463        current = None
464        for event, value in parser.parse(progress_callback=progress_callback):
465            if event == "TAG_VALUE":
466                current.add_tag(*value)
467            elif event == "START_STANZA":
468                current = OBOObject(value)
469            elif event == "CLOSE_STANZA":
470                self.add_object(current)
471                current = None
472            elif event == "HEADER_TAG":
473                self.add_header_tag(*value)
474            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               
486    def dump(self, file):
487        """ Dump the contents of the ontology to a .obo `file`.
488        """
489        if isinstance(file, basestring):
490            file = open(file, "wb")
491           
492        for key, value in self.header_tags:
493            file.write(key + ": " + value + "\n")
494           
495        # Skip the builtins
496        for object in self.objects[len(self.BUILTINS):]:
497            file.write("\n")
498            file.write(object.format_stanza())
499            file.write("\n")
500   
501    def update(self, other):
502        """ Update this ontology with the terms from another.
503        """
504        for term in other:
505            if term.id in self:
506                if not term.is_annonymous:
507                    self.term(term.id).update(term)
508                else: #  Do nothing
509                    pass 
510            else:
511                self.add_object(term)
512        self._invalid_cache_flag = True
513       
514    def _cache_validate(self, force=False):
515        """ Update the relations cache if `self._invalid_cache` flag is set.
516        """
517        if self._invalid_cache_flag or force:
518            self._cache_relations()
519           
520    def _cache_relations(self):
521        """ Collect all relations from parent to a child and store it in
522        `self._related_to` member.
523       
524        """
525        related_to = defaultdict(list)
526        for obj in self.objects:
527            for rel_type, id in self.related_terms(obj):
528                term = self.term(id)
529                related_to[term].append((rel_type, obj))
530               
531        self._related_to = related_to
532        self._invalid_cache_flag = False
533       
534    def term(self, id):
535        """ Return the OBOObject associated with this id.
536        """
537        if isinstance(id, basestring):
538            if id in self.id2term:
539                return self.id2term[id]
540            elif id in self.alt2id:
541                return self.id2term[self.alt2id[id]]
542            else:
543                raise ValueError("Unknown term id: %r" % id)
544        elif isinstance(id, OBOObject):
545            return id
546       
547    def terms(self):
548        """ Return all `Term` instances in the ontology.
549        """
550        return [obj for obj in self.objects if obj.stanza_type == "Term"]
551   
552    def term_by_name(self, name):
553        """ Return the term with name ``name``.
554        """
555        terms = [t for t in self.terms() if t.name == name]
556        if len(terms) != 1:
557            raise ValueError("Unknown term name: %r" % name)
558        return terms[0]
559   
560    def typedefs(self):
561        """ Return all `Typedef` instances in the ontology.
562        """
563        return [obj for obj in self.objects if obj.stanza_type == "Typedef"]
564   
565    def instances(self):
566        """ Return all `Instance` instances in the ontology.
567        """
568        return [obj for obj in self.objects if obj.stanza_type == "Instance"]
569       
570    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       
575        """
576        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
578        relationships = term.values.get("relationship", [])
579        for rel in relationships:
580            related.append(tuple(rel.split(None, 1)))
581        return related
582       
583    def edge_types(self):
584        """ Return a list of all edge types in the ontology
585        """
586        return [obj.id for obj in self.objects if obj.stanza_type == "Typedef"]
587   
588    def parent_edges(self, term):
589        """ Return a list of (rel_type, parent_term) tuples
590        """
591        term = self.term(term)
592        parents = []
593        for rel_type, parent in self.related_terms(term):
594            parents.append((rel_type, self.term(parent)))
595        return parents
596       
597    def child_edges(self, term):
598        """ Return a list of (rel_type, source_term) tuples
599        """
600        self._cache_validate()
601        term = self.term(term)
602        return self._related_to.get(term, [])
603       
604       
605    def super_terms(self, term):
606        """ Return a set of all super terms of `term` up to the most general one.
607        """
608        terms = self.parent_terms(term)
609        visited = set()
610        queue = set(terms)
611        while queue:
612            term = queue.pop()
613            visited.add(term)
614            queue.update(self.parent_terms(term) - visited)
615        return visited
616   
617    def sub_terms(self, term):
618        """ Return a set of all sub terms for `term`.
619        """
620        terms = self.child_terms(term)
621        visited = set()
622        queue = set(terms)
623        while queue:
624            term = queue.pop()
625            visited.add(term)
626            queue.update(self.child_terms(term) - visited)
627        return visited
628   
629    def child_terms(self, term):
630        """ Return a set of all child terms for this `term`.
631        """
632        self._cache_validate()
633        term = self.term(term)
634        children = []
635        for rel_type, term in self.child_edges(term):
636            children.append(term)
637        return set(children)
638       
639    def parent_terms(self, term):
640        """ Return a set of all parent terms for this `term`
641        """
642        term = self.term(term)
643        parents = []
644        for rel_type, id in self.parent_edges(term): #term.related_objects():
645            parents.append(self.term(id))
646        return set(parents)
647   
648    def relations(self):
649        """ Return a list of all relations in the ontology.
650        """
651        relations = []
652        for obj in self.objects:
653            for type_id, id in  obj.related:
654                target_term = self.term(id)
655            relations.append((obj, type_id, target_term))
656        return relations
657   
658    def __len__(self):
659        return len(self.objects)
660   
661    def __iter__(self):
662        return iter(self.objects)
663   
664    def __contains__(self, obj):
665        if isinstance(obj, basestring):
666            return obj in self.id2term
667        else:
668            return obj in self.objects
669   
670    def __getitem__(self, key):
671        return self.id2term[key]
672   
673    def has_key(self, key):
674        return self.id2tem.has_key(key)
675   
676    def traverse_bf(self, term):
677        """ BF traverse of the ontology down from term.
678        """
679        queue = list(self.child_terms(term))
680        while queue:
681            term = queue.pop(0)
682            queue.extend(self.child_terms(term))
683            yield term
684                   
685    def traverse_df(self, term, depth=1e30):
686        """ DF traverse of the ontology down from term.
687        """
688        if depth >= 1:
689            for child in self.child_terms(term):
690                yield child
691                for t in self.traverse_df(child, depth-1):
692                    yield t
693       
694   
695    def to_network(self, terms=None):
696        """ Return an Orange.network.Network instance constructed from
697        this ontology.
698       
699        """
700        edge_types = self.edge_types()
701        terms = self.terms()
702        from Orange.orng import orngNetwork
703        import orange
704       
705        network = orngNetwork.Network(len(terms), True, len(edge_types))
706        network.objects = dict([(term.id, i) for i, term in enumerate(terms)])
707       
708        edges = defaultdict(set)
709        for term in self.terms():
710            related = self.related_terms(term)
711            for relType, relTerm in related:
712                edges[(term.id, relTerm)].add(relType)
713               
714        edgeitems = edges.items()
715        for (src, dst), eTypes in edgeitems:
716            network[src, dst] = [1 if e in eTypes else 0 for e in edge_types]
717           
718        domain = orange.Domain([orange.StringVariable("id"),
719                                orange.StringVariable("name"),
720                                orange.StringVariable("def"),
721                                ], False)
722       
723        items = orange.ExampleTable(domain)
724        for term in terms:
725            ex = orange.Example(domain, [term.id, term.name, term.values.get("def", [""])[0]])
726            items.append(ex)
727       
728        relationships = set([", ".join(sorted(eTypes)) for (_, _), eTypes in edgeitems])
729        domain = orange.Domain([orange.FloatVariable("u"),
730                                orange.FloatVariable("v"),
731                                orange.EnumVariable("relationship", values=list(edge_types))
732                                ], False)
733       
734        id2index = dict([(term.id, i + 1) for i, term in enumerate(terms)])
735        links = orange.ExampleTable(domain)
736        for (src, dst), eTypes in edgeitems:
737            ex = orange.Example(domain, [id2index[src], id2index[dst], eTypes.pop()])
738            links.append(ex)
739           
740        network.items = items
741        network.links = links
742        network.optimization = None
743        return network
744   
745    def to_networkx(self, terms=None):
746        """ Return a NetworkX graph of this ontology
747        """
748        import networkx
749        graph = networkx.Graph()
750       
751        edge_types = self.edge_types()
752       
753        edge_colors = {"is_a": "red"}
754       
755        if terms is None:
756            terms = self.terms()
757        else:
758            terms = [self.term(term) for term in terms]
759            super_terms = [self.super_terms(term) for term in terms]
760            terms = reduce(set.union, super_terms, set(terms))
761           
762        for term in terms:
763            graph.add_node(term.id, name=term.name)
764           
765        for term in terms:
766            for rel_type, rel_term in self.related_terms(term):
767                rel_term = self.term(rel_term)
768                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                   
771        return graph
772   
773    def to_graphviz(self, terms=None):
774        """ Return an pygraphviz.AGraph representation of the ontology in.
775        If `terms` is not `None` it must be a list of terms in the ontology.
776        The graph will in this case contain only the super graph of those
777        terms. 
778       
779        """
780        import pygraphviz as pgv
781        graph = pgv.AGraph(directed=True, name="ontology")
782       
783        edge_types = self.edge_types()
784       
785        edge_colors = {"is_a": "red"}
786       
787        if terms is None:
788            terms = self.terms()
789        else:
790            terms = [self.term(term) for term in terms]
791            super_terms = [self.super_terms(term) for term in terms]
792            terms = reduce(set.union, super_terms, set(terms))
793           
794        for term in terms:
795            graph.add_node(term.id, name=term.name)
796           
797        for term in terms:
798            for rel_type, rel_term in self.related_terms(term):
799                rel_term = self.term(rel_term)
800                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                   
803        return graph
804   
805   
806def load(file):
807    """ Load an ontology from a .obo file
808    """
809    return OBOOntology(file)
810   
811   
812def 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
821    stream = urllib2.urlopen("http://www.obofoundry.org/")
822    text = stream.read()
823    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>'
824    return re.findall(pattern, text)
825   
826   
827if __name__ == "__main__":
828    import doctest
829    stanza = '''[Term]
830id: FOO:001
831name: bar
832'''
833    from StringIO import StringIO
834    seinfeld = StringIO("""
835[Typedef]
836id: parent
837
838[Typedef]
839id: child
840inverse_of: parent ! not actually used yet
841
842[Term]
843id: 001
844name: George
845
846[Term]
847id: 002
848name: Estelle
849relationship: parent 001 ! George
850
851[Term]
852id: 003
853name: Frank
854relationship: parent 001 ! George
855
856""") # TODO: fill the ontology with all characters
857    term = OBOObject.parse_stanza(stanza)
858   
859    seinfeld = OBOOntology(seinfeld)
860    print seinfeld.child_edges("001")
861   
862    doctest.testmod(extraglobs={"stanza": stanza, "term": term}, optionflags=doctest.ELLIPSIS)
863       
Note: See TracBrowser for help on using the repository browser.