import math
from typing import List
from pyhpo.similarity.base import SimilarityBase
import pyhpo
[docs]
class Resnik(SimilarityBase):
"""
Based on *Resnik P, Proceedings of the 14th IJCAI, (1995)*
https://www.ijcai.org/Proceedings/95-1/Papers/059.pdf
"""
def __call__(
self,
term1: "pyhpo.HPOTerm",
term2: "pyhpo.HPOTerm",
kind: str,
dependencies: List[float],
) -> float:
sim = 0.0
for term in term1.common_ancestors(term2):
ic = term.information_content[kind]
if ic > sim:
sim = ic
return sim
[docs]
class Lin(SimilarityBase):
"""
Based on *Lin D, Proceedings of the 15th ICML, (1998)*
https://dl.acm.org/doi/10.5555/645527.657297
"""
dependencies: List[str] = ["resnik"]
def __call__(
self,
term1: "pyhpo.HPOTerm",
term2: "pyhpo.HPOTerm",
kind: str,
dependencies: List[float],
) -> float:
ic_t1 = term1.information_content[kind]
ic_t2 = term2.information_content[kind]
try:
return (2 * dependencies[0]) / (ic_t1 + ic_t2)
except ZeroDivisionError:
return 0.0
[docs]
class JC(SimilarityBase):
"""
Jiang & Conrath similarity Score, based on
*Jiang J, Conrath D, Rocling X, (1997) and
Deng Y, et. al., PLoS One, (2015)*
https://aclanthology.org/O97-1002.pdf
.. note::
This method was previously wrongly implemented
and fixed in 3.3.0 based on `this discussion <https://github.com/anergictcell/pyhpo/issues/20>`_
"""
dependencies: List[str] = ["resnik"]
def __call__(
self,
term1: "pyhpo.HPOTerm",
term2: "pyhpo.HPOTerm",
kind: str,
dependencies: List[float],
) -> float:
if term1 == term2:
return 1.0
ic_t1 = term1.information_content[kind]
ic_t2 = term2.information_content[kind]
if ic_t1 == 0.0 or ic_t2 == 0.0:
return 0.0
return 1.0 / (ic_t1 + ic_t2 - (2.0 * dependencies[0]) + 1.0)
[docs]
class Relevance(SimilarityBase):
"""
Based on *Schlicker A, et.al., BMC Bioinformatics, (2006)*
https://bmcbioinformatics.biomedcentral.com/articles/10.1186/1471-2105-7-302
"""
dependencies: List[str] = ["resnik", "lin"]
def __call__(
self,
term1: "pyhpo.HPOTerm",
term2: "pyhpo.HPOTerm",
kind: str,
dependencies: List[float],
) -> float:
return dependencies[1] * (1 - (math.exp(dependencies[0] * -1)))
[docs]
class GraphIC(SimilarityBase):
"""
Graph based Information coefficient, based on
*Deng Y, et. al., PLoS One, (2015)*
https://pubmed.ncbi.nlm.nih.gov/25664462/
"""
def __call__(
self,
term1: "pyhpo.HPOTerm",
term2: "pyhpo.HPOTerm",
kind: str,
dependencies: List[float],
) -> float:
if term1 == term2:
return 1.0
common = sum(
[x.information_content[kind] for x in term1.common_ancestors(term2)]
)
union = sum(
[
x.information_content[kind]
for x in (term1.all_parents | term2.all_parents)
]
)
try:
return common / union
except ZeroDivisionError:
return 0.0
[docs]
class Distance(SimilarityBase):
"""
actual distance (number of hpos) between Terms
"""
def __call__(
self,
term1: "pyhpo.HPOTerm",
term2: "pyhpo.HPOTerm",
kind: str,
dependencies: List[float],
) -> float:
try:
dist = term1.path_to_other(term2)[0]
except IndexError:
# No path found -> This can only happen if one of the
# terms is obsolete or otherwise not part of the Ontology
return 0.0
return 1 / (dist + 1)
def register_defaults(simscore: "pyhpo.similarity.base._Similarity") -> None:
simscore.register("resnik", Resnik)
simscore.register("lin", Lin)
simscore.register("jc", JC)
simscore.register("jc2", JC)
simscore.register("rel", Relevance)
simscore.register("ic", InformationCoefficient)
simscore.register("graphic", GraphIC)
simscore.register("dist", Distance)