Added first batch of weighted statistics

* mean
  * sum
  * product
  * variance
JIRA: MATH-287
Thanks to Matthew Rowles

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@809448 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Phil Steitz 2009-08-31 01:56:09 +00:00
parent b91ed85747
commit dd63599d2a
13 changed files with 624 additions and 114 deletions

View File

@ -178,6 +178,9 @@
<contributor> <contributor>
<name>Andreas Rieger</name> <name>Andreas Rieger</name>
</contributor> </contributor>
<contributor>
<name>Matthew Rowles</name>
</contributor>
<contributor> <contributor>
<name>Gilles Sadowski</name> <name>Gilles Sadowski</name>
</contributor> </contributor>

View File

@ -102,4 +102,75 @@ public abstract class AbstractUnivariateStatistic
return true; return true;
} }
/**
* This method is used by <code>evaluate(double[], double[], int, int)</code> methods
* to verify that the begin and length parameters designate a subarray of positive length
* and the weights are all non-negative, non-NaN, finite, and not all zero.
* <p>
* <ul>
* <li>returns <code>true</code> iff the parameters designate a subarray of
* positive length and the weights array contains legitimate values.</li>
* <li>throws <code>IllegalArgumentException</code> if any of the following are true:
* <ul><li>the values array is null</li>
* <li>the weights array is null</li>
* <li>the weights array does not have the same length as the values array</li>
* <li>the weights array contains one or more infinite values</li>
* <li>the weights array contains one or more NaN values</li>
* <li>the weights array contains negative values</li>
* <li>the start and length arguments do not determine a valid array</li></ul>
* </li>
* <li>returns <code>false</li> if the array is non-null, but
* <code>length</code> is 0.
* </ul></p>
*
* @param values the input array
* @param weights the weights array
* @param begin index of the first array element to include
* @param length the number of elements to include
* @return true if the parameters are valid and designate a subarray of positive length
* @throws IllegalArgumentException if the indices are invalid or the array is null
*/
protected boolean test(
final double[] values,
final double[] weights,
final int begin,
final int length) {
if (weights == null) {
throw MathRuntimeException.createIllegalArgumentException("input weights array is null");
} }
if (weights.length != values.length) {
throw MathRuntimeException.createIllegalArgumentException(
"Different number of weights and values");
}
boolean containsPositiveWeight = false;
for (int i = begin; i < begin + length; i++) {
if (Double.isNaN(weights[i])) {
throw MathRuntimeException.createIllegalArgumentException(
"NaN weight at index {0}", i);
}
if (Double.isInfinite(weights[i])) {
throw MathRuntimeException.createIllegalArgumentException(
"Infinite weight at index {0}", i);
}
if (weights[i] < 0) {
throw MathRuntimeException.createIllegalArgumentException(
"negative weight {0} at index {1} ", weights[i], i);
}
if (!containsPositiveWeight && weights[i] > 0.0) {
containsPositiveWeight = true;
}
}
if (!containsPositiveWeight) {
throw MathRuntimeException.createIllegalArgumentException(
"weight array must contain at least one non-zero value");
}
return test(values, begin, length);
}
}

View File

