added multivariate summary statistics

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@619934 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Luc Maisonobe 2008-02-08 16:42:34 +00:00
parent ac6a598144
commit 1cabcd496c
6 changed files with 1454 additions and 0 deletions

View File

@ -0,0 +1,592 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.commons.math.stat.descriptive;
import java.io.Serializable;
import java.util.Arrays;
import org.apache.commons.math.DimensionMismatchException;
import org.apache.commons.math.linear.RealMatrix;
import org.apache.commons.math.stat.descriptive.moment.GeometricMean;
import org.apache.commons.math.stat.descriptive.moment.Mean;
import org.apache.commons.math.stat.descriptive.moment.VectorialCovariance;
import org.apache.commons.math.stat.descriptive.rank.Max;
import org.apache.commons.math.stat.descriptive.rank.Min;
import org.apache.commons.math.stat.descriptive.summary.Sum;
import org.apache.commons.math.stat.descriptive.summary.SumOfLogs;
import org.apache.commons.math.stat.descriptive.summary.SumOfSquares;
import org.apache.commons.math.util.MathUtils;
/**
* <p>Computes summary statistics for a stream of data values added using the
* {@link #addValue(double[]) addValue} method. The data values are not stored in
* memory, so this class can be used to compute statistics for very large
* data streams.</p>
*
* <p>The {@link StorelessUnivariateStatistic} array instances used to maintain
* summary state and compute statistics are configurable via setters.
* For example, the default implementation for the mean can be overridden by
* calling {@link #setMeanImpl(StorelessUnivariateStatistic[])}. Actual
* parameters to these methods must implement the
* {@link StorelessUnivariateStatistic} interface and configuration must be
* completed before <code>addValue</code> is called. No configuration is
* necessary to use the default, commons-math provided implementations.</p>
*
* <p>Note: This class is not thread-safe. Use
* {@link SynchronizedMultivariateSummaryStatistics} if concurrent access from multiple
* threads is required.</p>
*
* @since 1.2
* @version $Revision: 618097 $ $Date: 2008-02-03 22:39:08 +0100 (dim., 03 févr. 2008) $
*/
public class MultivariateSummaryStatistics
implements StatisticalMultivariateSummary, Serializable {
/** Serialization UID */
private static final long serialVersionUID = 2271900808994826718L;
/**
* Construct a MultivariateSummaryStatistics instance
* @param k dimension of the data
* @param isCovarianceBiasCorrected if true, the unbiased sample
* covariance is computed, otherwise the biased population covariance
* is computed
*/
public MultivariateSummaryStatistics(int k, boolean isCovarianceBiasCorrected) {
this.k = k;
sumImpl = new StorelessUnivariateStatistic[k];
sumSqImpl = new StorelessUnivariateStatistic[k];
minImpl = new StorelessUnivariateStatistic[k];
maxImpl = new StorelessUnivariateStatistic[k];
sumLogImpl = new StorelessUnivariateStatistic[k];
geoMeanImpl = new StorelessUnivariateStatistic[k];
meanImpl = new StorelessUnivariateStatistic[k];
for (int i = 0; i < k; ++i) {
sumImpl[i] = new Sum();
sumSqImpl[i] = new SumOfSquares();
minImpl[i] = new Min();
maxImpl[i] = new Max();
sumLogImpl[i] = new SumOfLogs();
geoMeanImpl[i] = new GeometricMean();
meanImpl[i] = new Mean();
}
covarianceImpl =
new VectorialCovariance(k, isCovarianceBiasCorrected);
}
/** Dimension of the data. */
private int k;
/** Count of values that have been added */
private long n = 0;
/** Sum statistic implementation - can be reset by setter. */
private StorelessUnivariateStatistic[] sumImpl;
/** Sum of squares statistic implementation - can be reset by setter. */
private StorelessUnivariateStatistic[] sumSqImpl;
/** Minimum statistic implementation - can be reset by setter. */
private StorelessUnivariateStatistic[] minImpl;
/** Maximum statistic implementation - can be reset by setter. */
private StorelessUnivariateStatistic[] maxImpl;
/** Sum of log statistic implementation - can be reset by setter. */
private StorelessUnivariateStatistic[] sumLogImpl;
/** Geometric mean statistic implementation - can be reset by setter. */
private StorelessUnivariateStatistic[] geoMeanImpl;
/** Mean statistic implementation - can be reset by setter. */
private StorelessUnivariateStatistic[] meanImpl;
/** Covariance statistic implementation - cannot be reset. */
private VectorialCovariance covarianceImpl;
/**
* Return a {@link StatisticalMultivariateSummary} instance reporting current
* statistics.
*
* @return Current values of statistics
*/
public StatisticalMultivariateSummary getSummary() {
return new StatisticalMultivariateSummaryValues(getDimension(), getMean(),
getCovariance(), getStandardDeviation(),
getN(), getMax(), getMin(),
getSum(), getSumSq(), getSumLog());
}
/**
* Add a value to the data
*
* @param value the value to add
* @throws DimensionMismatchException if the value dimension
* does not match the one used at construction
*/
public void addValue(double[] value)
throws DimensionMismatchException {
if (value.length != k) {
throw new DimensionMismatchException(value.length, k);
}
for (int i = 0; i < k; ++i) {
double v = value[i];
sumImpl[i].increment(v);
sumSqImpl[i].increment(v);
minImpl[i].increment(v);
maxImpl[i].increment(v);
sumLogImpl[i].increment(v);
geoMeanImpl[i].increment(v);
meanImpl[i].increment(v);
}
covarianceImpl.increment(value);
n++;
}
/**
* Returns the dimension of the data
* @return The dimension of the data
*/
public int getDimension() {
return k;
}
/**
* Returns the number of available values
* @return The number of available values
*/
public long getN() {
return n;
}
/**
* Returns an array of the results of a statistic.
* @param stats univariate statistic array
* @return results array
*/
private double[] getResults(StorelessUnivariateStatistic[] stats) {
double[] results = new double[stats.length];
for (int i = 0; i < results.length; ++i) {
results[i] = stats[i].getResult();
}
return results;
}
/**
* Returns the sum of the values that have been added
* @return The sum or <code>Double.NaN</code> if no values have been added
*/
public double[] getSum() {
return getResults(sumImpl);
}
/**
* Returns the sum of the squares of the values that have been added.
* <p>
* Double.NaN is returned if no values have been added.</p>
*
* @return The sum of squares
*/
public double[] getSumSq() {
return getResults(sumSqImpl);
}
/**
* Returns the sum of the logarithms of the values that have been added.
* <p>
* Double.NaN is returned if no values have been added.</p>
*
* @return The sum of logarithms
*/
public double[] getSumLog() {
return getResults(sumLogImpl);
}
/**
* Returns the mean of the values that have been added.
* <p>
* Double.NaN is returned if no values have been added.</p>
*
* @return the mean
*/
public double[] getMean() {
return getResults(meanImpl);
}
/**
* Returns the standard deviation of the values that have been added.
* <p>
* Double.NaN is returned if no values have been added.</p>
*
* @return the standard deviation
*/
public double[] getStandardDeviation() {
double[] stdDev = new double[k];
if (getN() < 1) {
Arrays.fill(stdDev, Double.NaN);
} else if (getN() < 2) {
Arrays.fill(stdDev, 0.0);
} else {
RealMatrix matrix = covarianceImpl.getResult();
for (int i = 0; i < k; ++i) {
stdDev[i] = Math.sqrt(matrix.getEntry(i, i));
}
}
return stdDev;
}
/**
* Returns the covariance of the values that have been added.
* <p>
* Double.NaN is returned if no values have been added.</p>
*
* @return the variance
*/
public RealMatrix getCovariance() {
return covarianceImpl.getResult();
}
/**
* Returns the maximum of the values that have been added.
* <p>
* Double.NaN is returned if no values have been added.</p>
*
* @return the maximum
*/
public double[] getMax() {
return getResults(maxImpl);
}
/**
* Returns the minimum of the values that have been added.
* <p>
* Double.NaN is returned if no values have been added.</p>
*
* @return the minimum
*/
public double[] getMin() {
return getResults(minImpl);
}
/**
* Returns the geometric mean of the values that have been added.
* <p>
* Double.NaN is returned if no values have been added.</p>
*
* @return the geometric mean
*/
public double[] getGeometricMean() {
return getResults(geoMeanImpl);
}
/**
* Generates a text report displaying
* summary statistics from values that
* have been added.
* @return String with line feeds displaying statistics
*/
public String toString() {
StringBuffer outBuffer = new StringBuffer();
outBuffer.append("MultivariateSummaryStatistics:\n");
outBuffer.append("n: " + getN() + "\n");
append(outBuffer, getMin(), "min: ", ", ", "\n");
append(outBuffer, getMax(), "max: ", ", ", "\n");
append(outBuffer, getMean(), "mean: ", ", ", "\n");
append(outBuffer, getGeometricMean(), "geometric mean: ", ", ", "\n");
append(outBuffer, getSumSq(), "sum of squares: ", ", ", "\n");
append(outBuffer, getSumLog(), "sum of logarithms: ", ", ", "\n");
append(outBuffer, getStandardDeviation(), "standard deviation: ", ", ", "\n");
outBuffer.append("covariance: " + getCovariance().toString() + "\n");
return outBuffer.toString();
}
/**
* Append a text representation of an array to a buffer.
* @param buffer buffer to fill
* @param data data array
* @param prefix text prefix
* @param separator elements separator
* @param suffix text suffix
*/
private void append(StringBuffer buffer, double[] data,
String prefix, String separator, String suffix) {
buffer.append(prefix);
for (int i = 0; i < data.length; ++i) {
if (i > 0) {
buffer.append(separator);
}
buffer.append(data[i]);
}
buffer.append(suffix);
}
/**
* Resets all statistics and storage
*/
public void clear() {
this.n = 0;
for (int i = 0; i < k; ++i) {
minImpl[i].clear();
maxImpl[i].clear();
sumImpl[i].clear();
sumLogImpl[i].clear();
sumSqImpl[i].clear();
geoMeanImpl[i].clear();
meanImpl[i].clear();
}
covarianceImpl.clear();
}
/**
* Returns true iff <code>object</code> is a <code>SummaryStatistics</code>
* instance and all statistics have the same values as this.
* @param object the object to test equality against.
* @return true if object equals this
*/
public boolean equals(Object object) {
if (object == this ) {
return true;
}
if (object instanceof MultivariateSummaryStatistics == false) {
return false;
}
MultivariateSummaryStatistics stat = (MultivariateSummaryStatistics) object;
return (MathUtils.equals(stat.getGeometricMean(),
this.getGeometricMean()) &&
MathUtils.equals(stat.getMax(), this.getMax()) &&
MathUtils.equals(stat.getMean(),this.getMean()) &&
MathUtils.equals(stat.getMin(),this.getMin()) &&
MathUtils.equals(stat.getN(), this.getN()) &&
MathUtils.equals(stat.getSum(), this.getSum()) &&
MathUtils.equals(stat.getSumSq(),this.getSumSq()) &&
MathUtils.equals(stat.getSumLog(),this.getSumLog()) &&
stat.getCovariance().equals(this.getCovariance()));
}
/**
* Returns hash code based on values of statistics
*
* @return hash code
*/
public int hashCode() {
int result = 31 + MathUtils.hash(getGeometricMean());
result = result * 31 + MathUtils.hash(getGeometricMean());
result = result * 31 + MathUtils.hash(getMax());
result = result * 31 + MathUtils.hash(getMean());
result = result * 31 + MathUtils.hash(getMin());
result = result * 31 + MathUtils.hash(getN());
result = result * 31 + MathUtils.hash(getSum());
result = result * 31 + MathUtils.hash(getSumSq());
result = result * 31 + MathUtils.hash(getSumLog());
result = result * 31 + getCovariance().hashCode();
return result;
}
// Getters and setters for statistics implementations
/**
* Returns the currently configured Sum implementation
*
* @return the StorelessUnivariateStatistic implementing the sum
*/
public StorelessUnivariateStatistic[] getSumImpl() {
return sumImpl;
}
/**
* <p>Sets the implementation for the Sum.</p>
* <p>This method must be activated before any data has been added - i.e.,
* before {@link #addValue(double[]) addValue} has been used to add data;
* otherwise an IllegalStateException will be thrown.</p>
*
* @param sumImpl the StorelessUnivariateStatistic instance to use
* for computing the Sum
* @throws IllegalArgumentException if the array dimension
* does not match the one used at construction
* @throws IllegalStateException if data has already been added
* (i.e if n > 0)
*/
public void setSumImpl(StorelessUnivariateStatistic[] sumImpl) {
checkEmpty();
this.sumImpl = sumImpl;
}
/**
* Returns the currently configured sum of squares implementation
*
* @return the StorelessUnivariateStatistic implementing the sum of squares
*/
public StorelessUnivariateStatistic[] getSumsqImpl() {
return sumSqImpl;
}
/**
* <p>Sets the implementation for the sum of squares.</p>
* <p>This method must be activated before any data has been added - i.e.,
* before {@link #addValue(double[]) addValue} has been used to add data;
* otherwise an IllegalStateException will be thrown.</p>
*
* @param sumsqImpl the StorelessUnivariateStatistic instance to use
* for computing the sum of squares
* @throws IllegalStateException if data has already been added
* (i.e if n > 0)
*/
public void setSumsqImpl(StorelessUnivariateStatistic[] sumsqImpl) {
checkEmpty();
this.sumSqImpl = sumsqImpl;
}
/**
* Returns the currently configured minimum implementation
*
* @return the StorelessUnivariateStatistic implementing the minimum
*/
public StorelessUnivariateStatistic[] getMinImpl() {
return minImpl;
}
/**
* <p>Sets the implementation for the minimum.</p>
* <p>This method must be activated before any data has been added - i.e.,
* before {@link #addValue(double[]) addValue} has been used to add data;
* otherwise an IllegalStateException will be thrown.</p>
*
* @param minImpl the StorelessUnivariateStatistic instance to use
* for computing the minimum
* @throws IllegalStateException if data has already been added
* (i.e if n > 0)
*/
public void setMinImpl(StorelessUnivariateStatistic[] minImpl) {
checkEmpty();
this.minImpl = minImpl;
}
/**
* Returns the currently configured maximum implementation
*
* @return the StorelessUnivariateStatistic implementing the maximum
*/
public StorelessUnivariateStatistic[] getMaxImpl() {
return maxImpl;
}
/**
* <p>Sets the implementation for the maximum.</p>
* <p>This method must be activated before any data has been added - i.e.,
* before {@link #addValue(double[]) addValue} has been used to add data;
* otherwise an IllegalStateException will be thrown.</p>
*
* @param maxImpl the StorelessUnivariateStatistic instance to use
* for computing the maximum
* @throws IllegalStateException if data has already been added
* (i.e if n > 0)
*/
public void setMaxImpl(StorelessUnivariateStatistic[] maxImpl) {
checkEmpty();
this.maxImpl = maxImpl;
}
/**
* Returns the currently configured sum of logs implementation
*
* @return the StorelessUnivariateStatistic implementing the log sum
*/
public StorelessUnivariateStatistic[] getSumLogImpl() {
return sumLogImpl;
}
/**
* <p>Sets the implementation for the sum of logs.</p>
* <p>This method must be activated before any data has been added - i.e.,
* before {@link #addValue(double[]) addValue} has been used to add data;
* otherwise an IllegalStateException will be thrown.</p>
*
* @param sumLogImpl the StorelessUnivariateStatistic instance to use
* for computing the log sum
* @throws IllegalStateException if data has already been added
* (i.e if n > 0)
*/
public void setSumLogImpl(StorelessUnivariateStatistic[] sumLogImpl) {
checkEmpty();
this.sumLogImpl = sumLogImpl;
}
/**
* Returns the currently configured geometric mean implementation
*
* @return the StorelessUnivariateStatistic implementing the geometric mean
*/
public StorelessUnivariateStatistic[] getGeoMeanImpl() {
return geoMeanImpl;
}
/**
* <p>Sets the implementation for the geometric mean.</p>
* <p>This method must be activated before any data has been added - i.e.,
* before {@link #addValue(double[]) addValue} has been used to add data;
* otherwise an IllegalStateException will be thrown.</p>
*
* @param geoMeanImpl the StorelessUnivariateStatistic instance to use
* for computing the geometric mean
* @throws IllegalStateException if data has already been added
* (i.e if n > 0)
*/
public void setGeoMeanImpl(StorelessUnivariateStatistic[] geoMeanImpl) {
checkEmpty();
this.geoMeanImpl = geoMeanImpl;
}
/**
* Returns the currently configured mean implementation
*
* @return the StorelessUnivariateStatistic implementing the mean
*/
public StorelessUnivariateStatistic[] getMeanImpl() {
return meanImpl;
}
/**
* <p>Sets the implementation for the mean.</p>
* <p>This method must be activated before any data has been added - i.e.,
* before {@link #addValue(double[]) addValue} has been used to add data;
* otherwise an IllegalStateException will be thrown.</p>
*
* @param meanImpl the StorelessUnivariateStatistic instance to use
* for computing the mean
* @throws IllegalStateException if data has already been added
* (i.e if n > 0)
*/
public void setMeanImpl(StorelessUnivariateStatistic[] meanImpl) {
checkEmpty();
this.meanImpl = meanImpl;
}
/**
* Throws IllegalStateException if n > 0.
*/
private void checkEmpty() {
if (n > 0) {
throw new IllegalStateException(
"Implementations must be configured before values are added.");
}
}
}

