Skip to content

Commit

Permalink
Merge pull request #352 from monarch-initiative/devries_score_update
Browse files Browse the repository at this point in the history
Devries score update
  • Loading branch information
ielis authored Nov 15, 2024
2 parents 4263bca + 79e14ec commit f71109f
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 58 deletions.
42 changes: 29 additions & 13 deletions docs/user-guide/predicates/devries.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Statistical significance of a difference in the De Vries score between groups ca
determined using the Mann-Whitney-U test.

We refer to `Feenstra et al. (2011) <https://pubmed.ncbi.nlm.nih.gov/21712853/>`_ for
the original description of the adjusted De Vries score. Here we offer a version of the
the original description of the adjusted De Vries score. Here we offer an adapted version of the
score that leverages the structure of the Human Phenotype Ontology to assess the phenotype.


Expand Down Expand Up @@ -113,38 +113,54 @@ is 2 because the same individual cannot have both tall and short stature or both
Facial dysmorphic features
~~~~~~~~~~~~~~~~~~~~~~~~~~

This section assigns two points if two or more anomalies are identified in the following
categories: hypertelorism, nasal anomalies and ear anomalies. Our implementation of this feature counts the total
number of terms or descendents of the following HPO terms.
This section assigns two points if two or more facial dysmorphisms are identified. In contrast to the list of anomalies described
in the original 2011 publication of the DeVries score, we leverage the structure of the HPO to include many more HPO terms that
denote various kinds of facial dysmorphism (e.g., `Abnormality of globe location <https://hpo.jax.org/browse/term/HP:0100886>`_ instead of just
`Hypertelorism (HP:0000316) <https://hpo.jax.org/browse/term/HP:0000316>`_).

Our implementation of this feature counts the total number of terms or descendents of the following HPO terms. Up to one point is given
for each of the categories.

+----------------------------------------------------------------------------------------------------------+-----------+
| HPO term | Score |
+==========================================================================================================+===========+
| `Hypertelorism (HP:0000316) <https://hpo.jax.org/browse/term/HP:0000316>`_ | 1 |
| `Abnormality of globe location (HP:0000316) <https://hpo.jax.org/browse/term/HP:0100886>`_ | 0 or 1 |
+----------------------------------------------------------------------------------------------------------+-----------+
| `Abnormal lip morphology (HP:0000159) <https://hpo.jax.org/browse/term/HP:0000159>`_ | 0 or 1 |
+----------------------------------------------------------------------------------------------------------+-----------+
| `Abnormal facial shape (HP:0001999) <https://hpo.jax.org/browse/term/HP:0001999>`_ | 0 or 1 |
+----------------------------------------------------------------------------------------------------------+-----------+
| `Abnormal midface morphology (HP:0000309) <https://hpo.jax.org/browse/term/HP:0000309>`_ | 0 or 1 |
+----------------------------------------------------------------------------------------------------------+-----------+
| `Abnormal external nose morphology (HP:0010938) <https://hpo.jax.org/browse/term/HP:0010938>`_ | 1 each |
| `Abnormal forehead morphology (HP:0000290) <https://hpo.jax.org/browse/term/HP:0000290>`_ | 0 or 1 |
+----------------------------------------------------------------------------------------------------------+-----------+
| `Abnormal pinna morphology (HP:0000377) <https://hpo.jax.org/browse/term/HP:0000377>`_ | 1 each |
| `Abnormal chin morphology (HP:0000306) <https://hpo.jax.org/browse/term/HP:0000306>`_ | 0 or 1 |
+----------------------------------------------------------------------------------------------------------+-----------+
| `Abnormal external nose morphology (HP:0010938) <https://hpo.jax.org/browse/term/HP:0010938>`_ | 0 or 1 |
+----------------------------------------------------------------------------------------------------------+-----------+
| `Abnormal pinna morphology (HP:0000377) <https://hpo.jax.org/browse/term/HP:0000377>`_ | 0 or 1 |
+----------------------------------------------------------------------------------------------------------+-----------+

