Skip to content

Commit

Permalink
Merge pull request #420 from monarch-initiative/release-v2.0.0-RC5
Browse files Browse the repository at this point in the history
Release `v2.0.0-RC5`
  • Loading branch information
ielis authored Sep 21, 2022
2 parents 4f3c8b2 + 81c8af0 commit 0da2c42
Show file tree
Hide file tree
Showing 75 changed files with 3,248 additions and 2,443 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ v2.0.0
- Support for GO GAF 2.2 files
- Speed up build by adding a new build profile
- ``phenol-core``

- ``MinimalOntology`` has a version
- ``phenol-io``
- Dropping support for reading OBO/OWL ontologies
- drop non-modular `curie-util` dependency
- ``phenol-annotations``
- Remodel ``HpoDisease``, ``HpoAnnotation``, ``HpoAssociationData``, ``GeneIdentifier``s, etc..
- ``HpoDiseases`` has a version
- Model temporal elements
- Implement ``HGNCGeneIdentifierLoader`` for reading ``GeneIdentifiers`` from HGNC complete set archive.
- Add new ``HpoOnset`` terms.
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ associate phenotype annotation files.

- **Language/Platform:** Java >=11
- **License:** BSD 3-Clause Clear
- **Version:** 2.0.0-RC4
- **Version:** 2.0.0-RC5
- **Authors:**
- Sebastian Bauer
- Peter N. Robinson
Expand All @@ -39,7 +39,7 @@ We recommend indicating the phenol version in the `properties` section of the po
```
<properties>
(...)
<phenol.version>2.0.0-RC4</phenol.version>
<phenol.version>2.0.0-RC5</phenol.version>
</properties>
```

Expand Down
2 changes: 1 addition & 1 deletion phenol-analysis/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>org.monarchinitiative.phenol</groupId>
<artifactId>phenol</artifactId>
<version>2.0.0-RC4</version>
<version>2.0.0-RC5</version>
</parent>

