Skip to content

Commit

Permalink
Merge pull request #38 from biolink/scigraph
Browse files Browse the repository at this point in the history
Adding a SciGraph adapter
  • Loading branch information
cmungall committed Jun 16, 2017
2 parents a43b9a6 + f06159e commit 61d3292
Show file tree
Hide file tree
Showing 11 changed files with 404 additions and 40 deletions.
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,7 @@ release: cleandist
nb:
PYTHONPATH=.. jupyter notebook

# Hack: generate marshmallow schema from flaskrest serializers
# used to make assoc_schema.py
mm:
./bin/flask2marshmallow.pl ../biolink-api/biolink/datamodel/serializers.py
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
<[![Build Status](https://travis-ci.org/biolink/ontobio.svg?branch=master)](https://travis-ci.org/biolink/ontobio)
[![DOI](https://zenodo.org/badge/13996/biolink/ontobio.svg)](https://zenodo.org/badge/latestdoi/13996/biolink/ontobio)
[![PyPI](https://img.shields.io/pypi/v/ontobio.svg)](https://pypi.python.org/pypi/ontobio)

Expand Down
15 changes: 9 additions & 6 deletions bin/ogr.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def main():
logging.info("Query for level: {}".format(args.level))
qids = qids + ont.get_level(args.level, relations=args.properties, prefix=args.prefix)

if args.insubset is not None:
if args.insubset is not None and args.insubset != "":
disjs = args.insubset.split("|")
dset = set()
for i in disjs:
Expand Down Expand Up @@ -116,8 +116,10 @@ def main():
nodes = ont.traverse_nodes(qids, up=dirn.find("u") > -1, down=dirn.find("d") > -1,
relations=args.properties)

g = ont.get_filtered_graph(relations=args.properties)
show_subgraph(ont, nodes, qids, args)
# deprecated
#g = ont.get_filtered_graph(relations=args.properties)
subont = ont.subontology(nodes, relations=args.properties)
show_subgraph(subont, qids, args)


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

def show_subgraph(ont, nodes, query_ids, args):
def show_subgraph(ont, query_ids, args):
"""
Writes or displays graph
"""
Expand All @@ -142,8 +144,9 @@ def show_subgraph(ont, nodes, query_ids, args):
w = GraphRenderer.create(args.to)
if args.outfile is not None:
w.outfile = args.outfile
logging.info("Writing subgraph for {}, |nodes|={}".format(ont,len(nodes)))
w.write_subgraph(ont, nodes, query_ids=query_ids, container_predicates=args.container_properties)
w.write(ont, query_ids=query_ids, container_predicates=args.container_properties)
#logging.info("Writing subgraph for {}, |nodes|={}".format(ont,len(nodes)))
#w.write_subgraph(ont, nodes, query_ids=query_ids, container_predicates=args.container_properties)

def resolve_ids(ont, ids, args):
r_ids = []
Expand Down
2 changes: 1 addition & 1 deletion conf/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ scigraph_ontology:
url: "https://scigraph-ontology.monarchinitiative.org/scigraph/"
timeout: 2
scigraph_data:
url: "https://scigraph-ontology.monarchinitiative.org/scigraph/"
url: "https://scigraph-data.monarchinitiative.org/scigraph/"
timeout: 2
use_amigo_for:
- function
Expand Down
28 changes: 28 additions & 0 deletions docs/inputs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,34 @@ Code example, using an :class:`OntologyFactory`
from ontobio.ontol_factory import OntologyFactory
ont = OntologyFactory().create("cl")
Remote SciGraph ontology access
-------------------------------

.. warning ::
Experimental
Command line example:

::

ogr.py -r scigraph:ontology

Code example, using an :class:`OntologyFactory`

.. code-block:: python
from ontobio.ontol_factory import OntologyFactory
ont = OntologyFactory().create("scigraph:ontology")
.. warning ::
Since SciGraph contains multiple graphs interwoven together, care
must be taken on queries that don't use relationship types, as
ancestor/descendant lists may be large
Local GAF or GPAD association files
-----------------------------------

Expand Down
4 changes: 2 additions & 2 deletions ontobio/io/ontol_renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,14 @@ def render_subgraph(self, ontol, nodes, **args):
"""
Render a `ontology` object after inducing a subgraph
"""
subont = ontol.subontology(nodes)
subont = ontol.subontology(nodes, **args)
return self.render(subont, **args)

def write_subgraph(self, ontol, nodes, **args):
"""
Write a `ontology` object after inducing a subgraph
"""
subont = ontol.subontology(nodes)
subont = ontol.subontology(nodes, **args)
self.write(subont, **args)

def render_relation(self, r, **args):
Expand Down
226 changes: 208 additions & 18 deletions ontobio/neo/scigraph_ontology.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,44 +13,234 @@

class RemoteScigraphOntology(Ontology):
"""
ontology backed by SciGraph endpoint
ontology backed by SciGraph endpoint.
This class implements a subset of the methods in `Ontology`,
it should be able to substitute for an Ontology method
"""

def __init__(self, url=None):
if url is not None:
self.url_prefix = url
def __init__(self,
handle=None,
url=None,
config=None):
"""
Constructor - typically you do not need to call this directly.
Specify a scigraph handle in ontol_factory, e.g.
``
OntologyFactory().create('scigraph:data')
``
The Session instance will be consulted to determine the URL
The handle can be
- `scigraph:ontology` use scigraph_ontology from config
- `scigraph:data` use scigraph_data from config
- `scigraph:` use default
"""
if handle is not None:
handle = handle.replace("scigraph:","")
else:
self.url_prefix = "http://scigraph-data.monarchinitiative.org/scigraph/"
handle = "ontology"

logging.info("Connecting: {} {}".format(handle, url))
if url is None:
if config is None:
from ontobio.config import get_config
config = get_config()
if config is not None:
logging.info("Fetching scigraph URL from config: {}".format(handle))
urlObj = config.scigraph_ontology
if handle == 'data':
logging.info("Using scigraph_data URL")
urlObj = config.scigraph_data
if urlObj is not None:
url = urlObj.url
logging.info("Set URL from config={} {}".format(url, urlObj))
if url is None:
url = 'https://scigraph-ontology.monarchinitiative.org/scigraph'
self.url = url
logging.info("Base SciGraph URL: {}".format(url))
return

# Internal wrapper onto requests API
def get_response(self, path="", q=None, format=None, **params):
url = self.url_prefix + path;
def _get_response(self, path="", q=None, format=None, **params):
url = self.url
if not url.endswith("/"):
url += "/"
url += path;
if q is not None:
url += "/" +q;
if format is not None:
url = url + "." + format;
r = requests.get(url, params=params)
return r

def neighbors(self, id=None, **params):
def _get_response_json(self, path="", q=None, format=None, **args):
r = self._get_response(path, q, format, **args)
if r.status_code == 200:
return r.json()
else:
return []

def _neighbors_graph(self, id=None, **params):
"""
Get neighbors of a node
parameters are directly passed through to SciGraph: e.g. depth, relationshipType
"""
response = self.get_response("graph/neighbors", id, "json", **params)
# TODO: should return ids?
response = self._get_response("graph/neighbors", id, "json", **params)
return response.json()

# Override
def subgraph(self, nodes=[], relations=None):
r_nodes = []
r_edges = []

logging.debug("Scigraph, Subgraph for {}".format(nodes))
for n in nodes:
logging.debug("Parents-of {}".format(n))
g = self._neighbors_graph(n,
direction='OUTGOING',
depth=1,
relationshipType=self._mkrel(relations))
r_nodes += g['nodes']
r_edges += g['edges']
digraph = nx.MultiDiGraph()
for n in r_nodes:
digraph.add_node(n['id'], attr_dict=self._repair(n))
for e in r_edges:
digraph.add_edge(e['obj'],e['sub'], pred=e['pred'])
return digraph


# Override
def subontology(self, nodes=[], **args):
g = self.subgraph(nodes, **args)
ont = Ontology(graph=g)
return ont

# Override
# TODO: dependent on modeling in scigraph
def subsets(self, nid, contract=True):
raise NotImplementedError()

def get_roots(self, relations=None, prefix=None):
raise NotImplementedError()

def extract_subset(self, subset):
pass
# Override
# Override
def nodes(self):
raise NotImplementedError()

def _mkrel(self, relations=None):
if relations is not None:
return "|".join(relations)
else:
return None


# Override
def ancestors(self, node, relations=None, reflexive=False):
logging.debug("Ancestors of {} over {}".format(node, relations))
g = self._neighbors_graph(node,
direction='OUTGOING',
depth=20,
relationshipType=self._mkrel(relations))
arr = [v['id'] for v in g['nodes']]
if reflexive:
arr.add(node)
else:
if node in arr:
arr.remove(node)
return arr

# Override
def descendants(self, node, relations=None, reflexive=False):
logging.debug("Descendants of {} over {}".format(node, relations))
g = self._neighbors_graph(node,
direction='INCOMING',
depth=20,
relationshipType=self._mkrel(relations))
arr = [v['id'] for v in g['nodes']]
if reflexive:
arr.add(node)
else:
if node in arr:
arr.remove(node)
return arr

def resolve_names(self, names, is_remote=False, **args):
## TODO
return names
# Override
def neighbors(self, node, relations=None):
g = self._neighbors_graph(node,
direction='BOTH',
depth=1,
relationshipType=self._mkrel(relations))
return [v['id'] for v in g['nodes']]

# map between bbopgraph and obograph
def _repair(self, n):
if 'lbl' in n:
n['label'] = n['lbl']
del n['lbl']
# TODO: transform meta
return n

# Override
def node(self, nid):
g = self._neighbors_graph(nid,
depth=0)
return self._repair(g['nodes'][0])

# Override
def has_node(self, id):
return self.node(id) is not None

# Override
def traverse_nodes(self, qids, up=True, down=False, relations=None):
nodes = set()
for id in qids:
# reflexive - always add self
nodes.add(id)
if down:
nodes.update(self.descendants(id, relations=relations))
if up:
nodes.update(self.ancestors(id, relations=relations))
return nodes

def label(self, nid, id_if_null=False):
n = self.node(nid)
if n is not None and 'label' in n:
return n['label']
else:
if id_if_null:
return nid
else:
return None

def extract_subset(self, subset):
raise NotImplementedError()

# Override
def resolve_names(self, names, synonyms=True, **args):
results = set()
for name in names:
for r in self._vocab_search(name, searchSynonyms=synonyms):
logging.debug("RESULT={}".format(r))
results.add(r['curie'])
logging.debug("Search {} -> {}".format(names, results))
return list(results)

# this uses one of two routes depending on whether exact or inexact
# matching is required
def _vocab_search(self, term, **args):
if '%' in term:
term = term.replace('%','')
return self._get_response_json("vocabulary/search", term, "json", **args)
else:
return self._get_response_json("vocabulary/term", term, "json", **args)

def subgraph(self, nodes=[]):
return self.get_graph().subgraph(nodes)


0 comments on commit 61d3292

Please sign in to comment.