View File

@ -0,0 +1,81 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.commons.math.stat.descriptive;
import org.apache.commons.math.linear.RealMatrix;
/**
* Reporting interface for basic multivariate statistics.
*
* @since 1.2
* @version $Revision: 480440 $ $Date: 2006-11-29 08:14:12 +0100 (mer., 29 nov. 2006) $
*/
public interface StatisticalMultivariateSummary {
/**
* Returns the dimension of the data
* @return The dimension of the data
*/
public int getDimension();
/**
* Returns the <a href="http://www.xycoon.com/arithmetic_mean.htm">
* arithmetic mean </a> of the available values
* @return The mean or null if no values have been added.
*/
public abstract double[] getMean();
/**
* Returns the covariance of the available values.
* @return The covariance, null if no values have been added
* or a zeroed matrix for a single value set.
*/
public abstract RealMatrix getCovariance();
/**
* Returns the standard deviation of the available values.
* @return The standard deviation, null if no values have been added
* or a zeroed array for a single value set.
*/
public abstract double[] getStandardDeviation();
/**
* Returns the maximum of the available values
* @return The max or null if no values have been added.
*/
public abstract double[] getMax();
/**
* Returns the minimum of the available values
* @return The min or null if no values have been added.
*/
public abstract double[] getMin();
/**
* Returns the number of available values
* @return The number of available values
*/
public abstract long getN();
/**
* Returns the sum of the values that have been added.
* @return The sum or null if no values have been added
*/
public abstract double[] getSum();
/**
* Returns the sum of the squares of the values that have been added.
* @return The sum or null if no values have been added
*/
public abstract double[] getSumSq();
/**
* Returns the sum of the logarithms of the values that have been added.
* @return The sum or null if no values have been added
*/
public abstract double[] getSumLog();
}

