001/*-------------------------------------------------------------------------+ 002| | 003| Copyright 2005-2011 The ConQAT Project | 004| | 005| Licensed under the Apache License, Version 2.0 (the "License"); | 006| you may not use this file except in compliance with the License. | 007| You may obtain a copy of the License at | 008| | 009| http://www.apache.org/licenses/LICENSE-2.0 | 010| | 011| Unless required by applicable law or agreed to in writing, software | 012| distributed under the License is distributed on an "AS IS" BASIS, | 013| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 014| See the License for the specific language governing permissions and | 015| limitations under the License. | 016+-------------------------------------------------------------------------*/ 017package org.conqat.lib.commons.math; 018 019import java.util.Collection; 020 021/** 022 * Calculates the variance with a two-pass algorithm. Implementation is highly 023 * similar to the one used in Apache Commons. 024 */ 025public class VarianceAggregator implements IAggregator { 026 027 /** 028 * If true, sample variance is calculated, population variance otherwise. 029 */ 030 private final boolean sample; 031 032 /** 033 * Constructor. 034 * 035 * @param sample 036 * if true sample variance is calculated, population variance 037 * otherwise. 038 */ 039 public VarianceAggregator(boolean sample) { 040 this.sample = sample; 041 } 042 043 /** 044 * Aggregates by finding the average value 045 * 046 * @return {@link Double#NaN} for empty input collection 047 */ 048 @Override 049 public double aggregate(Collection<? extends Number> values) { 050 if (values.isEmpty()) { 051 return Double.NaN; 052 } 053 return calcVariance(values, MathUtils.mean(values)); 054 } 055 056 /** 057 * Calculate variance. The implementation is based on the algorithm 058 * described at 059 * http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance 060 */ 061 private double calcVariance(Collection<? extends Number> values, double mean) { 062 063 if (values.size() == 1) { 064 return 0; 065 } 066 067 double totalDeviationSquared = 0.0; 068 double totalDeviation = 0.0; 069 070 for (Number value : values) { 071 double deviation = value.doubleValue() - mean; 072 totalDeviationSquared += deviation * deviation; 073 totalDeviation += deviation; 074 } 075 076 double x = totalDeviationSquared - (totalDeviation * totalDeviation / values.size()); 077 if (sample) { 078 return x / (values.size() - 1); 079 } 080 081 return x / values.size(); 082 } 083 084 /** Returns {@link Double#NaN}. */ 085 @Override 086 public double getNeutralElement() { 087 return Double.NaN; 088 } 089}