Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@
"sqlalchemy": ("https://docs.sqlalchemy.org/en/latest", None),
"requests": ("https://requests.kennethreitz.org/en/master/", None),
"setuptools": ("https://setuptools.readthedocs.io/en/latest/", None),
"bioregistry": ("https://bioregistry.readthedocs.io/en/latest/", None),
"pandas": ("https://pandas.pydata.org/docs", None),
"sklearn": ("https://scikit-learn.org/stable", None),
"numpy": ("https://numpy.org/doc/stable", None),
Expand Down
1 change: 1 addition & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ PyOBO |release| Documentation
cli
usage
functional
ner
scispacy

Indices and Tables
Expand Down
61 changes: 61 additions & 0 deletions docs/source/ner.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
Named Entity Recognition
========================

PyOBO has high-level wrappers to construct literal mapping objects defined by
:mod:`ssslm.LiteralMapping`, which can be used to construct generic named entity
recognition (NER) and named entity normalization (NEN) tooling (e.g., using ScispaCy or
Gilda as a backend)

You can use :func:`pyobo.ground` as an integrated workflow:

.. code-block:: python

import pyobo
import ssslm

matches: list[ssslm.Match] = pyobo.ground("taxrank", "species")

You can get the grounder directly first using :func:`pyobo.get_grounder`:

.. code-block:: python

import pyobo
import ssslm

grounder: ssslm.Grounder = pyobo.get_grounder("taxrank")
matches: list[ssslm.Match] = grounder.get_matches("species")

You can get the ontology directly using :func:`pyobo.get_ontology` then construct a
grounder with :meth:`pyobo.Obo.get_grounder`:

.. code-block:: python

import pyobo
import ssslm

ontology: pyobo.Obo = pyobo.get_ontology("taxrank")
grounder: ssslm.Grounder = ontology.get_grounder()
matches: list[ssslm.Match] = grounder.get_matches("species")

You can load a custom ontology with :func:`pyobo.from_obo_path` then construct a
grounder with :meth:`pyobo.Obo.get_grounder`:

.. code-block:: python

import pyobo
import ssslm
from urllib.request import urlretrieve

url = "http://purl.obolibrary.org/obo/taxrank.obo"
path = "taxrank.obo"
urlretrieve(url, path)

ontology: pyobo.Obo = pyobo.from_obo_path(path, prefix="taxrank")
grounder: ssslm.Grounder = ontology.get_grounder()
matches: list[ssslm.Match] = grounder.get_matches("species")

.. warning::

When loading a custom ontology, it's required that the prefix is registered in the
:mod:`bioregistry`, since PyOBO does additional standardization and normalization of
prefixes, CURIEs, and URIs that are not part of the OBO specification.
40 changes: 40 additions & 0 deletions src/pyobo/struct/struct.py
Original file line number Diff line number Diff line change
Expand Up @@ -1903,6 +1903,46 @@ def get_id_synonyms_mapping(self, *, use_tqdm: bool = False) -> Mapping[str, lis
"""Get a mapping from identifiers to a list of sorted synonym strings."""
return multidict(self.iterate_synonym_rows(use_tqdm=use_tqdm))

def get_grounder(self) -> ssslm.Grounder:
"""Get a grounder from this ontology.

:returns: An object that can be used for named entity recognition and named
entity normalization

Here's example usage for a built-in ontology:

.. code-block:: python

import pyobo
import ssslm

ontology = pyobo.get_ontology("taxrank")
grounder: ssslm.Grounder = ontology.get_grounder()
matches: list[ssslm.Match] = grounder.ground("species")

Here's example usage for a custom ontology:

.. code-block:: python

import pyobo
import ssslm
from urllib.request import urlretrieve

url = "http://purl.obolibrary.org/obo/taxrank.obo"
path = "taxrank.obo"
urlretrieve(url, path)

ontology = pyobo.from_obo_path(path, prefix="taxrank")
grounder: ssslm.Grounder = ontology.get_grounder()
matches: list[ssslm.Match] = grounder.get_matches("species")

.. warning::

It's required to tell PyOBO the prefix for a custom ontology when using
:func:`pyobo.from_obo_path`, and it must be registered in the Bioregistry
"""
return ssslm.make_grounder(self.get_literal_mappings())

def get_literal_mappings(self) -> Iterable[ssslm.LiteralMapping]:
"""Get literal mappings in a standard data model."""
stanzas: Iterable[Stanza] = itt.chain(self, self.typedefs or [])
Expand Down
18 changes: 18 additions & 0 deletions tests/test_obo_reader/test_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -1362,3 +1362,21 @@ def test_get_references(self) -> None:
"orcid": {ADNAN_MALIK},
}
self.assertEqual(expected_references, ontology._get_references())

def test_get_grounder(self) -> None:
"""Test getting a grounder from an ontology."""
ontology = from_str("""\
ontology: chebi
date: 20:11:2024 18:44

[Term]
id: CHEBI:16236
name: ethanol
""")
r1 = Reference(prefix="CHEBI", identifier="16236", name="ethanol")
grounder = ontology.get_grounder()
match = grounder.get_best_match("Ethanol")
self.assertIsNotNone(match)
if match is None:
raise ValueError
self.assertEqual(r1, match.reference)
Loading