View File

@ -0,0 +1,215 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.commons.math.stat.descriptive;
import java.io.Serializable;
import org.apache.commons.math.linear.RealMatrix;
import org.apache.commons.math.util.MathUtils;
/**
* Value object representing the results of a statistical multivariate summary.
*
* @since 1.2
* @version $Revision: 480440 $ $Date: 2006-11-29 08:14:12 +0100 (mer., 29 nov. 2006) $
*/
public class StatisticalMultivariateSummaryValues
implements Serializable, StatisticalMultivariateSummary {
/** Serialization id */
private static final long serialVersionUID = 8152538650791979064L;
/** Dimension of the data. */
private final int k;
/** The sample mean */
private final double[] mean;
/** The sample covariance */
private final RealMatrix covariance;
/** The sample standard deviation. */
private double[] stdev;
/** The number of observations in the sample */
private final long n;
/** The maximum value */
private final double[] max;
/** The minimum value */
private final double[] min;
/** The sum of the sample values */
private final double[] sum;
/** The sum of the squares of the sample values */
private final double[] sumSq;
/** The sum of the logarithms of the sample values */
private final double[] sumLog;
/**
* Constructor
*
* @param mean the sample mean
* @param covariance the sample covariance
* @param stdev the sample standard deviation
* @param k dimension of the data
* @param n the number of observations in the sample
* @param max the maximum value
* @param min the minimum value
* @param sum the sum of the values
* @param sumSq the sum of the squares of the values
* @param sumLog the sum of the logarithms of the values
*/
public StatisticalMultivariateSummaryValues(int k, double[] mean,
RealMatrix covariance, double[] stdev,
long n, double[] max, double[] min,
double[] sum, double[] sumSq, double[] sumLog) {
super();
this.k = k;
this.mean = mean;
this.covariance = covariance;
this.stdev = stdev;
this.n = n;
this.max = max;
this.min = min;
this.sum = sum;
this.sumSq = sumSq;
this.sumLog = sumLog;
}
/**
* Returns the dimension of the data
* @return The dimension of the data
*/
public int getDimension() {
return k;
}
/**
* @return Returns the max.
*/
public double[] getMax() {
return max;
}
/**
* @return Returns the mean.
*/
public double[] getMean() {
return mean;
}
/**
* @return Returns the min.
*/
public double[] getMin() {
return min;
}
/**
* @return Returns the number of values.
*/
public long getN() {
return n;
}
/**
* @return Returns the sum.
*/
public double[] getSum() {
return sum;
}
/**
* @return Returns the sum of the squares.
*/
public double[] getSumSq() {
return sumSq;
}
/**
* @return Returns the sum of the logarithms.
*/
public double[] getSumLog() {
return sumLog;
}
/**
* @return Returns the standard deviation (roots of the diagonal elements)
*/
public double[] getStandardDeviation() {
return stdev;
}
/**
* @return Returns the covariance.
*/
public RealMatrix getCovariance() {
return covariance;
}
/**
* Returns true iff <code>object</code> is a
* <code>StatisticalSummaryValues</code> instance and all statistics have
* the same values as this.
*
* @param object the object to test equality against.
* @return true if object equals this
*/
public boolean equals(Object object) {
if (object == this ) {
return true;
}
if (object instanceof StatisticalMultivariateSummaryValues == false) {
return false;
}
StatisticalMultivariateSummaryValues stat = (StatisticalMultivariateSummaryValues) object;
return ((stat.getDimension() == this.getDimension()) &&
MathUtils.equals(stat.getMax(), this.getMax()) &&
MathUtils.equals(stat.getMean(),this.getMean()) &&
MathUtils.equals(stat.getMin(),this.getMin()) &&
MathUtils.equals(stat.getN(), this.getN()) &&
MathUtils.equals(stat.getSum(), this.getSum()) &&
MathUtils.equals(stat.getSumSq(), this.getSumSq()) &&
MathUtils.equals(stat.getSumLog(), this.getSumLog()) &&
MathUtils.equals(stat.getStandardDeviation(), this.getStandardDeviation()) &&
stat.getCovariance().equals(this.getCovariance()));
}
/**
* Returns hash code based on values of statistics
*
* @return hash code
*/
public int hashCode() {
int result = getDimension();
result = result * 31 + MathUtils.hash(getMax());
result = result * 31 + MathUtils.hash(getMean());
result = result * 31 + MathUtils.hash(getMin());
result = result * 31 + MathUtils.hash(getN());
result = result * 31 + MathUtils.hash(getSum());
result = result * 31 + MathUtils.hash(getSumSq());
result = result * 31 + MathUtils.hash(getSumLog());
result = result * 31 + getCovariance().hashCode();
result = result * 31 + MathUtils.hash(getStandardDeviation());
return result;
}
}

