/* * Copyright 2015 by Thomas Lottermann * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package notaql.evaluation; import notaql.datamodel.NumberValue; import notaql.datamodel.Value; import notaql.datamodel.fixation.Fixation; import notaql.evaluation.values.PartialAvgValue; import notaql.model.EvaluationException; import notaql.model.vdata.VData; import notaql.model.vdata.aggregation.AvgVData; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; /** * Evaluates the AVG() function. */ public class AvgVDataEvaluator implements Evaluator, Reducer { private static final long serialVersionUID = -1334411928804069430L; /** * Evaluate averages. This only operates on Number values. * * This produces pre-aggregated values which still contain a weight. * @param vData * @param fixation * @return */ @Override public List<ValueEvaluationResult> evaluate(VData vData, Fixation fixation) { assert vData instanceof AvgVData; final AvgVData avgVData = (AvgVData) vData; final VData argument = avgVData.getExpression(); final List<ValueEvaluationResult> results = EvaluatorService.getInstance().evaluate(argument, fixation); final List<Value> values = results.stream().map(ValueEvaluationResult::getValue).collect(Collectors.toList()); if (values.stream().anyMatch(v -> !(v instanceof NumberValue))) throw new EvaluationException("AVG aggregation function encountered values which are not numbers."); final PartialAvgValue avgValue = new PartialAvgValue( values.stream() .map(a -> ((NumberValue) a).getValue()) .collect(Collectors.averagingDouble(Number::doubleValue)), values.size() ); return Arrays.asList(new ValueEvaluationResult(avgValue, fixation)); } @Override public boolean canReduce(VData vData) { return true; } /** * Adds the values with the correct weights. * * @param vData The instance of vData, that this reducer is built for * @param v1 * @param v2 * @return */ @Override public PartialAvgValue reduce(VData vData, Value v1, Value v2) { assert v1 instanceof PartialAvgValue && v2 instanceof PartialAvgValue; final PartialAvgValue pv1 = (PartialAvgValue) v1; final PartialAvgValue pv2 = (PartialAvgValue) v2; if(pv1.getCount() == 0) return pv2; if(pv2.getCount() == 0) return pv1; int count = (pv1.getCount() + pv2.getCount()); return new PartialAvgValue( (pv1.getValue().doubleValue() / count * pv1.getCount() + pv2.getValue().doubleValue() / count * pv2.getCount()), pv1.getCount() + pv2.getCount() ); } @Override public Value createIdentity(VData vData) { return new PartialAvgValue(0, 0); } @Override public Value finalize(VData vData, Value value) { assert value instanceof PartialAvgValue; return new NumberValue(((PartialAvgValue)value).getValue()); } @Override public List<Class<? extends VData>> getProcessedClasses() { return Arrays.asList(AvgVData.class); } }