Skip to content

Commit

Permalink
Merge pull request #35 from INCATools/gap-filling
Browse files Browse the repository at this point in the history
gap filling
  • Loading branch information
cmungall authored Apr 22, 2022
2 parents c512eed + 1b30ab8 commit 39215ed
Show file tree
Hide file tree
Showing 18 changed files with 359 additions and 44 deletions.
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,12 @@ __pycache__
docs/_build/
docs/datamodels/*/*.md
tests/output/

dist/
db/

notebooks/output/*json
notebooks/output/*tsv
notebooks/input/go.db


81 changes: 81 additions & 0 deletions notebooks/Command-Line-Examples.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,51 @@
"The default ubergraph endpoint is used, but you could also set up your own"
]
},
{
"cell_type": "markdown",
"id": "8dbaac3c",
"metadata": {},
"source": [
"### Graphy operations"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "ca33e9c1",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CL:0000000 ! cell\r\n",
"CL:0000540 ! neuron\r\n",
"CL:0002319 ! neural cell\r\n",
"GO:0001750 ! photoreceptor outer segment\r\n",
"GO:0005575 ! cellular_component\r\n",
"UBERON:0001062 ! anatomical entity\r\n",
"BFO:0000040 ! material entity\r\n",
"CARO:0000000 ! anatomical entity\r\n",
"CL:0000003 ! native cell\r\n",
"CL:0000211 ! electrically active cell\r\n",
"CL:0000255 ! eukaryotic cell\r\n",
"CL:0000393 ! electrically responsive cell\r\n",
"CL:0000404 ! electrically signaling cell\r\n",
"CL:0000548 ! animal cell\r\n",
"CL:0002371 ! somatic cell\r\n",
"BFO:0000002 ! continuant\r\n",
"BFO:0000004 ! independent continuant\r\n",
"CARO:0030000 ! biological entity\r\n",
"GO:0110165 ! cellular anatomical entity\r\n"
]
}
],
"source": [
"# all is-a (i.e. subClassOf between named classes) ancestors of a seed set\n",
"!runoak -i ubergraph: ancestors GO:0001750 CL:0000540 -p i"
]
},
{
"cell_type": "code",
"execution_count": 40,
Expand All @@ -121,6 +166,7 @@
}
],
"source": [
"# visualize starting from a seed set of terms\n",
"!runoak -i ubergraph: viz --no-view GO:0001750 CL:0000540 -p i,BFO:0000050 -o output/ug-test1.png -O png"
]
},
Expand All @@ -132,6 +178,41 @@
"![img](output/ug-test1.png)"
]
},
{
"cell_type": "markdown",
"id": "70dea17b",
"metadata": {},
"source": [
"### Gap Filling"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "1a9d16e8",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"(node:2377) [DEP0128] DeprecationWarning: Invalid 'main' field in '/Users/cjm/repos/obographviz/node_modules/node-getopt/package.json' of './lib'. Please either fix that or report it to the module author\r\n",
"(Use `node --trace-deprecation ...` to show where the warning was created)\r\n"
]
}
],
"source": [
"!runoak -i ubergraph: viz --gap-fill GO:0001750 CL:0000540 CL:0000000 NCBITaxon:9606 NCBITaxon:1 -p i,p -o output/ug-gap-fill1.png -O png"
]
},
{
"cell_type": "markdown",
"id": "695bb837",
"metadata": {},
"source": [
"![img](output/ug-gap-fill1.png)"
]
},
{
"cell_type": "markdown",
"id": "24d6a47e",
Expand Down
Binary file added notebooks/output/ug-gap-fill1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 13 additions & 13 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 18 additions & 3 deletions src/oaklib/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,10 @@ def annotate(words, output: str):
default=False,
show_default=True,
help="traverse down")
@click.option("--gap-fill/--no-gap-fill",
default=False,
show_default=True,
help="If set then find the minimal graph that spans all input curies")
@click.option('-S', '--stylemap',
help='a json file to configure visualization. See https://berkeleybop.github.io/kgviz-model/')
@click.option('-C', '--configure',
Expand All @@ -261,7 +265,7 @@ def annotate(words, output: str):
@click.option('-o', '--output',
help="Path to output file")
#@output_option
def viz(terms, predicates, down, view, stylemap, configure, output_type: str, output: str):
def viz(terms, predicates, down, gap_fill, view, stylemap, configure, output_type: str, output: str):
"""
Visualizing an ancestor graph using obographviz
Expand Down Expand Up @@ -308,17 +312,27 @@ def viz(terms, predicates, down, view, stylemap, configure, output_type: str, ou
curies = terms_expanded
if down:
graph = impl.subgraph(curies, predicates=actual_predicates)
elif gap_fill:
logging.info(f'Using gap-fill strategy')
if isinstance(impl, SubsetterInterface):
rels = impl.gap_fill_relationships(curies, predicates=actual_predicates)
if isinstance(impl, OboGraphInterface):
graph = impl.relationships_to_graph(rels)
else:
assert False
else:
raise NotImplementedError(f'{impl} needs to implement Subsetter for --gap-fill')
else:
graph = impl.ancestor_graph(curies, predicates=actual_predicates)
logging.info(f'Drawing graph seeded from {curies}')
if output_type == 'json':
if output:
json_dumper.dump(graph, to_file=output)
json_dumper.dump(graph, to_file=output, inject_type=False)
else:
print(json_dumper.dumps(graph))
elif output_type == 'yaml':
if output:
yaml_dumper.dump(graph, to_file=output)
yaml_dumper.dump(graph, to_file=output, inject_type=False)
else:
print(yaml_dumper.dumps(graph))
else:
Expand Down Expand Up @@ -369,6 +383,7 @@ def ancestors(terms, predicates, output: str):
else:
raise NotImplementedError(f'Cannot execute this using {impl} of type {type(impl)}')


@main.command()
@click.argument("terms", nargs=-1)
@predicates_option
Expand Down
23 changes: 18 additions & 5 deletions src/oaklib/implementations/sqldb/sql_implementation.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@
from abc import ABC
from collections import defaultdict
from dataclasses import dataclass
from typing import List, Any, Iterable, Optional, Type, Dict, Union, Tuple
from typing import List, Any, Iterable, Optional, Type, Dict, Union, Tuple, Iterator

from linkml_runtime import SchemaView
from linkml_runtime.utils.introspection import package_schemaview
from linkml_runtime.utils.metamodelcore import URIorCURIE
from oaklib.implementations.sqldb.model import Statements, Edge, HasSynonymStatement, \
HasTextDefinitionStatement, ClassNode, IriNode, RdfsLabelStatement, DeprecatedNode, EntailedEdge, \
ObjectPropertyNode, AnnotationPropertyNode, NamedIndividualNode, HasMappingStatement
from oaklib.interfaces.basic_ontology_interface import RELATIONSHIP_MAP, PRED_CURIE, ALIAS_MAP
from oaklib.interfaces import SubsetterInterface
from oaklib.interfaces.basic_ontology_interface import RELATIONSHIP_MAP, PRED_CURIE, ALIAS_MAP, RELATIONSHIP
from oaklib.interfaces.mapping_provider_interface import MappingProviderInterface
from oaklib.interfaces.obograph_interface import OboGraphInterface
from oaklib.interfaces.relation_graph_interface import RelationGraphInterface
Expand All @@ -20,6 +21,7 @@
from oaklib.datamodels import obograph, ontology_metadata
import oaklib.datamodels.validation_datamodel as vdm
from oaklib.datamodels.vocabulary import SYNONYM_PREDICATES, omd_slots, LABEL_PREDICATE, IN_SUBSET
from oaklib.utilities.graph.networkx_bridge import transitive_reduction_by_predicate
from sqlalchemy import select, text, exists
from sqlalchemy.orm import sessionmaker, aliased
from sqlalchemy import create_engine
Expand All @@ -38,7 +40,8 @@ def get_range_xsd_type(sv: SchemaView, rng: str) -> Optional[URIorCURIE]:


@dataclass
class SqlImplementation(RelationGraphInterface, OboGraphInterface, ValidatorInterface, SearchInterface, MappingProviderInterface, ABC):
class SqlImplementation(RelationGraphInterface, OboGraphInterface, ValidatorInterface, SearchInterface,
SubsetterInterface, MappingProviderInterface, ABC):
"""
A :class:`OntologyInterface` implementation that wraps a SQL Relational Database
Expand Down Expand Up @@ -381,5 +384,15 @@ def _check_slot(self, slot_name: str, class_name: str = 'Class') -> Iterable[vdm
)
yield result



def gap_fill_relationships(self, seed_curies: List[CURIE], predicates: List[PRED_CURIE] = None) -> Iterator[RELATIONSHIP]:
seed_curies = tuple(seed_curies)
q = self.session.query(EntailedEdge).filter(EntailedEdge.subject.in_(seed_curies))
q = q.filter(EntailedEdge.object.in_(seed_curies))
if predicates:
q = q.filter(EntailedEdge.predicate.in_(tuple(predicates)))
rels = []
for row in q:
if row.subject != row.object:
rels.append((row.subject, row.predicate, row.object))
for rel in transitive_reduction_by_predicate(rels):
yield rel
Loading

0 comments on commit 39215ed

Please sign in to comment.