View File

@ -0,0 +1,269 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.commons.math.stat.descriptive;
import org.apache.commons.math.DimensionMismatchException;
import org.apache.commons.math.linear.RealMatrix;
/**
* Implementation of
* {@link org.apache.commons.math.stat.descriptive.MultivariateSummaryStatistics} that
* is safe to use in a multithreaded environment. Multiple threads can safely
* operate on a single instance without causing runtime exceptions due to race
* conditions. In effect, this implementation makes modification and access
* methods atomic operations for a single instance. That is to say, as one
* thread is computing a statistic from the instance, no other thread can modify
* the instance nor compute another statistic.
* @since 1.2
* @version $Revision: 618097 $ $Date: 2008-02-03 22:39:08 +0100 (dim., 03 févr. 2008) $
*/
public class SynchronizedMultivariateSummaryStatistics
extends MultivariateSummaryStatistics {
/** Serialization UID */
private static final long serialVersionUID = 7099834153347155363L;
/**
* Construct a SynchronizedMultivariateSummaryStatistics instance
* @param k dimension of the data
* @param isCovarianceBiasCorrected if true, the unbiased sample
* covariance is computed, otherwise the biased population covariance
* is computed
*/
public SynchronizedMultivariateSummaryStatistics(int k, boolean isCovarianceBiasCorrected) {
super(k, isCovarianceBiasCorrected);
}
/**
* @see org.apache.commons.math.stat.descriptive.MultivariateSummary#getSummary()
*/
public synchronized StatisticalMultivariateSummary getSummary() {
return super.getSummary();
}
/**
* @see org.apache.commons.math.stat.descriptive.MultivariateSummary#addValue(double[])
*/
public synchronized void addValue(double[] value)
throws DimensionMismatchException {
super.addValue(value);
}
/**
* @see org.apache.commons.math.stat.descriptive.MultivariateSummary#getDimension()
*/
public synchronized int getDimension() {
return super.getDimension();
}
/**
* @see org.apache.commons.math.stat.descriptive.MultivariateSummary#getN()
*/
public synchronized long getN() {
return super.getN();
}
/**
* @see org.apache.commons.math.stat.descriptive.MultivariateSummary#getSum()
*/
public synchronized double[] getSum() {
return super.getSum();
}
/**
* @see org.apache.commons.math.stat.descriptive.MultivariateSummary#getSummSq()
*/
public synchronized double[] getSumSq() {
return super.getSumSq();
}
/**
* @see org.apache.commons.math.stat.descriptive.MultivariateSummary#getSumLog()
*/
public synchronized double[] getSumLog() {
return super.getSumLog();
}
/**
* @see org.apache.commons.math.stat.descriptive.MultivariateSummary#getMean()
*/
public synchronized double[] getMean() {
return super.getMean();
}
/**
* @see org.apache.commons.math.stat.descriptive.MultivariateSummary#getStandardDeviation()
*/
public synchronized double[] getStandardDeviation() {
return super.getStandardDeviation();
}
/**
* @see org.apache.commons.math.stat.descriptive.MultivariateSummary#getCovariance()
*/
public synchronized RealMatrix getCovariance() {
return super.getCovariance();
}
/**
* @see org.apache.commons.math.stat.descriptive.MultivariateSummary#getMax()
*/
public synchronized double[] getMax() {
return super.getMax();
}
/**
* @see org.apache.commons.math.stat.descriptive.MultivariateSummary#getMin()
*/
public synchronized double[] getMin() {
return super.getMin();
}
/**
* @see org.apache.commons.math.stat.descriptive.MultivariateSummary#getGeometricMean()
*/
public synchronized double[] getGeometricMean() {
return super.getGeometricMean();
}
/**
* @see org.apache.commons.math.stat.descriptive.MultivariateSummary#toString()
*/
public synchronized String toString() {
return super.toString();
}
/**
* @see org.apache.commons.math.stat.descriptive.MultivariateSummary#clear()
*/
public synchronized void clear() {
super.clear();
}
/**
* @see org.apache.commons.math.stat.descriptive.MultivariateSummary#equals()
*/
public synchronized boolean equals(Object object) {
return super.equals(object);
}
/**
* @see org.apache.commons.math.stat.descriptive.MultivariateSummary#hashCode()
*/
public synchronized int hashCode() {
return super.hashCode();
}
/**
* @see org.apache.commons.math.stat.descriptive.MultivariateSummary#getSumImpl()
*/
public synchronized StorelessUnivariateStatistic[] getSumImpl() {
return super.getSumImpl();
}
/**
* @see org.apache.commons.math.stat.descriptive.MultivariateSummary#setSumImpl(StorelessUnivariateStatistic[])
*/
public synchronized void setSumImpl(StorelessUnivariateStatistic[] sumImpl) {
super.setSumImpl(sumImpl);
}
/**
* @see org.apache.commons.math.stat.descriptive.MultivariateSummary#getSumsqImpl()
*/
public synchronized StorelessUnivariateStatistic[] getSumsqImpl() {
return super.getSumsqImpl();
}
/**
* @see org.apache.commons.math.stat.descriptive.MultivariateSummary#setSumsqImpl(StorelessUnivariateStatistic[])
*/
public synchronized void setSumsqImpl(StorelessUnivariateStatistic[] sumsqImpl) {
super.setSumsqImpl(sumsqImpl);
}
/**
* @see org.apache.commons.math.stat.descriptive.MultivariateSummary#getMinImpl()
*/
public synchronized StorelessUnivariateStatistic[] getMinImpl() {
return super.getMinImpl();
}
/**
* @see org.apache.commons.math.stat.descriptive.MultivariateSummary#setMinImpl(StorelessUnivariateStatistic[])
*/
public synchronized void setMinImpl(StorelessUnivariateStatistic[] minImpl) {
super.setMinImpl(minImpl);
}
/**
* @see org.apache.commons.math.stat.descriptive.MultivariateSummary#getMaxImpl()
*/
public synchronized StorelessUnivariateStatistic[] getMaxImpl() {
return super.getMaxImpl();
}
/**
* @see org.apache.commons.math.stat.descriptive.MultivariateSummary#setMaxImpl(StorelessUnivariateStatistic[])
*/
public synchronized void setMaxImpl(StorelessUnivariateStatistic[] maxImpl) {
super.setMaxImpl(maxImpl);
}
/**
* @see org.apache.commons.math.stat.descriptive.MultivariateSummary#getSumLogImpl()
*/
public synchronized StorelessUnivariateStatistic[] getSumLogImpl() {
return super.getSumLogImpl();
}
/**
* @see org.apache.commons.math.stat.descriptive.MultivariateSummary#setSumLogImpl(StorelessUnivariateStatistic[])
*/
public synchronized void setSumLogImpl(StorelessUnivariateStatistic[] sumLogImpl) {
super.setSumLogImpl(sumLogImpl);
}
/**
* @see org.apache.commons.math.stat.descriptive.MultivariateSummary#getGeoMeanImpl()
*/
public synchronized StorelessUnivariateStatistic[] getGeoMeanImpl() {
return super.getGeoMeanImpl();
}
/**
* @see org.apache.commons.math.stat.descriptive.MultivariateSummary#setGeoMeanImpl(StorelessUnivariateStatistic[])
*/
public synchronized void setGeoMeanImpl(StorelessUnivariateStatistic[] geoMeanImpl) {
super.setGeoMeanImpl(geoMeanImpl);
}
/**
* @see org.apache.commons.math.stat.descriptive.MultivariateSummary#getMeanImpl()
*/
public synchronized StorelessUnivariateStatistic[] getMeanImpl() {
return super.getMeanImpl();
}
/**
* @see org.apache.commons.math.stat.descriptive.MultivariateSummary#setMeanImpl(StorelessUnivariateStatistic[])
*/
public synchronized void setMeanImpl(StorelessUnivariateStatistic[] meanImpl) {
super.setMeanImpl(meanImpl);
}
}

