@Override
 public void aggregateTemporal(
     BinContext ctx, Vector spatialVector, int numSpatialObs, WritableVector temporalVector) {
   GrowableVector measurementsVec = ctx.get(mlName);
   float value = spatialVector.get(0);
   if (!Float.isNaN(value)) {
     measurementsVec.add(value);
   }
 }
 @Override
 public void completeSpatial(BinContext ctx, int numSpatialObs, WritableVector spatialVector) {
   Integer invalidCount = ((int[]) ctx.get(icName))[0];
   int effectiveCount = numSpatialObs - invalidCount;
   if (effectiveCount > 0) {
     spatialVector.set(0, spatialVector.get(0) / effectiveCount);
   } else {
     spatialVector.set(0, Float.NaN);
   }
 }
 @Override
 public void completeTemporal(BinContext ctx, int numTemporalObs, WritableVector temporalVector) {
   GrowableVector measurementsVec = ctx.get(mlName);
   float[] measurements = measurementsVec.getElements();
   if (measurements.length > 0) {
     Arrays.sort(measurements);
     temporalVector.set(0, computePercentile(percentage, measurements));
   } else {
     temporalVector.set(0, Float.NaN);
   }
 }
 @Override
 public void aggregateSpatial(
     BinContext ctx, Observation observationVector, WritableVector spatialVector) {
   float value = observationVector.get(varIndex);
   if (!Float.isNaN(value)) {
     spatialVector.set(0, spatialVector.get(0) + value);
   } else {
     // We count invalids rather than valid because it is more efficient.
     // (Key/value map operations are relatively slow, and it is more likely that we will receive
     // valid measurements.)
     ((int[]) ctx.get(icName))[0]++;
   }
 }