From 8ed6e84dc6b37360ef716422077d788b865eda48 Mon Sep 17 00:00:00 2001 From: Tim O'Brien Date: Wed, 21 May 2003 17:59:20 +0000 Subject: [PATCH] Added Product and Mean to Univariate and all implementations, this patch contains contributions from Mark Diggory. * This patch introduces Product and GeometricMean into the Univariate implementation. * Discarding the contribution of a discarded element in a rolling UnivariateImpl requires that the product be calculated explicitly each time a value is discarded. This is necessary because not all values will have non-zero values. * Errors in rolling logic for ListUimpl, and UnivariateImpl were corrected, and more test cases were added to the JUnit tests for the Univariate implementations. More rigorous test cases are needed for the entire suite of Univariate implementations git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/math/trunk@140851 13f79535-47bb-0310-9956-ffa450edef68 --- .../commons/math/AbstractStoreUnivariate.java | 26 +++++++ .../commons/math/ListUnivariateImpl.java | 11 +-- .../commons/math/StoreUnivariateImpl.java | 2 +- .../org/apache/commons/math/Univariate.java | 17 ++++- .../apache/commons/math/UnivariateImpl.java | 71 +++++++++++++++---- .../commons/math/ListUnivariateImplTest.java | 28 +++++++- .../commons/math/StoreUnivariateImplTest.java | 28 +++++++- .../commons/math/UnivariateImplTest.java | 27 ++++++- .../apache/commons/math/ValueServerTest.java | 6 +- 9 files changed, 189 insertions(+), 27 deletions(-) diff --git a/src/java/org/apache/commons/math/AbstractStoreUnivariate.java b/src/java/org/apache/commons/math/AbstractStoreUnivariate.java index 9d0bf57c1..4ec611df1 100644 --- a/src/java/org/apache/commons/math/AbstractStoreUnivariate.java +++ b/src/java/org/apache/commons/math/AbstractStoreUnivariate.java @@ -57,6 +57,7 @@ package org.apache.commons.math; * Provides univariate measures for an array of doubles. * * @author Tim O'Brien + * @author Mark Diggory */ public abstract class AbstractStoreUnivariate implements StoreUnivariate { @@ -158,6 +159,31 @@ public abstract class AbstractStoreUnivariate implements StoreUnivariate { return arithMean; } + /** + * Returns the geometric mean for this collection of values + * @see org.apache.commons.math.Univariate#getGeometricMean() + */ + public double getGeometricMean() { + double gMean = Math.pow(getProduct(),(1.0/getN())); + return gMean; + } + + /** + * Returns the product for this collection of values + * @see org.apache.commons.math.Univariate#getProduct() + */ + public double getProduct() { + double product = Double.NaN; + if( getN() > 0 ) { + product = 1.0; + for( int i = 0; i < getN(); i++) { + product *= getElement(i); + } + } + return product; + } + + /** * Returns the variance for this collection of values * @see org.apache.commons.math.Univariate#getVariance() diff --git a/src/java/org/apache/commons/math/ListUnivariateImpl.java b/src/java/org/apache/commons/math/ListUnivariateImpl.java index e7df32f1c..9d101c778 100644 --- a/src/java/org/apache/commons/math/ListUnivariateImpl.java +++ b/src/java/org/apache/commons/math/ListUnivariateImpl.java @@ -113,21 +113,24 @@ public class ListUnivariateImpl extends AbstractStoreUnivariate { double value = Double.NaN; if (windowSize != Univariate.INIFINTE_WINDOW && windowSize < list.size()) { - Number n = (Number) list.get(((list.size() - 1) - windowSize) + - index) ; + + int calcIndex = (list.size() - windowSize) + index; + + Number n = (Number) list.get(calcIndex); value = n.doubleValue(); } else { Number n = (Number) list.get(index); value = n.doubleValue(); } + System.out.println( "Returning value: " + value ); return value; } /* (non-Javadoc) * @see org.apache.commons.math.Univariate#getN() */ - public double getN() { - double N = 0.0; + public int getN() { + int N = 0; if (windowSize != Univariate.INIFINTE_WINDOW) { if (list.size() > windowSize) { diff --git a/src/java/org/apache/commons/math/StoreUnivariateImpl.java b/src/java/org/apache/commons/math/StoreUnivariateImpl.java index d79008405..21a60e74b 100644 --- a/src/java/org/apache/commons/math/StoreUnivariateImpl.java +++ b/src/java/org/apache/commons/math/StoreUnivariateImpl.java @@ -91,7 +91,7 @@ public class StoreUnivariateImpl extends AbstractStoreUnivariate { /* (non-Javadoc) * @see org.apache.commons.math.Univariate#getN() */ - public double getN() { + public int getN() { return eDA.getNumElements(); } diff --git a/src/java/org/apache/commons/math/Univariate.java b/src/java/org/apache/commons/math/Univariate.java index 734d93f22..53cec365a 100644 --- a/src/java/org/apache/commons/math/Univariate.java +++ b/src/java/org/apache/commons/math/Univariate.java @@ -61,7 +61,8 @@ * * @author Phil Steitz * @author Tim O'Brien - * @version $Revision: 1.4 $ $Date: 2003/05/20 18:15:29 $ + * @author Mark Diggory + * @version $Revision: 1.5 $ $Date: 2003/05/21 17:59:19 $ * */ public interface Univariate { @@ -78,6 +79,18 @@ public interface Univariate { */ public abstract double getMean(); + /** + * Returns the geometric mean of the values that have been added + * @return mean value + */ + public abstract double getGeometricMean(); + + /** + * Returns the product of all values that have been added + * @return product of all values + */ + public abstract double getProduct(); + /** * Returns the variance of the values that have been added * @return variance value @@ -103,7 +116,7 @@ public interface Univariate { /** Getter for property n. * @return Value of property n. */ - public abstract double getN(); + public abstract int getN(); /** Getter for property sum. * @return Value of property sum. diff --git a/src/java/org/apache/commons/math/UnivariateImpl.java b/src/java/org/apache/commons/math/UnivariateImpl.java index 3f9d77c73..b624df31c 100644 --- a/src/java/org/apache/commons/math/UnivariateImpl.java +++ b/src/java/org/apache/commons/math/UnivariateImpl.java @@ -64,7 +64,9 @@ import java.io.Serializable; * to doubles by addValue(). * * @author Phil Steitz - * @version $Revision: 1.5 $ $Date: 2003/05/20 18:15:29 $ + * @author Mark Diggory + * @author Tim O'Brien + * @version $Revision: 1.6 $ $Date: 2003/05/21 17:59:19 $ * */ public class UnivariateImpl implements Univariate, Serializable { @@ -84,7 +86,7 @@ public class UnivariateImpl implements Univariate, Serializable { private double sumsq = 0.0; /** count of values that have been added */ - private double n = 0.0; + private int n = 0; /** min of values that have been added */ private double min = Double.MAX_VALUE; @@ -92,6 +94,9 @@ public class UnivariateImpl implements Univariate, Serializable { /** max of values that have been added */ private double max = Double.MIN_VALUE; + /** produce of values that have been added */ + private double product = Double.NaN; + /** Creates new univariate */ public UnivariateImpl() { clear(); @@ -118,7 +123,23 @@ public class UnivariateImpl implements Univariate, Serializable { */ public double getMean() { // FIXME: throw something meaningful if n = 0 - return sum/n; + return (sum / (double) n ); + } + + /** + * Returns the geometric mean of the values that have been added + * @return geometric mean value + */ + public double getGeometricMean() { + return Math.pow(product,( 1.0/n ) ); + } + + /** + * Returns the product of all values add to this Univariate + * @return product value + */ + public double getProduct() { + return product; } /** @@ -133,7 +154,7 @@ public class UnivariateImpl implements Univariate, Serializable { variance = 0.0; } else if( n > 1 ) { double xbar = getMean(); - variance = (sumsq - xbar*xbar*n)/(n-1); + variance = (sumsq - xbar*xbar*((double) n))/(((double) n)-1); } return variance; @@ -156,18 +177,25 @@ public class UnivariateImpl implements Univariate, Serializable { */ private void insertValue(double v) { + // The default value of product is NaN, if you + // try to retrieve the product for a univariate with + // no values, we return NaN. + // + // If this is the first call to insertValue, we want + // to set product to 1.0, so that our first element + // is not "cancelled" out by the NaN. + if( n == 0 ) { + product = 1.0; + } + if( windowSize != Univariate.INIFINTE_WINDOW ) { + if( windowSize == n ) { double discarded = doubleArray.addElementRolling( v ); - // Remove the influence of discarded value ONLY - // if the discard value has any meaning. In other words - // don't discount until we "roll". - if( windowSize > doubleArray.getNumElements() ) { - // Remove the influence of the discarded - sum -= discarded; - sumsq -= discarded * discarded; - } + // Remove the influence of the discarded + sum -= discarded; + sumsq -= discarded * discarded; // Include the influence of the new // TODO: The next two lines seems rather expensive, but @@ -176,6 +204,19 @@ public class UnivariateImpl implements Univariate, Serializable { max = doubleArray.getMax(); sum += v; sumsq += v*v; + + // Note that the product CANNOT be discarded + // properly because one cannot discount the effect + // of a zero value. For this reason, the product + // of the altered array must be calculated from the + // current array elements. Product must be recalculated + // everytime the array is "rolled" + product = 1.0; + double[] elements = doubleArray.getElements(); + for( int i = 0; i < elements.length; i++ ) { + product *= elements[i]; + } + } else { doubleArray.addElement( v ); n += 1.0; @@ -183,6 +224,7 @@ public class UnivariateImpl implements Univariate, Serializable { if (v > max) max = v; sum += v; sumsq += v*v; + product *= v; } } else { // If the windowSize is inifinite please don't take the time to @@ -193,6 +235,7 @@ public class UnivariateImpl implements Univariate, Serializable { if (v > max) max = v; sum += v; sumsq += v*v; + product *= v; } } @@ -220,7 +263,7 @@ public class UnivariateImpl implements Univariate, Serializable { /** Getter for property n. * @return Value of property n. */ - public double getN() { + public int getN() { return n; } @@ -259,7 +302,7 @@ public class UnivariateImpl implements Univariate, Serializable { public void clear() { this.sum = 0.0; this.sumsq = 0.0; - this.n = 0.0; + this.n = 0; this.min = Double.MAX_VALUE; this.max = Double.MIN_VALUE; } diff --git a/src/test/org/apache/commons/math/ListUnivariateImplTest.java b/src/test/org/apache/commons/math/ListUnivariateImplTest.java index f8ef8b4e6..37e293709 100644 --- a/src/test/org/apache/commons/math/ListUnivariateImplTest.java +++ b/src/test/org/apache/commons/math/ListUnivariateImplTest.java @@ -64,7 +64,7 @@ import junit.framework.TestSuite; * Test cases for the {@link Univariate} class. * * @author Phil Steitz - * @version $Revision: 1.2 $ $Date: 2003/05/15 15:38:48 $ + * @version $Revision: 1.3 $ $Date: 2003/05/21 17:59:20 $ */ public final class ListUnivariateImplTest extends TestCase { @@ -151,5 +151,31 @@ public final class ListUnivariateImplTest extends TestCase { assertEquals("skewness", 1.437424, u.getSkewness(), 0.0001); assertEquals("kurtosis", 2.37719, u.getKurtosis(), 0.0001); } + + public void testProductAndGeometricMean() throws Exception { + ListUnivariateImpl u = new ListUnivariateImpl(new ArrayList()); + u.setWindowSize(10); + + u.addValue( 1.0 ); + u.addValue( 2.0 ); + u.addValue( 3.0 ); + u.addValue( 4.0 ); + + assertEquals( "Product not expected", 24.0, u.getProduct(), Double.MIN_VALUE ); + assertEquals( "Geometric mean not expected", 2.213364, u.getGeometricMean(), 0.00001 ); + + // Now test rolling - UnivariateImpl should discount the contribution + // of a discarded element + for( int i = 0; i < 10; i++ ) { + u.addValue( i + 2 ); + } + // Values should be (2,3,4,5,6,7,8,9,10,11) + + assertEquals( "Product not expected", 39916800.0, u.getProduct(), 0.00001 ); + assertEquals( "Geometric mean not expected", 5.755931, u.getGeometricMean(), 0.00001 ); + + + } + } diff --git a/src/test/org/apache/commons/math/StoreUnivariateImplTest.java b/src/test/org/apache/commons/math/StoreUnivariateImplTest.java index a74ca79b6..394f8bad2 100644 --- a/src/test/org/apache/commons/math/StoreUnivariateImplTest.java +++ b/src/test/org/apache/commons/math/StoreUnivariateImplTest.java @@ -61,7 +61,7 @@ import junit.framework.TestSuite; * Test cases for the {@link Univariate} class. * * @author Phil Steitz - * @version $Revision: 1.1 $ $Date: 2003/05/15 05:39:01 $ + * @version $Revision: 1.2 $ $Date: 2003/05/21 17:59:20 $ */ public final class StoreUnivariateImplTest extends TestCase { @@ -142,5 +142,31 @@ public final class StoreUnivariateImplTest extends TestCase { assertEquals("skewness", 1.437424, u.getSkewness(), 0.0001); assertEquals("kurtosis", 2.37719, u.getKurtosis(), 0.0001); } + + public void testProductAndGeometricMean() throws Exception { + StoreUnivariateImpl u = new StoreUnivariateImpl(); + u.setWindowSize(10); + + u.addValue( 1.0 ); + u.addValue( 2.0 ); + u.addValue( 3.0 ); + u.addValue( 4.0 ); + + assertEquals( "Product not expected", 24.0, u.getProduct(), Double.MIN_VALUE ); + assertEquals( "Geometric mean not expected", 2.213364, u.getGeometricMean(), 0.00001 ); + + // Now test rolling - UnivariateImpl should discount the contribution + // of a discarded element + for( int i = 0; i < 10; i++ ) { + u.addValue( i + 2 ); + } + // Values should be (2,3,4,5,6,7,8,9,10,11) + + assertEquals( "Product not expected", 39916800.0, u.getProduct(), 0.00001 ); + assertEquals( "Geometric mean not expected", 5.755931, u.getGeometricMean(), 0.00001 ); + + + } + } diff --git a/src/test/org/apache/commons/math/UnivariateImplTest.java b/src/test/org/apache/commons/math/UnivariateImplTest.java index 4cae7f739..ef589ad0f 100644 --- a/src/test/org/apache/commons/math/UnivariateImplTest.java +++ b/src/test/org/apache/commons/math/UnivariateImplTest.java @@ -61,7 +61,7 @@ import junit.framework.TestSuite; * Test cases for the {@link Univariate} class. * * @author Phil Steitz - * @version $Revision: 1.1 $ $Date: 2003/05/15 05:39:01 $ + * @version $Revision: 1.2 $ $Date: 2003/05/21 17:59:20 $ */ public final class UnivariateImplTest extends TestCase { @@ -125,5 +125,30 @@ public final class UnivariateImplTest extends TestCase { assertTrue( "Mean of n = 1 set should be zero", u.getStandardDeviation() == 0); assertTrue( "Variance of n = 1 set should be zero", u.getVariance() == 0); } + + public void testProductAndGeometricMean() throws Exception { + UnivariateImpl u = new UnivariateImpl(10); + + u.addValue( 1.0 ); + u.addValue( 2.0 ); + u.addValue( 3.0 ); + u.addValue( 4.0 ); + + assertEquals( "Product not expected", 24.0, u.getProduct(), Double.MIN_VALUE ); + assertEquals( "Geometric mean not expected", 2.213364, u.getGeometricMean(), 0.00001 ); + + // Now test rolling - UnivariateImpl should discount the contribution + // of a discarded element + for( int i = 0; i < 10; i++ ) { + u.addValue( i + 2 ); + } + // Values should be (2,3,4,5,6,7,8,9,10,11) + + assertEquals( "Product not expected", 39916800.0, u.getProduct(), 0.00001 ); + assertEquals( "Geometric mean not expected", 5.755931, u.getGeometricMean(), 0.00001 ); + + + } + } diff --git a/src/test/org/apache/commons/math/ValueServerTest.java b/src/test/org/apache/commons/math/ValueServerTest.java index 046dfe3d7..c1d9d4a30 100644 --- a/src/test/org/apache/commons/math/ValueServerTest.java +++ b/src/test/org/apache/commons/math/ValueServerTest.java @@ -63,7 +63,7 @@ import java.net.URL; * Test cases for the ValueServer class. * * @author Phil Steitz - * @version $Revision: 1.1 $ + * @version $Revision: 1.2 $ */ public final class ValueServerTest extends TestCase { @@ -105,9 +105,9 @@ public final class ValueServerTest extends TestCase { next = vs.getNext(); stats.addValue(next); } - assertEquals("mean", stats.getMean(),5.069831575018909,tolerance); + assertEquals("mean", 5.069831575018909, stats.getMean(), tolerance); assertEquals - ("std dev", stats.getStandardDeviation(),1.0173699343977738,tolerance); + ("std dev", 1.0173699343977738, stats.getStandardDeviation(), tolerance); } /**