Skip to content

Commit

Permalink
Merge pull request #48 from biolink/tests-and-docs
Browse files Browse the repository at this point in the history
Tests and docs
  • Loading branch information
cmungall committed Jun 26, 2017
2 parents e802802 + 4a06131 commit 2b4369e
Show file tree
Hide file tree
Showing 7 changed files with 223 additions and 70 deletions.
4 changes: 2 additions & 2 deletions bin/ogr.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ def main():
# deprecated
#g = ont.get_filtered_graph(relations=args.properties)
subont = ont.subontology(nodes, relations=args.properties)
show_subgraph(subont, qids, args)
render(subont, qids, args)


def cmd_cycles(handle, args):
Expand All @@ -134,7 +134,7 @@ def cmd_search(handle, args):
for r in results:
print(r)

def show_subgraph(ont, query_ids, args):
def render(ont, query_ids, args):
"""
Writes or displays graph
"""
Expand Down
1 change: 0 additions & 1 deletion ontobio/assoc_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,6 @@ def create_from_gaf(self, file, **args):
results = p.skim(file)
return self.create_from_tuples(results, **args)


def create_from_phenopacket(self, file):
"""
Creates from a phenopacket file
Expand Down
14 changes: 14 additions & 0 deletions ontobio/assocmodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ def __init__(self, ontology=None, association_map={}, subject_label_map=None, me
self.subject_label_map = subject_label_map
self.subject_to_inferred_map = {}
self.meta = meta # TODO
self.associations_by_subj_obj = None
self.strict = False
self.index()
logging.info("Created {}".format(self))
Expand Down Expand Up @@ -287,6 +288,19 @@ def subontology(self, minimal=False):
Generates a sub-ontology based on associations
"""
return self.ontology.subontology(self.objects, minimal=minimal)

# TODO
def get_assocations(self, subj, obj):
"""
Given a subject-object pair (e.g. gene id to ontology class id), return all association
objects that match.
Status: not yet implemented
"""
if self.associations_by_subj_obj is not None:
return self.associations_by_subj_obj[subj][obj]
else:
return []

def enrichment_test(self, subjects=[], background=None, hypotheses=None, threshold=0.05, labels=False, direction='greater'):
"""
Expand Down
45 changes: 19 additions & 26 deletions ontobio/io/ontol_renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,14 +200,13 @@ def __init__(self, **args):
super().__init__(**args)

def render(self, ontol, **args):
g = ontol.get_graph() # TODO - use ontol methods directly
s = ""
for n in ontol.nodes():
s += self.render_noderef(ontol, n, **args) + "\n"
for n2 in ontol.parents(n):
for _,ea in g[n2][n].items():
s += ' {} {}'.format(str(ea['pred']), self.render_noderef(ontol, n2, **args))
s += "\n"
#for n2 in ontol.parents(n):
# for _,ea in g[n2][n].items():
# s += ' {} {}'.format(str(ea['pred']), self.render_noderef(ontol, n2, **args))
# s += "\n"
return s

class AsciiTreeGraphRenderer(GraphRenderer):
Expand Down Expand Up @@ -254,38 +253,32 @@ def __init__(self, **args):
super().__init__(**args)

def render(self, ontol, **args):
g = ontol.get_graph() # TODO - use ontol methods directly
ts = g.nodes()
ts.sort()
s = "ontology: auto\n\n"
for n in ts:
s += self.render_noderef(self, n, ontol, **args)
return s

def render(self, ontol, **args):
g = ontol.get_graph() # TODO - use ontol methods directly
ts = g.nodes()
ts = ontol.nodes()
ts.sort()
s = "ontology: auto\n\n"
for n in ts:
s += self.render_node(n, ontol, **args)
return s

def render_node(self, nid, ontol, **args):
g = ontol.get_graph() # TODO - use ontol methods directly
n = g.node[nid]
s = "[Term]\n";
s += self.tag('id', nid)
s += self._dtag('name', n, 'label')
for p in g.predecessors(nid):
for _,ea in g[p][nid].items():
pred = ea['pred']
if p in g and 'label' in g.node[p]:
p = '{} ! {}'.format(p, g.node[p]['label'])
label = ontol.label(nid)
if label is not None:
s += self.tag('name', label)

for p in ontol.parents(nid):
for pred in ontol.child_parent_relations(nid,p):
p_str = p
if p in ontol.nodes():
p_label = ontol.label(p)
if p_label is not None:
p_str = '{} ! {}'.format(p, p_label)

if pred == 'subClassOf':
s += self.tag('is_a', p)
s += self.tag('is_a', p_str)
else:
s += self.tag('relationship', pred, p)
s += self.tag('relationship', pred, p_str)
for ld in ontol.logical_definitions(nid):
for gen in ld.genus_ids:
s += self.tag('intersection_of', gen)
Expand Down
154 changes: 117 additions & 37 deletions ontobio/ontol.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,14 +127,11 @@ def subontology(self, nodes=None, minimal=False, relations=None):
"""
Return a new ontology that is an extract of this one
Arguments:
- Nodes: list
Arguments
---------
- nodes: list
list of node IDs to include in subontology. If None, all are used
- Relations: list
- relations: list
list of relation IDs to include in subontology. If None, all are used
"""
Expand Down Expand Up @@ -317,7 +314,32 @@ def relations_used(self):

def neighbors(self, node, relations=None):
return self.parents(node, relations=relations) + self.children(node, relations=relations)

def child_parent_relations(self, subj, obj, graph=None):
"""
Get all relationship type ids between a subject and a parent.
Typically only one relation ID returned, but in some cases there may be more than one
Arguments
---------
subj: string
Child (subject) id
obj: string
Parent (object) id
Returns
-------
list
"""
if graph is None:
graph = self.get_graph()
preds = set()
for _,ea in graph[obj][subj].items():
preds.add(ea['pred'])
logging.debug('{}->{} = {}'.format(subj,obj,preds))
return preds

def parents(self, node, relations=None):
"""
Return all direct parents of specified node.
Expand All @@ -332,14 +354,14 @@ def parents(self, node, relations=None):
list of relation (object property) IDs used to filter
"""
g = None
if relations is None:
g = self.get_graph()
else:
# TODO: make this more efficient
g = self.get_filtered_graph(relations)
g = self.get_graph()
if node in g:
return g.predecessors(node)
parents = g.predecessors(node)
if relations is None:
return parents
else:
rset = set(relations)
return [p for p in parents if len(self.child_parent_relations(node, p, graph=g).intersection(rset)) > 0 ]
else:
return []

Expand All @@ -361,13 +383,14 @@ def children(self, node, relations=None):
list of relation (object property) IDs used to filter
"""
g = None
if relations is None:
g = self.get_graph()
else:
g = self.get_filtered_graph(relations)
g = self.get_graph()
if node in g:
return g.successors(node)
children = g.successors(node)
if relations is None:
return children
else:
rset = set(relations)
return [c for c in children if len(self.child_parent_relations(c, node, graph=g).intersection(rset)) > 0 ]
else:
return []