View File

@ -154,6 +154,9 @@ Commons Math Release Notes</title>
Added vectorial covariance computation (either sample or population
covariance)
</action>
<action dev="luc" type="update" >
Added multivariate summary statistics
</action>
</release>
<release version="1.1" date="2005-12-17"
description="This is a maintenance release containing bug fixes and enhancements.

View File

@ -0,0 +1,294 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.commons.math.stat.descriptive;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.apache.commons.math.DimensionMismatchException;
import org.apache.commons.math.TestUtils;
import org.apache.commons.math.stat.descriptive.moment.Mean;
/**
* Test cases for the {@link MultivariateSummaryStatistics} class.
*
* @version $Revision: 566833 $ $Date: 2007-08-16 13:36:33 -0700 (Thu, 16 Aug 2007) $
*/
public class MultivariateSummaryStatisticsTest extends TestCase {
public MultivariateSummaryStatisticsTest(String name) {
super(name);
}
public static Test suite() {
TestSuite suite = new TestSuite(MultivariateSummaryStatisticsTest.class);
suite.setName("MultivariateSummaryStatistics tests");
return suite;
}
public void testSetterInjection() throws Exception {
MultivariateSummaryStatistics u = new MultivariateSummaryStatistics(2, true);
u.setMeanImpl(new StorelessUnivariateStatistic[] {
new sumMean(), new sumMean()
});
u.addValue(new double[] { 1, 2 });
u.addValue(new double[] { 3, 4 });
assertEquals(4, u.getMean()[0], 1E-14);
assertEquals(6, u.getMean()[1], 1E-14);
u.clear();
u.addValue(new double[] { 1, 2 });
u.addValue(new double[] { 3, 4 });
assertEquals(4, u.getMean()[0], 1E-14);
assertEquals(6, u.getMean()[1], 1E-14);
u.clear();
u.setMeanImpl(new StorelessUnivariateStatistic[] {
new Mean(), new Mean()
}); // OK after clear
u.addValue(new double[] { 1, 2 });
u.addValue(new double[] { 3, 4 });
assertEquals(2, u.getMean()[0], 1E-14);
assertEquals(3, u.getMean()[1], 1E-14);
}
public void testSetterIllegalState() throws Exception {
MultivariateSummaryStatistics u = new MultivariateSummaryStatistics(2, true);
u.addValue(new double[] { 1, 2 });
u.addValue(new double[] { 3, 4 });
try {
u.setMeanImpl(new StorelessUnivariateStatistic[] {
new sumMean(), new sumMean()
});
fail("Expecting IllegalStateException");
} catch (IllegalStateException ex) {
// expected
}
}
/**
* Bogus mean implementation to test setter injection.
* Returns the sum instead of the mean.
*/
static class sumMean implements StorelessUnivariateStatistic {
private static final long serialVersionUID = 6492471391340853423L;
private double sum = 0;
private long n = 0;
public double evaluate(double[] values, int begin, int length) {
return 0;
}
public double evaluate(double[] values) {
return 0;
}
public void clear() {
sum = 0;
n = 0;
}
public long getN() {
return n;
}
public double getResult() {
return sum;
}
public void increment(double d) {
sum += d;
n++;
}
public void incrementAll(double[] values, int start, int length) {
}
public void incrementAll(double[] values) {
}
}
public void testDimension() {
try {
new MultivariateSummaryStatistics(2, true).addValue(new double[3]);
} catch (DimensionMismatchException dme) {
// expected behavior
} catch (Exception e) {
fail("wrong exception caught");
}
}
/** test stats */
public void testStats() throws DimensionMismatchException {
MultivariateSummaryStatistics u = new MultivariateSummaryStatistics(2, true);
assertEquals(0, u.getN());
u.addValue(new double[] { 1, 2 });
u.addValue(new double[] { 2, 3 });
u.addValue(new double[] { 2, 3 });
u.addValue(new double[] { 3, 4 });
assertEquals( 4, u.getN());
assertEquals( 8, u.getSum()[0], 1.0e-10);
assertEquals(12, u.getSum()[1], 1.0e-10);
assertEquals(18, u.getSumSq()[0], 1.0e-10);
assertEquals(38, u.getSumSq()[1], 1.0e-10);
assertEquals( 1, u.getMin()[0], 1.0e-10);
assertEquals( 2, u.getMin()[1], 1.0e-10);
assertEquals( 3, u.getMax()[0], 1.0e-10);
assertEquals( 4, u.getMax()[1], 1.0e-10);
assertEquals(2.4849066497880003102, u.getSumLog()[0], 1.0e-10);
assertEquals( 4.276666119016055311, u.getSumLog()[1], 1.0e-10);
assertEquals( 1.8612097182041991979, u.getGeometricMean()[0], 1.0e-10);
assertEquals( 2.9129506302439405217, u.getGeometricMean()[1], 1.0e-10);
assertEquals( 2, u.getMean()[0], 1.0e-10);
assertEquals( 3, u.getMean()[1], 1.0e-10);
assertEquals(Math.sqrt(2.0 / 3.0), u.getStandardDeviation()[0], 1.0e-10);
assertEquals(Math.sqrt(2.0 / 3.0), u.getStandardDeviation()[1], 1.0e-10);
assertEquals(2.0 / 3.0, u.getCovariance().getEntry(0, 0), 1.0e-10);
assertEquals(2.0 / 3.0, u.getCovariance().getEntry(0, 1), 1.0e-10);
assertEquals(2.0 / 3.0, u.getCovariance().getEntry(1, 0), 1.0e-10);
assertEquals(2.0 / 3.0, u.getCovariance().getEntry(1, 1), 1.0e-10);
u.clear();
assertEquals(0, u.getN());
}
public void testN0andN1Conditions() throws Exception {
MultivariateSummaryStatistics u = new MultivariateSummaryStatistics(1, true);
assertTrue(Double.isNaN(u.getMean()[0]));
assertTrue(Double.isNaN(u.getStandardDeviation()[0]));
/* n=1 */
u.addValue(new double[] { 1 });
assertEquals(1.0, u.getMean()[0], 1.0e-10);
assertEquals(1.0, u.getGeometricMean()[0], 1.0e-10);
assertEquals(0.0, u.getStandardDeviation()[0], 1.0e-10);
/* n=2 */
u.addValue(new double[] { 2 });
assertTrue(u.getStandardDeviation()[0] > 0);
}
public void testNaNContracts() throws DimensionMismatchException {
MultivariateSummaryStatistics u = new MultivariateSummaryStatistics(1, true);
assertTrue(Double.isNaN(u.getMean()[0]));
assertTrue(Double.isNaN(u.getMin()[0]));
assertTrue(Double.isNaN(u.getStandardDeviation()[0]));
assertTrue(Double.isNaN(u.getGeometricMean()[0]));
u.addValue(new double[] { 1.0 });
assertFalse(Double.isNaN(u.getMean()[0]));
assertFalse(Double.isNaN(u.getMin()[0]));
assertFalse(Double.isNaN(u.getStandardDeviation()[0]));
assertFalse(Double.isNaN(u.getGeometricMean()[0]));
}
public void testGetSummary() throws DimensionMismatchException {
MultivariateSummaryStatistics u = new MultivariateSummaryStatistics(2, true);
StatisticalMultivariateSummary summary = u.getSummary();
verifySummary(u, summary);
u.addValue(new double[] { 1, 2 });
summary = u.getSummary();
verifySummary(u, summary);
u.addValue(new double[] { 2, 5 });
summary = u.getSummary();
verifySummary(u, summary);
u.addValue(new double[] { 2, 2 });
summary = u.getSummary();
verifySummary(u, summary);
}
public void testSerialization() throws DimensionMismatchException {
MultivariateSummaryStatistics u = new MultivariateSummaryStatistics(2, true);
// Empty test
TestUtils.checkSerializedEquality(u);
MultivariateSummaryStatistics s = (MultivariateSummaryStatistics) TestUtils.serializeAndRecover(u);
StatisticalMultivariateSummary summary = s.getSummary();
verifySummary(u, summary);
// Add some data
u.addValue(new double[] { 2d, 1d });
u.addValue(new double[] { 1d, 1d });
u.addValue(new double[] { 3d, 1d });
u.addValue(new double[] { 4d, 1d });
u.addValue(new double[] { 5d, 1d });
// Test again
TestUtils.checkSerializedEquality(u);
s = (MultivariateSummaryStatistics) TestUtils.serializeAndRecover(u);
summary = s.getSummary();
verifySummary(u, summary);
}
public void testEqualsAndHashCode() throws DimensionMismatchException {
MultivariateSummaryStatistics u = new MultivariateSummaryStatistics(2, true);
MultivariateSummaryStatistics t = null;
int emptyHash = u.hashCode();
assertTrue(u.equals(u));
assertFalse(u.equals(t));
assertFalse(u.equals(new Double(0)));
t = new MultivariateSummaryStatistics(2, true);
assertTrue(t.equals(u));
assertTrue(u.equals(t));
assertEquals(emptyHash, t.hashCode());
// Add some data to u
u.addValue(new double[] { 2d, 1d });
u.addValue(new double[] { 1d, 1d });
u.addValue(new double[] { 3d, 1d });
u.addValue(new double[] { 4d, 1d });
u.addValue(new double[] { 5d, 1d });
assertFalse(t.equals(u));
assertFalse(u.equals(t));
assertTrue(u.hashCode() != t.hashCode());
//Add data in same order to t
t.addValue(new double[] { 2d, 1d });
t.addValue(new double[] { 1d, 1d });
t.addValue(new double[] { 3d, 1d });
t.addValue(new double[] { 4d, 1d });
t.addValue(new double[] { 5d, 1d });
assertTrue(t.equals(u));
assertTrue(u.equals(t));
assertEquals(u.hashCode(), t.hashCode());
// Clear and make sure summaries are indistinguishable from empty summary
u.clear();
t.clear();
assertTrue(t.equals(u));
assertTrue(u.equals(t));
assertEquals(emptyHash, t.hashCode());
assertEquals(emptyHash, u.hashCode());
}
private void verifySummary(MultivariateSummaryStatistics u, StatisticalMultivariateSummary s) {
assertEquals(s.getN(), u.getN());
for (int i = 0; i < u.getDimension(); ++i) {
checkValue(s.getSum()[i], u.getSum()[i], 1.0e-10);
checkValue(s.getStandardDeviation()[i], u.getStandardDeviation()[i], 1.0e-10);
checkValue(s.getMean()[i], u.getMean()[i], 1.0e-10);
checkValue(s.getMin()[i], u.getMin()[i], 1.0e-10);
checkValue(s.getMax()[i], u.getMax()[i], 1.0e-10);
checkValue(s.getSumSq()[i], u.getSumSq()[i], 1.0e-10);
checkValue(s.getSumLog()[i], u.getSumLog()[i], 1.0e-10);
checkValue(s.getMax()[i], u.getMax()[i], 1.0e-10);
}
}
private void checkValue(double expected, double actual, double tolerance) {
if (Double.isNaN(expected)) {
assertTrue(Double.isNaN(actual));
} else {
assertEquals(expected, actual, tolerance);
}
}
}