@ -168,6 +168,53 @@ public class Mean extends AbstractStorelessUnivariateStatistic
return Double.NaN; return Double.NaN;
} }
/**
* Returns the weighted arithmetic mean of the entries in the specified portion of
* the input array, or <code>Double.NaN</code> if the designated subarray
* is empty.
* <p>
* Throws <code>IllegalArgumentException</code> if either array is null.</p>
* <p>
* See {@link Mean} for details on the computing algorithm. The two-pass algorithm
* described above is used here, with weights applied in computing both the original
* estimate and the correction factor.</p>
* <p>
* Throws <code>IllegalArgumentException</code> if any of the following are true:
* <ul><li>the values array is null</li>
* <li>the weights array is null</li>
* <li>the weights array does not have the same length as the values array</li>
* <li>the weights array contains one or more infinite values</li>
* <li>the weights array contains one or more NaN values</li>
* <li>the weights array contains negative values</li>
* <li>the start and length arguments do not determine a valid array</li>
* </ul></p>
*
* @param values the input array
* @param weights the weights array
* @param begin index of the first array element to include
* @param length the number of elements to include
* @return the mean of the values or Double.NaN if length = 0
* @throws IllegalArgumentException if the parameters are not valid
*/
public double evaluate(final double[] values, final double[] weights,
final int begin, final int length) {
if (test(values, weights, begin, length)) {
Sum sum = new Sum();
// Compute initial estimate using definitional formula
double sumw = sum.evaluate(weights,begin,length);
double xbarw = sum.evaluate(values, weights, begin, length) / sumw;
// Compute correction factor in second pass
double correction = 0;
for (int i = begin; i < begin + length; i++) {
correction += weights[i] * (values[i] - xbarw);
}
return xbarw + (correction/sumw);
}
return Double.NaN;
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */

View File

@ -20,6 +20,7 @@ import java.io.Serializable;
import org.apache.commons.math.MathRuntimeException; import org.apache.commons.math.MathRuntimeException;
import org.apache.commons.math.stat.descriptive.AbstractStorelessUnivariateStatistic; import org.apache.commons.math.stat.descriptive.AbstractStorelessUnivariateStatistic;
import org.apache.commons.math.stat.descriptive.summary.Sum;
/** /**
* Computes the variance of the available values. By default, the unbiased * Computes the variance of the available values. By default, the unbiased
@ -42,7 +43,7 @@ import org.apache.commons.math.stat.descriptive.AbstractStorelessUnivariateStati
* full array of values in memory to execute a two-pass algorithm. * full array of values in memory to execute a two-pass algorithm.
* Specifically, these methods use the "corrected two-pass algorithm" from * Specifically, these methods use the "corrected two-pass algorithm" from
* Chan, Golub, Levesque, <i>Algorithms for Computing the Sample Variance</i>, * Chan, Golub, Levesque, <i>Algorithms for Computing the Sample Variance</i>,
* American Statistician, August 1983.</li></ul> * American Statistician, vol. 37, no. 3 (1983) pp. 242-247.</li></ul>
* Note that adding values using <code>increment</code> or * Note that adding values using <code>increment</code> or
* <code>incrementAll</code> and then executing <code>getResult</code> will * <code>incrementAll</code> and then executing <code>getResult</code> will
* sometimes give a different, less accurate, result than executing * sometimes give a different, less accurate, result than executing
@ -255,6 +256,65 @@ public class Variance extends AbstractStorelessUnivariateStatistic implements Se
return var; return var;
} }
/**
* <p>Returns the weighted variance of the entries in the specified portion of
* the input array, or <code>Double.NaN</code> if the designated subarray
* is empty.</p>
* <p>
* Uses the formula <pre>
* &Sigma;(weights[i]*(values[i] - weightedMean)<sup>2</sup>)/(&Sigma;(weights[i]) - 1)
* </pre>
* where weightedMean is the weighted mean</p>
* <p>
* This formula will not return the same result as the unweighted variance when all
* weights are equal, unless all weights are equal to 1. The formula assumes that
* weights are to be treated as "expansion values," as will be the case if for example
* the weights represent frequency counts. To normalize weights so that the denominator
* in the variance computation equals the length of the input vector minus one, use <pre>
* <code>evaluate(values, MathUtils.normalizeArray(weights, values.length)); </code>
* </pre>
* <p>
* Returns 0 for a single-value (i.e. length = 1) sample.</p>
* <p>
* Throws <code>IllegalArgumentException</code> if any of the following are true:
* <ul><li>the values array is null</li>
* <li>the weights array is null</li>
* <li>the weights array does not have the same length as the values array</li>
* <li>the weights array contains one or more infinite values</li>
* <li>the weights array contains one or more NaN values</li>
* <li>the weights array contains negative values</li>
* <li>the start and length arguments do not determine a valid array</li>
* </ul></p>
* <p>
* Does not change the internal state of the statistic.</p>
* <p>
* Throws <code>IllegalArgumentException</code> if either array is null.</p>
*
* @param values the input array
* @param weights the weights array
* @param begin index of the first array element to include
* @param length the number of elements to include
* @return the variance of the values or Double.NaN if length = 0
* @throws IllegalArgumentException if the parameters are not valid
*/
public double evaluate(final double[] values, final double[] weights,
final int begin, final int length) {
double var = Double.NaN;
if (test(values, weights,begin, length)) {
clear();
if (length == 1) {
var = 0.0;
} else if (length > 1) {
Mean mean = new Mean();
double m = mean.evaluate(values, weights, begin, length);
var = evaluate(values, weights, m, begin, length);
}
}
return var;
}
/** /**
* Returns the variance of the entries in the specified portion of * Returns the variance of the entries in the specified portion of
* the input array, using the precomputed mean value. Returns * the input array, using the precomputed mean value. Returns
@ -309,6 +369,81 @@ public class Variance extends AbstractStorelessUnivariateStatistic implements Se
return var; return var;
} }
/**
* Returns the weighted variance of the entries in the specified portion of
* the input array, using the precomputed weighted mean value. Returns
* <code>Double.NaN</code> if the designated subarray is empty.
* <p>
* Uses the formula <pre>
* &Sigma;(weights[i]*(values[i] - mean)<sup>2</sup>)/(&Sigma;(weights[i]) - 1)
* </pre></p>
* <p>
* The formula used assumes that the supplied mean value is the weighted arithmetic
* mean of the sample data, not a known population parameter. This method
* is supplied only to save computation when the mean has already been
* computed.</p>
* <p>
* This formula will not return the same result as the unweighted variance when all
* weights are equal, unless all weights are equal to 1. The formula assumes that
* weights are to be treated as "expansion values," as will be the case if for example
* the weights represent frequency counts. To normalize weights so that the denominator
* in the variance computation equals the length of the input vector minus one, use <pre>
* <code>evaluate(values, MathUtils.normalizeArray(weights, values.length)); </code>
* </pre>
* <p>
* Returns 0 for a single-value (i.e. length = 1) sample.</p>
* <p>
* Throws <code>IllegalArgumentException</code> if any of the following are true:
* <ul><li>the values array is null</li>
* <li>the weights array is null</li>
* <li>the weights array does not have the same length as the values array</li>
* <li>the weights array contains one or more infinite values</li>
* <li>the weights array contains one or more NaN values</li>
* <li>the weights array contains negative values</li>
* <li>the start and length arguments do not determine a valid array</li>
* </ul></p>
* <p>
* Does not change the internal state of the statistic.</p>
*
* @param values the input array
* @param weights the weights array
* @param mean the precomputed weighted mean value
* @param begin index of the first array element to include
* @param length the number of elements to include
* @return the variance of the values or Double.NaN if length = 0
* @throws IllegalArgumentException if the parameters are not valid
*/
public double evaluate(final double[] values, final double[] weights,
final double mean, final int begin, final int length) {
double var = Double.NaN;
if (test(values, weights, begin, length)) {
if (length == 1) {
var = 0.0;
} else if (length > 1) {
double accum = 0.0;
double dev = 0.0;
for (int i = begin; i < begin + length; i++) {
dev = values[i] - mean;
accum += weights[i] * (dev * dev);
}
double sumWts = 0;
for (int i = 0; i < weights.length; i++) {
sumWts += weights[i];
}
if (isBiasCorrected) {
var = accum / (sumWts - 1);
} else {
var = accum / sumWts;
}
}
}
return var;
}
/** /**
* Returns the variance of the entries in the input array, using the * Returns the variance of the entries in the input array, using the
* precomputed mean value. Returns <code>Double.NaN</code> if the array * precomputed mean value. Returns <code>Double.NaN</code> if the array

View File

@ -19,6 +19,7 @@ package org.apache.commons.math.stat.descriptive.summary;
import java.io.Serializable; import java.io.Serializable;
import org.apache.commons.math.stat.descriptive.AbstractStorelessUnivariateStatistic; import org.apache.commons.math.stat.descriptive.AbstractStorelessUnivariateStatistic;
import org.apache.commons.math.stat.descriptive.AbstractUnivariateStatistic;
/** /**
* Returns the product of the available values. * Returns the product of the available values.
@ -127,6 +128,45 @@ public class Product extends AbstractStorelessUnivariateStatistic implements Ser
return product; return product;
} }
/**
* <p>Returns the weighted product of the entries in the specified portion of
* the input array, or <code>Double.NaN</code> if the designated subarray
* is empty.</p>
*
* <p>Throws <code>IllegalArgumentException</code> if any of the following are true:
* <ul><li>the values array is null</li>
* <li>the weights array is null</li>
* <li>the weights array does not have the same length as the values array</li>
* <li>the weights array contains one or more infinite values</li>
* <li>the weights array contains one or more NaN values</li>
* <li>the weights array contains negative values</li>
* <li>the start and length arguments do not determine a valid array</li>
* </ul></p>
*
* <p>Uses the formula, <pre>
* weighted product = &prod;values[i]<sup>weights[i]</sup>
* </pre>
* that is, the weights are applied as exponents when computing the weighted product.</p>
*
* @param values the input array
* @param weights the weights array
* @param begin index of the first array element to include
* @param length the number of elements to include
* @return the product of the values or Double.NaN if length = 0
* @throws IllegalArgumentException if the parameters are not valid
*/
public double evaluate(final double[] values, final double[] weights,
final int begin, final int length) {
double product = Double.NaN;
if (test(values, weights, begin, length)) {
product = 1.0;
for (int i = begin; i < begin + length; i++) {
product *= Math.pow(values[i], weights[i]);
}
}
return product;
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */

View File

@ -127,6 +127,44 @@ public class Sum extends AbstractStorelessUnivariateStatistic implements Seriali
return sum; return sum;
} }
/**
* The weighted sum of the entries in the specified portion of
* the input array, or <code>Double.NaN</code> if the designated subarray
* is empty.
* <p>
* Throws <code>IllegalArgumentException</code> if any of the following are true:
* <ul><li>the values array is null</li>
* <li>the weights array is null</li>
* <li>the weights array does not have the same length as the values array</li>
* <li>the weights array contains one or more infinite values</li>
* <li>the weights array contains one or more NaN values</li>
* <li>the weights array contains negative values</li>
* <li>the start and length arguments do not determine a valid array</li>
* </ul></p>
* <p>
* Uses the formula, <pre>
* weighted sum = &Sigma;(values[i] * weights[i])
* </pre></p>
*
* @param values the input array
* @param weights the weights array
* @param begin index of the first array element to include
* @param length the number of elements to include
* @return the sum of the values or Double.NaN if length = 0
* @throws IllegalArgumentException if the parameters are not valid
*/
public double evaluate(final double[] values, final double[] weights,
final int begin, final int length) {
double sum = Double.NaN;
if (test(values, weights, begin, length)) {
sum = 0.0;
for (int i = begin; i < begin + length; i++) {
sum += (values[i] * weights[i]);
}
}
return sum;
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */

View File

@ -39,6 +39,9 @@ The <action> type attribute can be add,update,fix,remove.
</properties> </properties>
<body> <body>
<release version="2.1" date="TBD" description="TBD"> <release version="2.1" date="TBD" description="TBD">
<action dev="psteitz" tyoe="add" issue="MATH-287" due-to="Matthew Rowles">
Added support for weighted descriptive statistics.
</action>
<action dev="psteitz" type="add"> <action dev="psteitz" type="add">
Added normalizeArray method to MathUtils. Added normalizeArray method to MathUtils.
</action> </action>

View File

@ -40,6 +40,8 @@ public class AbstractUnivariateStatisticTest extends TestCase {
} }
protected double[] testArray = {0, 1, 2, 3, 4, 5}; protected double[] testArray = {0, 1, 2, 3, 4, 5};
protected double[] testWeightsArray = {0.3, 0.2, 1.3, 1.1, 1.0, 1.8};
protected double[] testNegativeWeightsArray = {-0.3, 0.2, -1.3, 1.1, 1.0, 1.8};
protected double[] nullArray = null; protected double[] nullArray = null;
protected double[] singletonArray = {0}; protected double[] singletonArray = {0};
protected Mean testStatistic = new Mean(); protected Mean testStatistic = new Mean();
@ -86,5 +88,23 @@ public class AbstractUnivariateStatisticTest extends TestCase {
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
// expected // expected
} }
try {
testStatistic.test(testArray, nullArray, 0, 1); // null weights array
fail("Expecting IllegalArgumentException");
} catch (IllegalArgumentException ex) {
// expected
}
try {
testStatistic.test(singletonArray, testWeightsArray, 0, 1); // weights.length != value.length
fail("Expecting IllegalArgumentException");
} catch (IllegalArgumentException ex) {
// expected
}
try {
testStatistic.test(testArray, testNegativeWeightsArray, 0, 6); // can't have negative weights
fail("Expecting IllegalArgumentException");
} catch (IllegalArgumentException ex) {
// expected
}
} }
} }

