Skip to content

Commit

Permalink
Fully qualify the time windows associated with the legacy CSV file na…
Browse files Browse the repository at this point in the history
…mes when generating outputs by valid date pool, #130
  • Loading branch information
james-d-brown committed Dec 24, 2024
1 parent 7d4394a commit 38c78e4
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,72 @@ void testDetectWithTrendAndOneEvent()

Set<TimeWindowOuter> expected = Set.of( TimeWindowOuter.of( expectedInner ) );

// Assert that one event is detected
assertEquals( expected, actual );
}

@Test
void testDetectWithTrendAndThreeEvents()
{
TimeSeries<Double> testSeries = this.createTestSeries( false );

// Duplicate the series three times
Instant start = testSeries.getEvents()
.first()
.getTime();
TimeSeries.Builder<Double> builder = new TimeSeries.Builder<Double>().setMetadata( testSeries.getMetadata() );
for ( int i = 0; i < 3; i++ )
{
for ( Event<Double> nextEvent : testSeries.getEvents() )
{
Event<Double> adjusted = Event.of( start, nextEvent.getValue() );
builder.addEvent( adjusted );
start = start.plus( Duration.ofHours( 1 ) );
}
}

testSeries = builder.build();

EventDetectionParameters parameters = EventDetectionParametersBuilder.builder()
.windowSize( Duration.ofDays( 7 ) )
.minimumEventDuration( Duration.ZERO )
.halfLife( Duration.ofHours( 6 ) )
.build();

ReginaOgdenEventDetector detector = ReginaOgdenEventDetector.of( parameters );

Set<TimeWindowOuter> actual = detector.detect( testSeries );

Instant startOne = Instant.parse( "2018-01-22T02:00:00Z" );
Instant endOne = Instant.parse( "2018-01-29T00:00:00Z" );

TimeWindow expectedInnerOne = MessageFactory.getTimeWindow()
.toBuilder()
.setEarliestValidTime( MessageFactory.getTimestamp( startOne ) )
.setLatestValidTime( MessageFactory.getTimestamp( endOne ) )
.build();

Instant startTwo = Instant.parse( "2018-03-04T18:00:00Z" );
Instant endTwo = Instant.parse( "2018-03-11T16:00:00Z" );

TimeWindow expectedInnerTwo = MessageFactory.getTimeWindow()
.toBuilder()
.setEarliestValidTime( MessageFactory.getTimestamp( startTwo ) )
.setLatestValidTime( MessageFactory.getTimestamp( endTwo ) )
.build();

Instant startThree = Instant.parse( "2018-04-15T10:00:00Z" );
Instant endThree = Instant.parse( "2018-04-22T08:00:00Z" );

TimeWindow expectedInnerThree = MessageFactory.getTimeWindow()
.toBuilder()
.setEarliestValidTime( MessageFactory.getTimestamp( startThree ) )
.setLatestValidTime( MessageFactory.getTimestamp( endThree ) )
.build();

Set<TimeWindowOuter> expected = Set.of( TimeWindowOuter.of( expectedInnerOne),
TimeWindowOuter.of( expectedInnerTwo ),
TimeWindowOuter.of( expectedInnerThree ) );

assertEquals( expected, actual );
}

Expand Down Expand Up @@ -158,7 +223,6 @@ void testDetectWithTrendAndOneEventAndNoise()

Set<TimeWindowOuter> expected = Set.of( TimeWindowOuter.of( expectedInner ) );

// Assert that one event is detected
assertEquals( expected, actual );
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package wres.writing.csv.statistics;

import java.io.IOException;
import java.nio.file.Path;
import java.text.Format;
import java.time.temporal.ChronoUnit;
Expand Down Expand Up @@ -103,35 +102,28 @@ public Set<Path> apply( List<DiagramStatisticOuter> statistics )
Format formatter = declaration.decimalFormat();