If two or more terms are found, the score is 2, otherwise a score of zero is assigned.
If items from two or more categories are found, the score is 2, otherwise a score of zero is assigned.


Non-facial dysmorphism and congenital abnormalities
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
One point is assigned for either the
corresponding HPO terms or any of their descendents up to a maximum of two points.
One point is assigned for either the corresponding HPO terms or any of their descendents up to a maximum of two points.
A maximum of one point is assigned for each of the following categories.

+----------------------------------------------------------------------------------------------------------+-----------+
| HPO term | Score |
+==========================================================================================================+===========+
| `Abnormal hand morphology (HP:0005922) <https://hpo.jax.org/browse/term/HP:0005922>`_ | 1 each |
| `Abnormal hand morphology (HP:0005922) <https://hpo.jax.org/browse/term/HP:0005922>`_ | 0 or 1 |
+----------------------------------------------------------------------------------------------------------+-----------+
| `Abnormal heart morphology (HP:0001627) <https://hpo.jax.org/browse/term/HP:0001627>`_ | 1 each |
| `Abnormal heart morphology (HP:0001627) <https://hpo.jax.org/browse/term/HP:0001627>`_ | 0 or 1 |
+----------------------------------------------------------------------------------------------------------+-----------+
| `Hypospadias (HP:0000047) <https://hpo.jax.org/browse/term/HP:0000047>`_ | 1 |
| `Abnormal external genitalia morphology (HP:0000811) <https://hpo.jax.org/browse/term/HP:0000811>`_ | 0 or 1 |
+----------------------------------------------------------------------------------------------------------+-----------+

The score for this section can thus be 0, 1, or 2.


Final score
~~~~~~~~~~~
Expand Down
72 changes: 27 additions & 45 deletions src/gpsea/analysis/pscore/_hpo.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ def _developmental_delay_score(
) -> float:
"""
Calculate the dev delay component of the score
Args:
observed_term_ids: terms observed in patient
Expand All @@ -177,32 +177,12 @@ def _developmental_delay_score(
for t in observed_term_ids:
if t in self._gdd_tids:
return self._gdd_tids[t]

# Intellectual disability
for t in observed_term_ids:
if t in self._idd_tids:
return self._idd_tids[t]

return 0

def _term_or_descendant(
self,
target_tid: str,
observed_term_ids: typing.Iterable[str],
) -> int:
"""
Args:
target_tid: term of interest
observed_term_ids: all terms observed in patient

Returns:
1 if the term or any descendant is present in the patient, otherwise 0
"""
for term_id in observed_term_ids:
if term_id == target_tid \
or any(ancestor == target_tid for ancestor in self._hpo.graph.get_ancestors(term_id)):
return 1

return 0

def _term_or_descendant_count(
Expand All @@ -216,22 +196,20 @@ def _term_or_descendant_count(
observed_term_ids: all terms observed in patient
Returns:
the total count of the terms equal to or descending from the target_tid
1 if at least one term is equal to or descending from the target_tid, otherwise 0
"""
total_count = 0
for term_id in observed_term_ids:
for desc_tid in self._hpo.graph.get_ancestors(term_id, include_source=True):
if desc_tid.value == target_tid:
total_count += 1
return total_count
if term_id == target_tid or self._hpo.graph.is_descendant_of(term_id, target_tid):
return 1
return 0