View File

@ -8,7 +8,7 @@
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software s * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
@ -16,8 +16,16 @@
*/ */
package org.apache.commons.math.stat.descriptive; package org.apache.commons.math.stat.descriptive;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.commons.math.TestUtils;
import org.apache.commons.math.random.RandomData;
import org.apache.commons.math.random.RandomDataImpl;
/** /**
* Test cases for the {@link UnivariateStatistic} class. * Test cases for the {@link UnivariateStatistic} class.
* @version $Revision$ $Date$ * @version $Revision$ $Date$
@ -46,12 +54,35 @@ public abstract class UnivariateStatisticAbstractTest extends TestCase {
protected double thirdMoment = 868.0906859504136; protected double thirdMoment = 868.0906859504136;
protected double fourthMoment = 9244.080993773481; protected double fourthMoment = 9244.080993773481;
protected double weightedMean = 12.366995073891626d;
protected double weightedVar = 9.974760968886391d;
protected double weightedStd = Math.sqrt(weightedVar);
protected double weightedProduct = 8517647448765288000000d;
protected double weightedSum = 251.05d;
protected double tolerance = 10E-12; protected double tolerance = 10E-12;
protected double[] testArray = protected double[] testArray =
{12.5, 12, 11.8, 14.2, 14.9, 14.5, 21, 8.2, 10.3, 11.3, { 12.5, 12.0, 11.8, 14.2, 14.9, 14.5, 21.0, 8.2, 10.3, 11.3,
14.1, 9.9, 12.2, 12, 12.1, 11, 19.8, 11, 10, 8.8, 14.1, 9.9, 12.2, 12.0, 12.1, 11.0, 19.8, 11.0, 10.0, 8.8,
9, 12.3 }; 9.0, 12.3 };
protected double[] testWeightsArray =
{ 1.5, 0.8, 1.2, 0.4, 0.8, 1.8, 1.2, 1.1, 1.0, 0.7,
1.3, 0.6, 0.7, 1.3, 0.7, 1.0, 0.4, 0.1, 1.4, 0.9,
1.1, 0.3 };
protected double[] identicalWeightsArray =
{ 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
0.5, 0.5 };
protected double[] unitWeightsArray =
{ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
1.0, 1.0 };
public UnivariateStatisticAbstractTest(String name) { public UnivariateStatisticAbstractTest(String name) {
super(name); super(name);
@ -81,4 +112,70 @@ public abstract class UnivariateStatisticAbstractTest extends TestCase {
getTolerance()); getTolerance());
} }
/**
* Tests consistency of weighted statistic computation.
* For statistics that support weighted evaluation, this test case compares
* the result of direct computation on an array with repeated values with
* a weighted computation on the corresponding (shorter) array with each
* value appearing only once but with a weight value equal to its multiplicity
* in the repeating array.
*/
public void testWeightedConsistency() throws Exception {
// See if this statistic computes weighted statistics
// If not, skip this test
UnivariateStatistic statistic = getUnivariateStatistic();
Method evaluateMethod = null;
try {
evaluateMethod = statistic.getClass().getDeclaredMethod("evaluate",
double[].class, double[].class, int.class, int.class);
} catch (NoSuchMethodException ex) {
return; // skip test
}
// Create arrays of values and corresponding integral weights
// and longer array with values repeated according to the weights
final int len = 10; // length of values array
final double mu = 0; // mean of test data
final double sigma = 5; // std dev of test data
double[] values = new double[len];
double[] weights = new double[len];
RandomData randomData = new RandomDataImpl();
// Fill weights array with random int values between 1 and 5
int[] intWeights = new int[len];
for (int i = 0; i < len; i++) {
intWeights[i] = randomData.nextInt(1, 5);
weights[i] = intWeights[i];
}
// Fill values array with random data from N(mu, sigma)
// and fill valuesList with values from values array with
// values[i] repeated weights[i] times, each i
List<Double> valuesList = new ArrayList<Double>();
for (int i = 0; i < len; i++) {
double value = randomData.nextGaussian(mu, sigma);
values[i] = value;
for (int j = 0; j < intWeights[i]; j++) {
valuesList.add(new Double(value));
}
}
// Dump valuesList into repeatedValues array
int sumWeights = valuesList.size();
double[] repeatedValues = new double[sumWeights];
for (int i = 0; i < sumWeights; i++) {
repeatedValues[i] = valuesList.get(i);
}
// Compare result of weighted statistic computation with direct computation
// on array of repeated values
double weightedResult = (Double) evaluateMethod.invoke(
statistic, values, weights, 0, values.length);
TestUtils.assertRelativelyEquals(
statistic.evaluate(repeatedValues), weightedResult, 10E-14);
}
} }