Expand Down Expand Up @@ -410,7 +433,7 @@ def ancestors(self, node, relations=None, reflexive=False):

def descendants(self, node, relations=None, reflexive=False):
"""
Returns all ancestors of specified node.
Returns all descendants of specified node.
The default implementation is to use networkx, but some
implementations of the Ontology class may use a database or
Expand All @@ -432,8 +455,9 @@ def descendants(self, node, relations=None, reflexive=False):
descendant node IDs
"""
if reflexive:
ancs = self.ancestors(node, relations, reflexive=False)
return ancs + [node]
decs = self.descendants(node, relations, reflexive=False)
decs.add(node)
return decs
g = None
if relations is None:
g = self.get_graph()
Expand Down Expand Up @@ -531,7 +555,29 @@ def parent_index(self, relations=None):
for n in g:
l.append([n] ++ g.predecessors(b))
return l


def text_definition(self, nid):
"""
Retrieves logical definitions for a class or relation id
Arguments
---------
nid : str
Node identifier for entity to be queried
Returns
-------
TextDefinition
"""
tdefs = []
meta = self._meta(nid)
if 'definition' in meta:
obj = meta['definition']
return TextDefinition(nid, **obj)
else:
return None


def logical_definitions(self, nid):
"""
Retrieves logical definitions for a class id
Expand Down Expand Up @@ -834,7 +880,47 @@ def __str__(self):
def __repr__(self):
return self.__str__()

class Synonym():
class AbstractPropertyValue(object):
"""
Abstract superclass of all property-value mapping classes.
These correspond to Annotations in OWL
"""
def __str__(self):
return '{} "{}" {}'.format(self.subject, self.val, self.xrefs)
def __repr__(self):
return self.__str__()

def __cmp__(self, other):
(x,y) = (str(self),str(other))
if x > y:
return 1
elif x < y:
return -1
else:
return 0

class TextDefinition(AbstractPropertyValue):
"""
Represents a textual definition for a class or relation
"""

def __init__(self, subject, val=None, xrefs=None, ontology=None):
"""
Arguments
---------
- subject : string
id for the class or relation that is being defined
- val : string
the definition itself
- xrefs: list
Provenance or cross-references to same usage
"""
self.subject = subject
self.val = val
self.xrefs = xrefs
self.ontology = ontology

class Synonym(AbstractPropertyValue):
"""
Represents a synonym using the OBO model
"""
Expand All @@ -847,26 +933,17 @@ class Synonym():

def __init__(self, class_id, val=None, pred=None, lextype=None, xrefs=None, ontology=None):
"""
Arguments:
Arguments
---------
- class_id : string
the class that is being defined
- value : string
- val : string
the synonym itself
- pred: string
oboInOwl predicate used to model scope. One of: has{Exact,Narrow,Related,Broad}Synonym - may also be 'label'
- lextype: string
From an open ended set of types
- xrefs: list
Provenance or cross-references to same usage
"""
Expand All @@ -885,6 +962,9 @@ def __repr__(self):

def scope(self):
return self.predmap[self.pred].upper()

def exact_or_label(self):
return self.pred == 'hasExactSynonym' or self.pred == 'label'

def __cmp__(self, other):
(x,y) = (str(self),str(other))
Expand Down

0 comments on commit 2b4369e

Please sign in to comment.