Skip to content

Commit

Permalink
Simplify cross-pairing and improve documentation, #36.
Browse files Browse the repository at this point in the history
  • Loading branch information
james-d-brown committed Aug 29, 2024
1 parent cae067a commit 27f236d
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 45 deletions.
17 changes: 10 additions & 7 deletions wres-config/nonsrc/schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1489,13 +1489,16 @@ definitions:
is present, evaluates common events by reference time and valid time. If
absent, retains all events of each type. With fuzzy matching, each time-
series is matched with its nearest, corresponding, time-series according to
the total duration between all reference times of a corresponding type. In
other words, if there is an exact match, that will be used, else the time-
series whose reference times are nearest overall. Once a time-series has
been matched, it cannot be re-used. Always uses exact matching for valid
times. The scope of the cross-pairing always includes the predicted and
baseline datasets, where defined, and may be further controlled using the
'scope' parameter."
the total duration between all reference times across the candidate time-
series. In other words, if there is an exact match, that will be used, else
the time-series whose reference times are nearest overall. With exact
matching, both the type of reference time and the time itself must
correspond. With fuzzy matching, all types of reference time are considered
equal and hence used to calculate the total duration between all reference
times of the candidate series. Once a time-series has been matched, it
cannot be re-used. Always uses exact matching for valid times. The scope of
the cross-pairing always includes the predicted and baseline datasets, where
defined, and may be further controlled using the 'scope' parameter."
type: string
enum:
- exact
Expand Down
54 changes: 25 additions & 29 deletions wres-datamodel/src/wres/datamodel/time/TimeSeriesCrossPairer.java
Original file line number Diff line number Diff line change
Expand Up @@ -359,9 +359,31 @@ private <P, Q> Duration getTotalDurationBetweenCommonTimeTypes( TimeSeries<P> fi
Set<ReferenceTimeType> common = new HashSet<>( firstTimes.keySet() );
common.retainAll( secondTimes.keySet() );

// Filter non-matching reference time types
if ( method != CrossPairMethod.FUZZY )
// For exact matching, the reference time types must match
if ( method == CrossPairMethod.EXACT )
{
if ( common.isEmpty() )
{
throw new PairingException( "Encountered an error while inspecting time-series to cross-pair. "
+ "Attempted to calculate the total duration between the commonly typed "
+ "reference times of two time-series, but no commonly typed reference "
+ "times were discovered, which is not allowed. For lenient cross-pairing "
+ "that considers all types of reference time equivalent, declare the "
+ "'fuzzy' cross-pairing method instead of 'exact'. The first time-series "
+ "was: "
+ first.getMetadata()
+ ". The second time-series was: "
+ second.getMetadata()
+ ". The first time-series had reference time types of: "
+ first.getReferenceTimes()
.keySet()
+ ". The second time-series had reference time types of: "
+ second.getReferenceTimes()
.keySet()
+ "." );
}

// Filter non-matching reference time types
firstTimes = firstTimes.entrySet()
.stream()
.filter( e -> common.contains( e.getKey() ) )
Expand All @@ -372,36 +394,10 @@ private <P, Q> Duration getTotalDurationBetweenCommonTimeTypes( TimeSeries<P> fi
.collect( Collectors.toMap( Map.Entry::getKey, Map.Entry::getValue ) );
}

if ( firstTimes.isEmpty() || secondTimes.isEmpty() )
{
String append = "";
if ( method != CrossPairMethod.FUZZY )
{
append = "For lenient cross-pairing that considers all types of reference time equivalent, declare the "
+ "'fuzzy' cross-pairing method. ";
}

throw new PairingException( "Encountered an error while inspecting time-series to cross-pair. Attempted to "
+ "calculate the total duration between the commonly typed "
+ "reference times of two time-series, but no commonly typed reference times "
+ "were discovered, which is not allowed. "
+ append
+ "The first time-series was: "
+ first.getMetadata()
+ ". The second time-series was: "
+ second.getMetadata()
+ ". The first time-series had reference time types of: "
+ first.getReferenceTimes()
.keySet()
+ ". The second time-series had reference time types of: "
+ second.getReferenceTimes()
.keySet()
+ "." );
}

// The neutral difference
Duration returnMe = Duration.ZERO;

// Iterate through the differences and sum them
for ( Instant firstInstant : firstTimes.values() )
{
for ( Instant secondInstant : secondTimes.values() )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
Expand Down Expand Up @@ -57,7 +58,7 @@ void runBeforeEachTest()
}

@Test
void testCrossPairTwoTimeSeriesWithEqualReferenceTimesThatEachAppearTwice()
void testCrossPairTwoTimeSeriesWithEqualReferenceTimesThatEachAppearTwiceAndFuzzyMatching()
{
Event<Pair<Integer, Integer>> first = Event.of( FIRST, Pair.of( 1, 1 ) );
Event<Pair<Integer, Integer>> second = Event.of( SECOND, Pair.of( 2, 2 ) );
Expand Down Expand Up @@ -120,7 +121,7 @@ void testCrossPairTwoTimeSeriesWithEqualReferenceTimesThatEachAppearTwice()
}

@Test
void testCrossPairTimeSeriesWithSomeEqualReferenceTimes()
void testCrossPairTimeSeriesWithSomeEqualReferenceTimesOfDifferentTypesAndFuzzyMatching()
{
Event<Pair<Integer, Integer>> first = Event.of( FIRST, Pair.of( 1, 1 ) );

Expand All @@ -139,7 +140,7 @@ void testCrossPairTimeSeriesWithSomeEqualReferenceTimes()
Event<Pair<Integer, Integer>> second = Event.of( SECOND, Pair.of( 2, 2 ) );

TimeSeriesMetadata secondMetadata =
TimeSeriesMetadata.of( Collections.singletonMap( ReferenceTimeType.T0,
TimeSeriesMetadata.of( Collections.singletonMap( ReferenceTimeType.ISSUED_TIME,
FIRST ),
TimeScaleOuter.of(),
CHICKENS,
Expand Down Expand Up @@ -181,7 +182,7 @@ void testCrossPairTimeSeriesWithSomeEqualReferenceTimes()
}

@Test
void testCrossPairTimeSeriesWithNoEqualReferenceTimesOrValidTimes()
void testCrossPairTimeSeriesWithNoEqualReferenceTimesOrValidTimesWhenFuzzyMatching()
{
Event<Pair<Integer, Integer>> first = Event.of( FIRST, Pair.of( 1, 1 ) );

Expand Down Expand Up @@ -222,7 +223,7 @@ void testCrossPairTimeSeriesWithNoEqualReferenceTimesOrValidTimes()
}

@Test
void testCrossPairTwoTimeSeriesWithEqualReferenceTimesAndNoEqualValidTimes()
void testCrossPairTwoTimeSeriesWithEqualReferenceTimesAndNoEqualValidTimesWhenFuzzyMatching()
{
Event<Pair<Integer, Integer>> first = Event.of( FIRST, Pair.of( 1, 1 ) );

Expand Down Expand Up @@ -263,7 +264,7 @@ void testCrossPairTwoTimeSeriesWithEqualReferenceTimesAndNoEqualValidTimes()
}

@Test
void testCrossPairTimeSeriesWithNoEqualReferenceTimesAndSomeEqualValidTimes()
void testCrossPairTimeSeriesWithNoEqualReferenceTimesAndSomeEqualValidTimesWhenFuzzyMatching()
{
Event<Pair<Integer, Integer>> first = Event.of( FIRST, Pair.of( 1, 1 ) );

Expand Down Expand Up @@ -353,7 +354,7 @@ void testCrossPairTimeSeriesWithNoEqualReferenceTimesAndSomeEqualValidTimesWhenE
}

@Test
void testCrossPairTwoTimeSeriesWithNoReferenceTimes()
void testCrossPairTwoTimeSeriesWithNoReferenceTimesAndFuzzyMatching()
{
Event<Pair<Integer, Integer>> first = Event.of( FIRST, Pair.of( 1, 1 ) );

Expand Down Expand Up @@ -391,7 +392,7 @@ void testCrossPairTwoTimeSeriesWithNoReferenceTimes()
}

@Test
void testCrossPairTimeSeriesWithSomeNearbyReferenceTimes()
void testCrossPairTimeSeriesWithSomeNearbyReferenceTimesAndFuzzyMatching()
{
Event<Pair<Integer, Integer>> first = Event.of( FIRST, Pair.of( 1, 1 ) );

Expand Down Expand Up @@ -489,7 +490,7 @@ void testCrossPairTimeSeriesWithSomeNearbyReferenceTimes()
}

@Test
void testCrossPairTimeSeriesWithNoEqualReferenceTimeTypes()
void testCrossPairTimeSeriesWithNoEqualReferenceTimeTypesThrowsPairingException()
{
Event<Pair<Integer, Integer>> first = Event.of( FIRST, Pair.of( 1, 1 ) );

Expand Down Expand Up @@ -535,6 +536,31 @@ void testCrossPairTimeSeriesWithNoEqualReferenceTimeTypes()
.contains( "no commonly typed reference times" ) );
}

@Test
void testCrossPairWithEmptyBaselineProducesEmptyCrossPairsWhenFuzzyMatching()
{
Event<Pair<Integer, Integer>> first = Event.of( FIRST, Pair.of( 1, 1 ) );

TimeSeriesMetadata firstMetadata =
TimeSeriesMetadata.of( Collections.singletonMap( ReferenceTimeType.T0,
ZEROTH ),
TimeScaleOuter.of(),
CHICKENS,
GEORGIA,
KG_H );

TimeSeries<Pair<Integer, Integer>> firstSeries =
new Builder<Pair<Integer, Integer>>().setMetadata( firstMetadata )
.addEvent( first )
.build();

CrossPairs<Pair<Integer, Integer>, Pair<Integer, Integer>> cp = this.instance.apply( List.of( firstSeries ),
List.of() );

assertAll( () -> assertTrue( cp.getFirstPairs().isEmpty() ),
() -> assertTrue( cp.getSecondPairs().isEmpty() ) );
}

@Test
void testCrossPairProducesSymmetricallyShapedPairs()
{
Expand Down

0 comments on commit 27f236d

Please sign in to comment.