View File

@ -59,6 +59,11 @@ public class MeanTest extends StorelessUnivariateStatisticAbstractTest{
return this.mean; return this.mean;
} }
/**Expected value for the testArray defined in UnivariateStatisticAbstractTest */
public double expectedWeightedValue() {
return this.weightedMean;
}
public void testSmallSamples() { public void testSmallSamples() {
Mean mean = new Mean(); Mean mean = new Mean();
assertTrue(Double.isNaN(mean.getResult())); assertTrue(Double.isNaN(mean.getResult()));
@ -66,4 +71,10 @@ public class MeanTest extends StorelessUnivariateStatisticAbstractTest{
assertEquals(1d, mean.getResult(), 0); assertEquals(1d, mean.getResult(), 0);
} }
public void testWeightedMean() {
Mean mean = new Mean();
assertEquals(expectedWeightedValue(), mean.evaluate(testArray, testWeightsArray, 0, testArray.length), getTolerance());
assertEquals(expectedValue(), mean.evaluate(testArray, identicalWeightsArray, 0, testArray.length), getTolerance());
}
} }

View File

@ -21,6 +21,7 @@ import junit.framework.TestSuite;
import org.apache.commons.math.stat.descriptive.StorelessUnivariateStatisticAbstractTest; import org.apache.commons.math.stat.descriptive.StorelessUnivariateStatisticAbstractTest;
import org.apache.commons.math.stat.descriptive.UnivariateStatistic; import org.apache.commons.math.stat.descriptive.UnivariateStatistic;
import org.apache.commons.math.util.MathUtils;
/** /**
* Test cases for the {@link UnivariateStatistic} class. * Test cases for the {@link UnivariateStatistic} class.
@ -60,6 +61,11 @@ public class VarianceTest extends StorelessUnivariateStatisticAbstractTest{
return this.var; return this.var;
} }
/**Expected value for the testArray defined in UnivariateStatisticAbstractTest */
public double expectedWeightedValue() {
return this.weightedVar;
}
/** /**
* Make sure Double.NaN is returned iff n = 0 * Make sure Double.NaN is returned iff n = 0
* *
@ -103,4 +109,21 @@ public class VarianceTest extends StorelessUnivariateStatisticAbstractTest{
return sum / v.length; return sum / v.length;
} }
public void testWeightedVariance() {
Variance variance = new Variance();
assertEquals(expectedWeightedValue(),
variance.evaluate(testArray, testWeightsArray, 0, testArray.length), getTolerance());
// All weights = 1 -> weighted variance = unweighted variance
assertEquals(expectedValue(),
variance.evaluate(testArray, unitWeightsArray, 0, testArray.length), getTolerance());
// All weights the same -> when weights are normalized to sum to the length of the values array,
// weighted variance = unweighted value
assertEquals(expectedValue(),
variance.evaluate(testArray, MathUtils.normalizeArray(identicalWeightsArray, testArray.length),
0, testArray.length), getTolerance());
}
} }

View File

@ -67,6 +67,11 @@ public class ProductTest extends StorelessUnivariateStatisticAbstractTest{
return this.product; return this.product;
} }
/**Expected value for the testArray defined in UnivariateStatisticAbstractTest */
public double expectedWeightedValue() {
return this.weightedProduct;
}
public void testSpecialValues() { public void testSpecialValues() {
Product product = new Product(); Product product = new Product();
assertTrue(Double.isNaN(product.getResult())); assertTrue(Double.isNaN(product.getResult()));
@ -82,4 +87,10 @@ public class ProductTest extends StorelessUnivariateStatisticAbstractTest{
assertTrue(Double.isNaN(product.getResult())); assertTrue(Double.isNaN(product.getResult()));
} }
public void testWeightedProduct() {
Product product = new Product();
assertEquals(expectedWeightedValue(), product.evaluate(testArray, testWeightsArray, 0, testArray.length),getTolerance());
assertEquals(expectedValue(), product.evaluate(testArray, unitWeightsArray, 0, testArray.length), getTolerance());
}
} }

View File

@ -59,6 +59,11 @@ public class SumTest extends StorelessUnivariateStatisticAbstractTest{
return this.sum; return this.sum;
} }
/**Expected value for the testArray defined in UnivariateStatisticAbstractTest */
public double expectedWeightedValue() {
return this.weightedSum;
}
public void testSpecialValues() { public void testSpecialValues() {
Sum sum = new Sum(); Sum sum = new Sum();
assertTrue(Double.isNaN(sum.getResult())); assertTrue(Double.isNaN(sum.getResult()));
@ -72,4 +77,10 @@ public class SumTest extends StorelessUnivariateStatisticAbstractTest{
assertTrue(Double.isNaN(sum.getResult())); assertTrue(Double.isNaN(sum.getResult()));
} }
public void testWeightedSum() {
Sum sum = new Sum();
assertEquals(expectedWeightedValue(), sum.evaluate(testArray, testWeightsArray, 0, testArray.length), getTolerance());
assertEquals(expectedValue(), sum.evaluate(testArray, unitWeightsArray, 0, testArray.length), getTolerance());
}
} }