/* * Copyright 2014 the original author or authors. * * 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 org.gradle.performance.measure; import com.google.common.collect.Lists; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * A collection of measurements of some given units. */ public class DataSeries<Q> extends ArrayList<Amount<Q>> { private final Amount<Q> average; private final Amount<Q> median; private final Amount<Q> max; private final Amount<Q> min; // https://en.wikipedia.org/wiki/Standard_error private final Amount<Q> standardError; // https://en.wikipedia.org/wiki/Standard_error#Standard_error_of_the_mean private final Amount<Q> standardErrorOfMean; public DataSeries(Iterable<? extends Amount<Q>> values) { for (Amount<Q> value : values) { if (value != null) { add(value); } } if (isEmpty()) { average = null; median = null; max = null; min = null; standardError = null; standardErrorOfMean = null; return; } Amount<Q> total = get(0); Amount<Q> min = get(0); Amount<Q> max = get(0); for (int i = 1; i < size(); i++) { Amount<Q> amount = get(i); total = total.plus(amount); min = min.compareTo(amount) <= 0 ? min : amount; max = max.compareTo(amount) >= 0 ? max : amount; } List<Amount<Q>> sorted = Lists.newArrayList(this); Collections.sort(sorted); Amount<Q> medianLeft = sorted.get((sorted.size() - 1) / 2); Amount<Q> medianRight = sorted.get((sorted.size() - 1) / 2 + 1 - sorted.size() % 2); median = medianLeft.plus(medianRight).div(2); average = total.div(size()); this.min = min; this.max = max; BigDecimal sumSquares = BigDecimal.ZERO; Units<Q> baseUnits = average.getUnits().getBaseUnits(); BigDecimal averageValue = average.toUnits(baseUnits).getValue(); for (int i = 0; i < size(); i++) { Amount<Q> amount = get(i); BigDecimal diff = amount.toUnits(baseUnits).getValue(); diff = diff.subtract(averageValue); diff = diff.multiply(diff); sumSquares = sumSquares.add(diff); } // This isn't quite right, as we may lose precision when converting to a double BigDecimal result = BigDecimal.valueOf(Math.sqrt(sumSquares.divide(BigDecimal.valueOf(size()), BigDecimal.ROUND_HALF_UP).doubleValue())).setScale(2, BigDecimal.ROUND_HALF_UP); standardError = Amount.valueOf(result, baseUnits); standardErrorOfMean = standardError.div(BigDecimal.valueOf(Math.sqrt(size()))); } public Amount<Q> getAverage() { return average; } public Amount<Q> getMedian() { return median; } public Amount<Q> getMin() { return min; } public Amount<Q> getMax() { return max; } public Amount<Q> getStandardError() { return standardError; } public Amount<Q> getStandardErrorOfMean() { return standardErrorOfMean; } }