Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Devries score update #352

Merged
merged 4 commits into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
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
51 changes: 27 additions & 24 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,12 +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(
Expand All @@ -202,7 +202,7 @@ def _term_or_descendant(
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 +216,21 @@ 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
return 1
Copy link
Member

@ielis ielis Nov 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pnrobinson
note that the methods _term_or_descendant_count (this) and _term_or_descendant (above) are now functionally identical.

Is that on purpose?

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 Down Expand Up @@ -264,14 +263,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 +291,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 +331,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 +345,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
Loading