def _postnatal_growth_score(
self,
observed_term_ids: typing.Iterable[str],
) -> int:
"""
Calculate the postnatal growth component of the score.
Args:
observed_term_ids: terms observed in patient
Expand All @@ -243,7 +221,7 @@ def _postnatal_growth_score(
tall_stature = 'HP:0000098'
total_count = 0
for tid in (microcephaly, short_stature, macrocephaly, tall_stature):
total_count += self._term_or_descendant(tid, observed_term_ids)
total_count += self._term_or_descendant_count(tid, observed_term_ids)
if total_count > 2:
raise ValueError(f"Inconsistent annotations for postnatal growth score {total_count}: {observed_term_ids}")
return total_count
Expand All @@ -264,14 +242,22 @@ def _facial_dysmorphism_score(
Returns: facial dysmorphism score (between 0 and 2)
"""
hypertelorism = 'HP:0000316'
globe_location = 'HP:0100886' # include Hypertelorism and others
lip = 'HP:0000159' # Abnormal lip morphology HP:0000159
external_nose = 'HP:0010938'
pinna_morphology = 'HP:0000377'
facial_shape = 'HP:0001999' # Abnormal facial shape
midface = 'HP:0000309' # Abnormal midface morphology
chin = 'HP:0000306' # Abnormality of the chin

# No need to inspect descendants since Hypertelorism has none.
total_count = 1 if hypertelorism in observed_term_ids else 0
total_count = self._term_or_descendant_count(target_tid=globe_location, observed_term_ids=observed_term_ids)
total_count += self._term_or_descendant_count(target_tid=lip, observed_term_ids=observed_term_ids)
total_count += self._term_or_descendant_count(target_tid=external_nose, observed_term_ids=observed_term_ids)
total_count += self._term_or_descendant_count(target_tid=pinna_morphology, observed_term_ids=observed_term_ids)
total_count += self._term_or_descendant_count(target_tid=facial_shape, observed_term_ids=observed_term_ids)
total_count += self._term_or_descendant_count(target_tid=midface, observed_term_ids=observed_term_ids)
total_count += self._term_or_descendant_count(target_tid=chin, observed_term_ids=observed_term_ids)

if total_count > 1:
return 2
else:
Expand All @@ -284,24 +270,20 @@ def _congenital_score(
"""
Non-facial dysmorphism and congenital abnormalities component.
One point is assigned for either the corresponding HPO terms or any of their descendents up to a maximum of 2.
Args:
observed_term_ids: terms observed in patient
Returns: Non-facial dysmorphism and congenital abnormalities score (between 0 and 2)
"""
hypospadias = 'HP:0000047'
abn_external_genitalia = 'HP:0000811' # Abnormal external genitalia
abnormal_hand_morphology = 'HP:0005922'
abnormal_heart_morphology = 'HP:0001627'
# total_count = len([t for t in observed_term_ids if t == hypospadias])
total_count = self._term_or_descendant_count(
target_tid=hypospadias, observed_term_ids=observed_term_ids,
)
total_count += self._term_or_descendant_count(target_tid=abnormal_hand_morphology,
observed_term_ids=observed_term_ids)
total_count += self._term_or_descendant_count(target_tid=abnormal_heart_morphology,
observed_term_ids=observed_term_ids)

total_count = self._term_or_descendant_count(target_tid=abn_external_genitalia, observed_term_ids=observed_term_ids,)
total_count += self._term_or_descendant_count(target_tid=abnormal_hand_morphology, observed_term_ids=observed_term_ids)
total_count += self._term_or_descendant_count(target_tid=abnormal_heart_morphology, observed_term_ids=observed_term_ids)
return min(2, total_count)

def _prenatal_growth_score(
Expand All @@ -328,7 +310,7 @@ def _prenatal_growth_score(
def score(self, patient: Patient) -> float:
"""
Calculate score based on list of strings with term identifiers or observed HPO terms.
Args:
patient: list of strings with term identifiers or observed HPO terms
Expand All @@ -342,5 +324,5 @@ def score(self, patient: Patient) -> float:
facial_score = self._facial_dysmorphism_score(observed_term_ids)
congen_score = self._congenital_score(observed_term_ids)
prenatal_score = self._prenatal_growth_score(observed_term_ids)

return delay_score + growth_score + facial_score + congen_score + prenatal_score

0 comments on commit f71109f

Please sign in to comment.