// Default, per time-window
try
{
// Group the statistics by the LRB context in which they appear. There will be one path written
// for each group (e.g., one path for each window with LeftOrRightOrBaseline.RIGHT data and one for
// each window with LeftOrRightOrBaseline.BASELINE data): #48287
Map<DatasetOrientation, List<DiagramStatisticOuter>> groups =
Slicer.getGroupedStatistics( statistics );
// Group the statistics by the LRB context in which they appear. There will be one path written
// for each group (e.g., one path for each window with LeftOrRightOrBaseline.RIGHT data and one for
// each window with LeftOrRightOrBaseline.BASELINE data): #48287
Map<DatasetOrientation, List<DiagramStatisticOuter>> groups =
Slicer.getGroupedStatistics( statistics );

for ( List<DiagramStatisticOuter> nextGroup : groups.values() )
for ( List<DiagramStatisticOuter> nextGroup : groups.values() )
{
// Slice by ensemble averaging type
List<List<DiagramStatisticOuter>> sliced =
CommaSeparatedDiagramWriter.getSlicedStatistics( nextGroup );
for ( List<DiagramStatisticOuter> nextSlice : sliced )
{
// Slice by ensemble averaging type
List<List<DiagramStatisticOuter>> sliced =
CommaSeparatedDiagramWriter.getSlicedStatistics( nextGroup );
for ( List<DiagramStatisticOuter> nextSlice : sliced )
{
Set<Path> innerPathsWrittenTo =
CommaSeparatedDiagramWriter.writeOneDiagramOutputType( super.getOutputDirectory(),
super.getDeclaration(),
nextSlice,
formatter,
super.getDurationUnits() );
paths.addAll( innerPathsWrittenTo );
}
Set<Path> innerPathsWrittenTo =
CommaSeparatedDiagramWriter.writeOneDiagramOutputType( super.getOutputDirectory(),
declaration,
nextSlice,
formatter,
super.getDurationUnits() );
paths.addAll( innerPathsWrittenTo );
}
}
catch ( IOException e )
{
throw new CommaSeparatedWriteException( "While writing comma separated output: ", e );
}

return Collections.unmodifiableSet( paths );
}
Expand All @@ -140,19 +132,17 @@ public Set<Path> apply( List<DiagramStatisticOuter> statistics )
* Writes all output for one diagram type.
*
* @param outputDirectory the directory into which to write
* @param declaration the project declaration
* @param declaration the declaration
* @param output the diagram output
* @param formatter optional formatter, can be null
* @param durationUnits the time units for durations
* @throws IOException if the output cannot be written
*/

private static Set<Path> writeOneDiagramOutputType( Path outputDirectory,
EvaluationDeclaration declaration,
List<DiagramStatisticOuter> output,
Format formatter,
ChronoUnit durationUnits )
throws IOException
{
Set<Path> pathsWrittenTo = new HashSet<>( 1 );

Expand All @@ -173,7 +163,8 @@ private static Set<Path> writeOneDiagramOutputType( Path outputDirectory,
Slicer.filter( output, m ),
headerRow,
formatter,
durationUnits );
durationUnits,
declaration );

pathsWrittenTo.addAll( innerPathsWrittenTo );

Expand All @@ -191,14 +182,16 @@ private static Set<Path> writeOneDiagramOutputType( Path outputDirectory,
* @param headerRow the header row
* @param formatter optional formatter, can be null
* @param durationUnits the time units for durations
* @param declaration the declaration
* @return set of paths actually written to
*/

private static Set<Path> writeOneDiagramOutputTypePerTimeWindow( Path outputDirectory,
List<DiagramStatisticOuter> output,
StringJoiner headerRow,
Format formatter,
ChronoUnit durationUnits )
ChronoUnit durationUnits,
EvaluationDeclaration declaration )
{
Set<Path> pathsWrittenTo = new HashSet<>( 1 );

Expand All @@ -208,7 +201,9 @@ private static Set<Path> writeOneDiagramOutputTypePerTimeWindow( Path outputDire
for ( TimeWindowOuter timeWindow : timeWindows )
{
List<DiagramStatisticOuter> next =
Slicer.filter( output, data -> data.getPoolMetadata().getTimeWindow().equals( timeWindow ) );
Slicer.filter( output, data -> data.getPoolMetadata()
.getTimeWindow()
.equals( timeWindow ) );

MetricConstants metricName = next.get( 0 ).getMetricName();
PoolMetadata meta = next.get( 0 ).getPoolMetadata();
Expand All @@ -222,7 +217,10 @@ private static Set<Path> writeOneDiagramOutputTypePerTimeWindow( Path outputDire
headerRow ) ) );

// Write the output
String append = CommaSeparatedDiagramWriter.getPathQualifier( timeWindow, durationUnits, output );
String append = CommaSeparatedDiagramWriter.getPathQualifier( timeWindow,
declaration,
durationUnits,
output );
Path outputPath = DataUtilities.getPathFromPoolMetadata( outputDirectory,
meta,
append,
Expand Down Expand Up @@ -483,20 +481,32 @@ private static List<List<DiagramStatisticOuter>> getSlicedStatistics( List<Diagr
/**
* Generates a path qualifier based on the statistics provided.
* @param timeWindow the time window
* @param declaration the declaration
* @param leadUnits the lead duration units
* @param statistics the statistics
* @return a path qualifier or null if non is required
*/

private static String getPathQualifier( TimeWindowOuter timeWindow,
EvaluationDeclaration declaration,
ChronoUnit leadUnits,
List<DiagramStatisticOuter> statistics )
{
// Pooling windows
if ( Objects.nonNull( declaration.validDatePools() )
|| Objects.nonNull( declaration.eventDetection() )
|| !declaration.timePools()
.isEmpty() )
{
return DataUtilities.toStringSafe( timeWindow, leadUnits );
}

// Qualify all windows with the latest lead duration
return DataUtilities.durationToNumericUnits( timeWindow.getLatestLeadDuration(),
leadUnits )
+ "_"
+ leadUnits.name().toUpperCase()
+ leadUnits.name()
.toUpperCase()
+ CommaSeparatedDiagramWriter.getEnsembleAverageQualifierString( statistics );
}

Expand Down

0 comments on commit 38c78e4

Please sign in to comment.