<artifactId>phenol-analysis</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ public interface AssociationContainer<T> {
Map<T, DirectAndIndirectTermAnnotations> getAssociationMap(Set<TermId> annotatedItemTermIds);

Set<T> getAllAnnotatedGenes();

/**
* @param tid The {@link TermId} of an ontology term (e.g. Gene Ontology) used to annotated domain items
* @return A set of domain items (e.g., genes) annotated by the ontology term
*/
Set<T> getDomainItemsAnnotatedByOntologyTerm(TermId tid);
/**
* @return total number of GO (or HP, MP, etc) terms that are annotating the items in this container.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.monarchinitiative.phenol.analysis;

import org.monarchinitiative.phenol.analysis.util.Util;
import org.monarchinitiative.phenol.annotations.formats.go.GoGaf22Annotation;
import org.monarchinitiative.phenol.annotations.io.go.GoGeneAnnotationParser;
import org.monarchinitiative.phenol.ontology.algo.OntologyAlgorithm;
Expand Down Expand Up @@ -86,6 +87,11 @@ public Set<TermId> getAllAnnotatedGenes() {
return this.gene2associationMap.keySet();
}

@Override
public Set<TermId> getDomainItemsAnnotatedByOntologyTerm(TermId tid) {
return Util.getDomainItemsAnnotatedByOntologyTerm(tid, ontology, gene2associationMap);
}

/**
* @return number of genes (or items) represented in this association container.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.monarchinitiative.phenol.analysis;


import org.monarchinitiative.phenol.analysis.util.Util;
import org.monarchinitiative.phenol.annotations.io.go.GoGeneAnnotationParser;
import org.monarchinitiative.phenol.base.PhenolException;
import org.monarchinitiative.phenol.ontology.algo.OntologyAlgorithm;
Expand Down Expand Up @@ -112,6 +113,11 @@ public Set<TermId> getAllAnnotatedGenes() {
return this.gene2associationMap.keySet();
}

@Override
public Set<TermId> getDomainItemsAnnotatedByOntologyTerm(TermId tid) {
return Util.getDomainItemsAnnotatedByOntologyTerm(tid, ontology, gene2associationMap);
}

/**
* @return number of genes (or items) represented in this association container.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package org.monarchinitiative.phenol.analysis.util;

import org.monarchinitiative.phenol.analysis.ItemAnnotations;
import org.monarchinitiative.phenol.ontology.algo.OntologyAlgorithm;
import org.monarchinitiative.phenol.ontology.data.Ontology;
import org.monarchinitiative.phenol.ontology.data.TermId;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class Util {

public static Set<TermId> getDomainItemsAnnotatedByOntologyTerm(TermId termId,
Ontology ontology,
Map<TermId, ? extends ItemAnnotations<TermId>> gene2associationMap) {
Set<TermId> domainItemSet = new HashSet<>();
// the following includes termId in the descendent set
Set<TermId> descendentSet = OntologyAlgorithm.getDescendents(ontology, termId);

for (Map.Entry<TermId, ? extends ItemAnnotations<TermId>> entry : gene2associationMap.entrySet()) {
TermId gene = entry.getKey();
for (TermId ontologyTermId : entry.getValue().getAnnotatingTermIds()) {
if (descendentSet.contains(ontologyTermId) || ontologyTermId.equals(termId)) {
domainItemSet.add(gene);
}
}
}

return domainItemSet;
}

}
2 changes: 1 addition & 1 deletion phenol-annotations/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>org.monarchinitiative.phenol</groupId>
<artifactId>phenol</artifactId>
<version>2.0.0-RC4</version>
<version>2.0.0-RC5</version>
</parent>

<artifactId>phenol-annotations</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package org.monarchinitiative.phenol.annotations.analysis;

import org.monarchinitiative.phenol.annotations.base.Ratio;
import org.monarchinitiative.phenol.annotations.base.temporal.TemporalInterval;
import org.monarchinitiative.phenol.annotations.formats.hpo.HpoDisease;
import org.monarchinitiative.phenol.annotations.formats.hpo.HpoDiseaseAnnotation;
Expand All @@ -17,9 +16,8 @@ private HpoOnsetDistributionSimple() {
@Override
public boolean isObservableInAge(HpoDisease disease, TemporalInterval interval) {
for (HpoDiseaseAnnotation annotation : disease.annotations()) {
Ratio ratio = annotation.observedInInterval(interval);
boolean isObservable = ratio.isPositive();
if (isObservable)
int nPatients = annotation.observedInInterval(interval);
if (nPatients > 0)
return true;
}
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
* {@link Age} is a {@link PointInTime} with associated {@link ConfidenceRange}. The {@link Age}
* can be <em>precise</em> or <em>imprecise</em> based on the associated {@link ConfidenceRange}.
* The {@link ConfidenceRange} also allows to interpret the {@link Age} as a {@link TemporalInterval}.
* <em>Precise</em> {@link Age} has {@link #length()} equal to <code>0</code>,
* while <em>imprecise</em> {@link Age} has {@link #length()} equal to <code>0 + len(cr)</code>, where <em>cr</em>
* is {@link #confidenceRange()}.
* <p>
* As is the case of the {@link PointInTime}, {@link Age} has a day precision.
*/
Expand All @@ -22,6 +25,20 @@ public interface Age extends PointInTime, TemporalInterval {
*/
float DAYS_IN_MONTH = DAYS_IN_JULIAN_YEAR / 12;

/**
* @return {@link Age} representing the last menstrual period before the conception.
*/
static Age lastMenstrualPeriod() {
return Ages.LMP;
}

/**
* @return {@link Age} representing birth.
*/
static Age birth() {
return Ages.BIRTH;
}

/**
* Create a precise gestational {@link Age} representing the number of weeks and months
* since the {@link PointInTime#lastMenstrualPeriod()}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ class Ages {
static final Age START = new AgeOpen(Integer.MIN_VALUE, true);
static final Age END = new AgeOpen(Integer.MAX_VALUE, false);

private static final Age LMP = new AgeGestational.AgeGestationalPrecise(0);
private static final Age BIRTH = new AgePostnatal.AgePostnatalPrecise(0);
static final Age LMP = new AgeGestational.AgeGestationalPrecise(0);
static final Age BIRTH = new AgePostnatal.AgePostnatalPrecise(0);

static Age gestational(int days, ConfidenceRange cr) {
if (cr.isPrecise())
Expand Down Expand Up @@ -50,6 +50,11 @@ public boolean isOpen() {
return false;
}

@Override
public int length() {
return confidenceRange().length();
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
Expand Down Expand Up @@ -168,6 +173,11 @@ public boolean isOpen() {
return false;
}

@Override
public int length() {
return confidenceRange().length();
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,41 @@ default boolean isPositive() {
return days() > 0;
}

/**
* @return {@code true} if {@code this} {@link PointInTime} occurs <em>before</em> the {@code other}.
*/
default boolean isBefore(PointInTime other) {
return compare(this, other) < 0;
}

/**
* @return {@code true} if {@code this} {@link PointInTime} occurs <em>at the same time</em> or <em>before</em> the {@code other}.
*/
default boolean isAtOrBefore(PointInTime other) {
return compare(this, other) <= 0;
}

/**
* @return {@code true} if {@code this} {@link PointInTime} occurs <em>after</em> the {@code other}.
*/
default boolean isAfter(PointInTime other) {
return !isAtOrBefore(other);
}

/**
* @return {@code true} if {@code this} {@link PointInTime} occurs <em>at the same time</em> or <em>after</em> the {@code other}.
*/
default boolean isAtOrAfter(PointInTime other) {
return !isBefore( other);
}

/**
* @return {@code true} if {@code this} {@link PointInTime} occurs <em>at the same time</em> as the {@code other}.
*/
default boolean isAt(PointInTime other) {
return compare(this, other) == 0;
}

/**
* @return the greater {@link PointInTime} based on comparing {@code a} and {@code b} using {@link #compare(PointInTime, PointInTime)}.
* {@code a} is returned in case of a tie.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,32 @@
*/
public interface TemporalInterval {

/**
* Provide {@link TemporalInterval} to represent the span of the gestational lifetime.
* The {@link TemporalInterval} is delimited by {@link PointInTime#lastMenstrualPeriod()}
* and {@link PointInTime#birth()}.
* <p>
* Note that length of the gestational lifetime is {@link Integer#MAX_VALUE}
*
* @return the {@link TemporalInterval} representing the gestational lifetime
*/
static TemporalInterval gestationalPeriod() {
return TemporalIntervals.GESTATIONAL_PERIOD;
}

/**
* Provide {@link TemporalInterval} to represent the span of the postnatal lifetime.
* The {@link TemporalInterval} is delimited by {@link PointInTime#birth()} ()}
* and {@link PointInTime#openEnd()}.
* <p>
* Note that the postnatal lifetime is half-open, hence the length is {@link Integer#MAX_VALUE}.
*
* @return the {@link TemporalInterval} representing the postnatal lifetime
*/
static TemporalInterval postnatalPeriod() {
return TemporalIntervals.POSTNATAL_PERIOD;
}

/**
* Create a {@link TemporalInterval} using <code>start</code> and <code>end</code>.
* The <code>start</code> must <em>not</em> be after end.
Expand Down Expand Up @@ -94,12 +120,14 @@ default boolean isFullyClosed() {

/**
* Get the number of days spanned by <code>this</code> {@link TemporalInterval}.
* If {@link #isStartOpen()} or {@link #isEndOpen()}, then the length is equal to {@link Integer#MAX_VALUE}.
* <p>
* Note: the length of a half-open or fully-open interval, or an interval with endpoints located
* on different timelines (e.g. {@link TemporalInterval#gestationalPeriod()}) is equal to {@link Integer#MAX_VALUE}.
*
* @return the number of days.
*/
default int length() {
return isFullyClosed()
return isFullyClosed() && start().isGestational() == end().isGestational()
? end().days() - start().days()
: Integer.MAX_VALUE;
}
Expand All @@ -121,9 +149,31 @@ default TemporalInterval intersection(TemporalInterval other) {
return TemporalInterval.of(start, end);
}

/**
* @return {@code true} if {@code this} and {@code other} overlap.
*/
default boolean overlapsWith(TemporalInterval other) {
// TODO - calculate without allocating
return !intersection(other).isEmpty();
switch (temporalOverlapType(other)) {
case BEFORE:
case AFTER:
return false;
case BEFORE_AND_DURING:
case CONTAINS:
case CONTAINED_IN:
case DURING_AND_AFTER:
return true;
default:
throw new RuntimeException(String.format("Unknown item %s", temporalOverlapType(other)));
}
}

/**
* Get {@link TemporalOverlapType} for {@link TemporalInterval}s {@code this} and {@code other}.
* <p>
* Note: the method returns {@link TemporalOverlapType#CONTAINED_IN} if {@code this} and {@code other} are equal.
*/
default TemporalOverlapType temporalOverlapType(TemporalInterval other) {
return temporalOverlapType(this, other);
}

default boolean contains(PointInTime age) {
Expand All @@ -147,4 +197,37 @@ static int compare(TemporalInterval x, TemporalInterval y) {
return PointInTime.compare(x.end(), y.end());
}

/**
* Get {@link TemporalOverlapType} for {@link TemporalInterval}s {@code x} and {@code y}.
* <p>
* Note: the method returns {@link TemporalOverlapType#CONTAINED_IN} if {@code x} and {@code y} are equal.
*/
private static TemporalOverlapType temporalOverlapType(TemporalInterval x, TemporalInterval y) {
if (x.end().isAtOrBefore(y.start())) {
return x.isEmpty() && x.end().isAt(y.start())
? TemporalOverlapType.CONTAINED_IN
: TemporalOverlapType.BEFORE;
} else if (x.start().isAtOrAfter(y.end())) {
return x.isEmpty() && x.start().isAt(y.end())
? TemporalOverlapType.CONTAINED_IN
: TemporalOverlapType.AFTER;
} else {
// We have some sort of overlap here.
int startCompare = PointInTime.compare(x.start(), y.start()); // Cache the start comparison result.
if (startCompare < 0) {
// The block handles `x.start().isBefore(y.start())`.
if (x.end().isBefore(y.end()))
return TemporalOverlapType.BEFORE_AND_DURING;
else
return TemporalOverlapType.CONTAINS;
} else { // The block handles `x.start().isAtOrAfter(y.start())`.
if (x.end().isAtOrBefore(y.end())) {
return TemporalOverlapType.CONTAINED_IN;
} else return startCompare == 0 // This is true if `x.start().isAt(y.start())`.
? TemporalOverlapType.CONTAINS
: TemporalOverlapType.DURING_AND_AFTER;
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
class TemporalIntervals {

static final TemporalInterval BIRTH = TemporalInterval.of(PointsInTime.BIRTH, PointsInTime.BIRTH);
static final TemporalInterval GESTATIONAL_PERIOD = TemporalInterval.of(PointInTime.lastMenstrualPeriod(), PointInTime.birth());
static final TemporalInterval POSTNATAL_PERIOD = TemporalInterval.of(PointInTime.birth(), PointInTime.openEnd());

static final TemporalInterval OPEN = new OpenTemporalInterval();

Expand Down
Loading

0 comments on commit 0da2c42

Please sign in to comment.