Home | History | Annotate | Download | only in descriptive
      1 /*
      2  * Licensed to the Apache Software Foundation (ASF) under one or more
      3  * contributor license agreements.  See the NOTICE file distributed with
      4  * this work for additional information regarding copyright ownership.
      5  * The ASF licenses this file to You under the Apache License, Version 2.0
      6  * (the "License"); you may not use this file except in compliance with
      7  * the License.  You may obtain a copy of the License at
      8  *
      9  *      http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 package org.apache.commons.math.stat.descriptive;
     18 
     19 import java.io.Serializable;
     20 import java.lang.reflect.InvocationTargetException;
     21 import java.util.Arrays;
     22 
     23 import org.apache.commons.math.MathRuntimeException;
     24 import org.apache.commons.math.exception.util.LocalizedFormats;
     25 import org.apache.commons.math.stat.descriptive.moment.GeometricMean;
     26 import org.apache.commons.math.stat.descriptive.moment.Kurtosis;
     27 import org.apache.commons.math.stat.descriptive.moment.Mean;
     28 import org.apache.commons.math.stat.descriptive.moment.Skewness;
     29 import org.apache.commons.math.stat.descriptive.moment.Variance;
     30 import org.apache.commons.math.stat.descriptive.rank.Max;
     31 import org.apache.commons.math.stat.descriptive.rank.Min;
     32 import org.apache.commons.math.stat.descriptive.rank.Percentile;
     33 import org.apache.commons.math.stat.descriptive.summary.Sum;
     34 import org.apache.commons.math.stat.descriptive.summary.SumOfSquares;
     35 import org.apache.commons.math.util.ResizableDoubleArray;
     36 import org.apache.commons.math.util.FastMath;
     37 
     38 
     39 /**
     40  * Maintains a dataset of values of a single variable and computes descriptive
     41  * statistics based on stored data. The {@link #getWindowSize() windowSize}
     42  * property sets a limit on the number of values that can be stored in the
     43  * dataset.  The default value, INFINITE_WINDOW, puts no limit on the size of
     44  * the dataset.  This value should be used with caution, as the backing store
     45  * will grow without bound in this case.  For very large datasets,
     46  * {@link SummaryStatistics}, which does not store the dataset, should be used
     47  * instead of this class. If <code>windowSize</code> is not INFINITE_WINDOW and
     48  * more values are added than can be stored in the dataset, new values are
     49  * added in a "rolling" manner, with new values replacing the "oldest" values
     50  * in the dataset.
     51  *
     52  * <p>Note: this class is not threadsafe.  Use
     53  * {@link SynchronizedDescriptiveStatistics} if concurrent access from multiple
     54  * threads is required.</p>
     55  *
     56  * @version $Revision: 1054186 $ $Date: 2011-01-01 03:28:46 +0100 (sam. 01 janv. 2011) $
     57  */
     58 public class DescriptiveStatistics implements StatisticalSummary, Serializable {
     59 
     60     /**
     61      * Represents an infinite window size.  When the {@link #getWindowSize()}
     62      * returns this value, there is no limit to the number of data values
     63      * that can be stored in the dataset.
     64      */
     65     public static final int INFINITE_WINDOW = -1;
     66 
     67     /** Serialization UID */
     68     private static final long serialVersionUID = 4133067267405273064L;
     69 
     70     /** Name of the setQuantile method. */
     71     private static final String SET_QUANTILE_METHOD_NAME = "setQuantile";
     72 
     73     /** hold the window size **/
     74     protected int windowSize = INFINITE_WINDOW;
     75 
     76     /**
     77      *  Stored data values
     78      */
     79     protected ResizableDoubleArray eDA = new ResizableDoubleArray();
     80 
     81     /** Mean statistic implementation - can be reset by setter. */
     82     private UnivariateStatistic meanImpl = new Mean();
     83 
     84     /** Geometric mean statistic implementation - can be reset by setter. */
     85     private UnivariateStatistic geometricMeanImpl = new GeometricMean();
     86 
     87     /** Kurtosis statistic implementation - can be reset by setter. */
     88     private UnivariateStatistic kurtosisImpl = new Kurtosis();
     89 
     90     /** Maximum statistic implementation - can be reset by setter. */
     91     private UnivariateStatistic maxImpl = new Max();
     92 
     93     /** Minimum statistic implementation - can be reset by setter. */
     94     private UnivariateStatistic minImpl = new Min();
     95 
     96     /** Percentile statistic implementation - can be reset by setter. */
     97     private UnivariateStatistic percentileImpl = new Percentile();
     98 
     99     /** Skewness statistic implementation - can be reset by setter. */
    100     private UnivariateStatistic skewnessImpl = new Skewness();
    101 
    102     /** Variance statistic implementation - can be reset by setter. */
    103     private UnivariateStatistic varianceImpl = new Variance();
    104 
    105     /** Sum of squares statistic implementation - can be reset by setter. */
    106     private UnivariateStatistic sumsqImpl = new SumOfSquares();
    107 
    108     /** Sum statistic implementation - can be reset by setter. */
    109     private UnivariateStatistic sumImpl = new Sum();
    110 
    111     /**
    112      * Construct a DescriptiveStatistics instance with an infinite window
    113      */
    114     public DescriptiveStatistics() {
    115     }
    116 
    117     /**
    118      * Construct a DescriptiveStatistics instance with the specified window
    119      *
    120      * @param window the window size.
    121      */
    122     public DescriptiveStatistics(int window) {
    123         setWindowSize(window);
    124     }
    125 
    126     /**
    127      * Construct a DescriptiveStatistics instance with an infinite window
    128      * and the initial data values in double[] initialDoubleArray.
    129      * If initialDoubleArray is null, then this constructor corresponds to
    130      * DescriptiveStatistics()
    131      *
    132      * @param initialDoubleArray the initial double[].
    133      */
    134     public DescriptiveStatistics(double[] initialDoubleArray) {
    135         if (initialDoubleArray != null) {
    136             eDA = new ResizableDoubleArray(initialDoubleArray);
    137         }
    138     }
    139 
    140     /**
    141      * Copy constructor.  Construct a new DescriptiveStatistics instance that
    142      * is a copy of original.
    143      *
    144      * @param original DescriptiveStatistics instance to copy
    145      */
    146     public DescriptiveStatistics(DescriptiveStatistics original) {
    147         copy(original, this);
    148     }
    149 
    150     /**
    151      * Adds the value to the dataset. If the dataset is at the maximum size
    152      * (i.e., the number of stored elements equals the currently configured
    153      * windowSize), the first (oldest) element in the dataset is discarded
    154      * to make room for the new value.
    155      *
    156      * @param v the value to be added
    157      */
    158     public void addValue(double v) {
    159         if (windowSize != INFINITE_WINDOW) {
    160             if (getN() == windowSize) {
    161                 eDA.addElementRolling(v);
    162             } else if (getN() < windowSize) {
    163                 eDA.addElement(v);
    164             }
    165         } else {
    166             eDA.addElement(v);
    167         }
    168     }
    169 
    170     /**
    171      * Removes the most recent value from the dataset.
    172      */
    173     public void removeMostRecentValue() {
    174         eDA.discardMostRecentElements(1);
    175     }
    176 
    177     /**
    178      * Replaces the most recently stored value with the given value.
    179      * There must be at least one element stored to call this method.
    180      *
    181      * @param v the value to replace the most recent stored value
    182      * @return replaced value
    183      */
    184     public double replaceMostRecentValue(double v) {
    185         return eDA.substituteMostRecentElement(v);
    186     }
    187 
    188     /**
    189      * Returns the <a href="http://www.xycoon.com/arithmetic_mean.htm">
    190      * arithmetic mean </a> of the available values
    191      * @return The mean or Double.NaN if no values have been added.
    192      */
    193     public double getMean() {
    194         return apply(meanImpl);
    195     }
    196 
    197     /**
    198      * Returns the <a href="http://www.xycoon.com/geometric_mean.htm">
    199      * geometric mean </a> of the available values
    200      * @return The geometricMean, Double.NaN if no values have been added,
    201      * or if the product of the available values is less than or equal to 0.
    202      */
    203     public double getGeometricMean() {
    204         return apply(geometricMeanImpl);
    205     }
    206 
    207     /**
    208      * Returns the variance of the available values.
    209      * @return The variance, Double.NaN if no values have been added
    210      * or 0.0 for a single value set.
    211      */
    212     public double getVariance() {
    213         return apply(varianceImpl);
    214     }
    215 
    216     /**
    217      * Returns the standard deviation of the available values.
    218      * @return The standard deviation, Double.NaN if no values have been added
    219      * or 0.0 for a single value set.
    220      */
    221     public double getStandardDeviation() {
    222         double stdDev = Double.NaN;
    223         if (getN() > 0) {
    224             if (getN() > 1) {
    225                 stdDev = FastMath.sqrt(getVariance());
    226             } else {
    227                 stdDev = 0.0;
    228             }
    229         }
    230         return stdDev;
    231     }
    232 
    233     /**
    234      * Returns the skewness of the available values. Skewness is a
    235      * measure of the asymmetry of a given distribution.
    236      * @return The skewness, Double.NaN if no values have been added
    237      * or 0.0 for a value set &lt;=2.
    238      */
    239     public double getSkewness() {
    240         return apply(skewnessImpl);
    241     }
    242 
    243     /**
    244      * Returns the Kurtosis of the available values. Kurtosis is a
    245      * measure of the "peakedness" of a distribution
    246      * @return The kurtosis, Double.NaN if no values have been added, or 0.0
    247      * for a value set &lt;=3.
    248      */
    249     public double getKurtosis() {
    250         return apply(kurtosisImpl);
    251     }
    252 
    253     /**
    254      * Returns the maximum of the available values
    255      * @return The max or Double.NaN if no values have been added.
    256      */
    257     public double getMax() {
    258         return apply(maxImpl);
    259     }
    260 
    261     /**
    262     * Returns the minimum of the available values
    263     * @return The min or Double.NaN if no values have been added.
    264     */
    265     public double getMin() {
    266         return apply(minImpl);
    267     }
    268 
    269     /**
    270      * Returns the number of available values
    271      * @return The number of available values
    272      */
    273     public long getN() {
    274         return eDA.getNumElements();
    275     }
    276 
    277     /**
    278      * Returns the sum of the values that have been added to Univariate.
    279      * @return The sum or Double.NaN if no values have been added
    280      */
    281     public double getSum() {
    282         return apply(sumImpl);
    283     }
    284 
    285     /**
    286      * Returns the sum of the squares of the available values.
    287      * @return The sum of the squares or Double.NaN if no
    288      * values have been added.
    289      */
    290     public double getSumsq() {
    291         return apply(sumsqImpl);
    292     }
    293 
    294     /**
    295      * Resets all statistics and storage
    296      */
    297     public void clear() {
    298         eDA.clear();
    299     }
    300 
    301 
    302     /**
    303      * Returns the maximum number of values that can be stored in the
    304      * dataset, or INFINITE_WINDOW (-1) if there is no limit.
    305      *
    306      * @return The current window size or -1 if its Infinite.
    307      */
    308     public int getWindowSize() {
    309         return windowSize;
    310     }
    311 
    312     /**
    313      * WindowSize controls the number of values which contribute
    314      * to the reported statistics.  For example, if
    315      * windowSize is set to 3 and the values {1,2,3,4,5}
    316      * have been added <strong> in that order</strong>
    317      * then the <i>available values</i> are {3,4,5} and all
    318      * reported statistics will be based on these values
    319      * @param windowSize sets the size of the window.
    320      */
    321     public void setWindowSize(int windowSize) {
    322         if (windowSize < 1) {
    323             if (windowSize != INFINITE_WINDOW) {
    324                 throw MathRuntimeException.createIllegalArgumentException(
    325                       LocalizedFormats.NOT_POSITIVE_WINDOW_SIZE, windowSize);
    326             }
    327         }
    328 
    329         this.windowSize = windowSize;
    330 
    331         // We need to check to see if we need to discard elements
    332         // from the front of the array.  If the windowSize is less than
    333         // the current number of elements.
    334         if (windowSize != INFINITE_WINDOW && windowSize < eDA.getNumElements()) {
    335             eDA.discardFrontElements(eDA.getNumElements() - windowSize);
    336         }
    337     }
    338 
    339     /**
    340      * Returns the current set of values in an array of double primitives.
    341      * The order of addition is preserved.  The returned array is a fresh
    342      * copy of the underlying data -- i.e., it is not a reference to the
    343      * stored data.
    344      *
    345      * @return returns the current set of numbers in the order in which they
    346      *         were added to this set
    347      */
    348     public double[] getValues() {
    349         return eDA.getElements();
    350     }
    351 
    352     /**
    353      * Returns the current set of values in an array of double primitives,
    354      * sorted in ascending order.  The returned array is a fresh
    355      * copy of the underlying data -- i.e., it is not a reference to the
    356      * stored data.
    357      * @return returns the current set of
    358      * numbers sorted in ascending order
    359      */
    360     public double[] getSortedValues() {
    361         double[] sort = getValues();
    362         Arrays.sort(sort);
    363         return sort;
    364     }
    365 
    366     /**
    367      * Returns the element at the specified index
    368      * @param index The Index of the element
    369      * @return return the element at the specified index
    370      */
    371     public double getElement(int index) {
    372         return eDA.getElement(index);
    373     }
    374 
    375     /**
    376      * Returns an estimate for the pth percentile of the stored values.
    377      * <p>
    378      * The implementation provided here follows the first estimation procedure presented
    379      * <a href="http://www.itl.nist.gov/div898/handbook/prc/section2/prc252.htm">here.</a>
    380      * </p><p>
    381      * <strong>Preconditions</strong>:<ul>
    382      * <li><code>0 &lt; p &le; 100</code> (otherwise an
    383      * <code>IllegalArgumentException</code> is thrown)</li>
    384      * <li>at least one value must be stored (returns <code>Double.NaN
    385      *     </code> otherwise)</li>
    386      * </ul></p>
    387      *
    388      * @param p the requested percentile (scaled from 0 - 100)
    389      * @return An estimate for the pth percentile of the stored data
    390      * @throws IllegalStateException if percentile implementation has been
    391      *  overridden and the supplied implementation does not support setQuantile
    392      * values
    393      */
    394     public double getPercentile(double p) {
    395         if (percentileImpl instanceof Percentile) {
    396             ((Percentile) percentileImpl).setQuantile(p);
    397         } else {
    398             try {
    399                 percentileImpl.getClass().getMethod(SET_QUANTILE_METHOD_NAME,
    400                         new Class[] {Double.TYPE}).invoke(percentileImpl,
    401                                 new Object[] {Double.valueOf(p)});
    402             } catch (NoSuchMethodException e1) { // Setter guard should prevent
    403                 throw MathRuntimeException.createIllegalArgumentException(
    404                       LocalizedFormats.PERCENTILE_IMPLEMENTATION_UNSUPPORTED_METHOD,
    405                       percentileImpl.getClass().getName(), SET_QUANTILE_METHOD_NAME);
    406             } catch (IllegalAccessException e2) {
    407                 throw MathRuntimeException.createIllegalArgumentException(
    408                       LocalizedFormats.PERCENTILE_IMPLEMENTATION_CANNOT_ACCESS_METHOD,
    409                       SET_QUANTILE_METHOD_NAME, percentileImpl.getClass().getName());
    410             } catch (InvocationTargetException e3) {
    411                 throw MathRuntimeException.createIllegalArgumentException(e3.getCause());
    412             }
    413         }
    414         return apply(percentileImpl);
    415     }
    416 
    417     /**
    418      * Generates a text report displaying univariate statistics from values
    419      * that have been added.  Each statistic is displayed on a separate
    420      * line.
    421      *
    422      * @return String with line feeds displaying statistics
    423      */
    424     @Override
    425     public String toString() {
    426         StringBuilder outBuffer = new StringBuilder();
    427         String endl = "\n";
    428         outBuffer.append("DescriptiveStatistics:").append(endl);
    429         outBuffer.append("n: ").append(getN()).append(endl);
    430         outBuffer.append("min: ").append(getMin()).append(endl);
    431         outBuffer.append("max: ").append(getMax()).append(endl);
    432         outBuffer.append("mean: ").append(getMean()).append(endl);
    433         outBuffer.append("std dev: ").append(getStandardDeviation())
    434             .append(endl);
    435         outBuffer.append("median: ").append(getPercentile(50)).append(endl);
    436         outBuffer.append("skewness: ").append(getSkewness()).append(endl);
    437         outBuffer.append("kurtosis: ").append(getKurtosis()).append(endl);
    438         return outBuffer.toString();
    439     }
    440 
    441     /**
    442      * Apply the given statistic to the data associated with this set of statistics.
    443      * @param stat the statistic to apply
    444      * @return the computed value of the statistic.
    445      */
    446     public double apply(UnivariateStatistic stat) {
    447         return stat.evaluate(eDA.getInternalValues(), eDA.start(), eDA.getNumElements());
    448     }
    449 
    450     // Implementation getters and setter
    451 
    452     /**
    453      * Returns the currently configured mean implementation.
    454      *
    455      * @return the UnivariateStatistic implementing the mean
    456      * @since 1.2
    457      */
    458     public synchronized UnivariateStatistic getMeanImpl() {
    459         return meanImpl;
    460     }
    461 
    462     /**
    463      * <p>Sets the implementation for the mean.</p>
    464      *
    465      * @param meanImpl the UnivariateStatistic instance to use
    466      * for computing the mean
    467      * @since 1.2
    468      */
    469     public synchronized void setMeanImpl(UnivariateStatistic meanImpl) {
    470         this.meanImpl = meanImpl;
    471     }
    472 
    473     /**
    474      * Returns the currently configured geometric mean implementation.
    475      *
    476      * @return the UnivariateStatistic implementing the geometric mean
    477      * @since 1.2
    478      */
    479     public synchronized UnivariateStatistic getGeometricMeanImpl() {
    480         return geometricMeanImpl;
    481     }
    482 
    483     /**
    484      * <p>Sets the implementation for the gemoetric mean.</p>
    485      *
    486      * @param geometricMeanImpl the UnivariateStatistic instance to use
    487      * for computing the geometric mean
    488      * @since 1.2
    489      */
    490     public synchronized void setGeometricMeanImpl(
    491             UnivariateStatistic geometricMeanImpl) {
    492         this.geometricMeanImpl = geometricMeanImpl;
    493     }
    494 
    495     /**
    496      * Returns the currently configured kurtosis implementation.
    497      *
    498      * @return the UnivariateStatistic implementing the kurtosis
    499      * @since 1.2
    500      */
    501     public synchronized UnivariateStatistic getKurtosisImpl() {
    502         return kurtosisImpl;
    503     }
    504 
    505     /**
    506      * <p>Sets the implementation for the kurtosis.</p>
    507      *
    508      * @param kurtosisImpl the UnivariateStatistic instance to use
    509      * for computing the kurtosis
    510      * @since 1.2
    511      */
    512     public synchronized void setKurtosisImpl(UnivariateStatistic kurtosisImpl) {
    513         this.kurtosisImpl = kurtosisImpl;
    514     }
    515 
    516     /**
    517      * Returns the currently configured maximum implementation.
    518      *
    519      * @return the UnivariateStatistic implementing the maximum
    520      * @since 1.2
    521      */
    522     public synchronized UnivariateStatistic getMaxImpl() {
    523         return maxImpl;
    524     }
    525 
    526     /**
    527      * <p>Sets the implementation for the maximum.</p>
    528      *
    529      * @param maxImpl the UnivariateStatistic instance to use
    530      * for computing the maximum
    531      * @since 1.2
    532      */
    533     public synchronized void setMaxImpl(UnivariateStatistic maxImpl) {
    534         this.maxImpl = maxImpl;
    535     }
    536 
    537     /**
    538      * Returns the currently configured minimum implementation.
    539      *
    540      * @return the UnivariateStatistic implementing the minimum
    541      * @since 1.2
    542      */
    543     public synchronized UnivariateStatistic getMinImpl() {
    544         return minImpl;
    545     }
    546 
    547     /**
    548      * <p>Sets the implementation for the minimum.</p>
    549      *
    550      * @param minImpl the UnivariateStatistic instance to use
    551      * for computing the minimum
    552      * @since 1.2
    553      */
    554     public synchronized void setMinImpl(UnivariateStatistic minImpl) {
    555         this.minImpl = minImpl;
    556     }
    557 
    558     /**
    559      * Returns the currently configured percentile implementation.
    560      *
    561      * @return the UnivariateStatistic implementing the percentile
    562      * @since 1.2
    563      */
    564     public synchronized UnivariateStatistic getPercentileImpl() {
    565         return percentileImpl;
    566     }
    567 
    568     /**
    569      * Sets the implementation to be used by {@link #getPercentile(double)}.
    570      * The supplied <code>UnivariateStatistic</code> must provide a
    571      * <code>setQuantile(double)</code> method; otherwise
    572      * <code>IllegalArgumentException</code> is thrown.
    573      *
    574      * @param percentileImpl the percentileImpl to set
    575      * @throws IllegalArgumentException if the supplied implementation does not
    576      *  provide a <code>setQuantile</code> method
    577      * @since 1.2
    578      */
    579     public synchronized void setPercentileImpl(
    580             UnivariateStatistic percentileImpl) {
    581         try {
    582             percentileImpl.getClass().getMethod(SET_QUANTILE_METHOD_NAME,
    583                     new Class[] {Double.TYPE}).invoke(percentileImpl,
    584                             new Object[] {Double.valueOf(50.0d)});
    585         } catch (NoSuchMethodException e1) {
    586             throw MathRuntimeException.createIllegalArgumentException(
    587                   LocalizedFormats.PERCENTILE_IMPLEMENTATION_UNSUPPORTED_METHOD,
    588                   percentileImpl.getClass().getName(), SET_QUANTILE_METHOD_NAME);
    589         } catch (IllegalAccessException e2) {
    590             throw MathRuntimeException.createIllegalArgumentException(
    591                   LocalizedFormats.PERCENTILE_IMPLEMENTATION_CANNOT_ACCESS_METHOD,
    592                   SET_QUANTILE_METHOD_NAME, percentileImpl.getClass().getName());
    593         } catch (InvocationTargetException e3) {
    594             throw MathRuntimeException.createIllegalArgumentException(e3.getCause());
    595         }
    596         this.percentileImpl = percentileImpl;
    597     }
    598 
    599     /**
    600      * Returns the currently configured skewness implementation.
    601      *
    602      * @return the UnivariateStatistic implementing the skewness
    603      * @since 1.2
    604      */
    605     public synchronized UnivariateStatistic getSkewnessImpl() {
    606         return skewnessImpl;
    607     }
    608 
    609     /**
    610      * <p>Sets the implementation for the skewness.</p>
    611      *
    612      * @param skewnessImpl the UnivariateStatistic instance to use
    613      * for computing the skewness
    614      * @since 1.2
    615      */
    616     public synchronized void setSkewnessImpl(
    617             UnivariateStatistic skewnessImpl) {
    618         this.skewnessImpl = skewnessImpl;
    619     }
    620 
    621     /**
    622      * Returns the currently configured variance implementation.
    623      *
    624      * @return the UnivariateStatistic implementing the variance
    625      * @since 1.2
    626      */
    627     public synchronized UnivariateStatistic getVarianceImpl() {
    628         return varianceImpl;
    629     }
    630 
    631     /**
    632      * <p>Sets the implementation for the variance.</p>
    633      *
    634      * @param varianceImpl the UnivariateStatistic instance to use
    635      * for computing the variance
    636      * @since 1.2
    637      */
    638     public synchronized void setVarianceImpl(
    639             UnivariateStatistic varianceImpl) {
    640         this.varianceImpl = varianceImpl;
    641     }
    642 
    643     /**
    644      * Returns the currently configured sum of squares implementation.
    645      *
    646      * @return the UnivariateStatistic implementing the sum of squares
    647      * @since 1.2
    648      */
    649     public synchronized UnivariateStatistic getSumsqImpl() {
    650         return sumsqImpl;
    651     }
    652 
    653     /**
    654      * <p>Sets the implementation for the sum of squares.</p>
    655      *
    656      * @param sumsqImpl the UnivariateStatistic instance to use
    657      * for computing the sum of squares
    658      * @since 1.2
    659      */
    660     public synchronized void setSumsqImpl(UnivariateStatistic sumsqImpl) {
    661         this.sumsqImpl = sumsqImpl;
    662     }
    663 
    664     /**
    665      * Returns the currently configured sum implementation.
    666      *
    667      * @return the UnivariateStatistic implementing the sum
    668      * @since 1.2
    669      */
    670     public synchronized UnivariateStatistic getSumImpl() {
    671         return sumImpl;
    672     }
    673 
    674     /**
    675      * <p>Sets the implementation for the sum.</p>
    676      *
    677      * @param sumImpl the UnivariateStatistic instance to use
    678      * for computing the sum
    679      * @since 1.2
    680      */
    681     public synchronized void setSumImpl(UnivariateStatistic sumImpl) {
    682         this.sumImpl = sumImpl;
    683     }
    684 
    685     /**
    686      * Returns a copy of this DescriptiveStatistics instance with the same internal state.
    687      *
    688      * @return a copy of this
    689      */
    690     public DescriptiveStatistics copy() {
    691         DescriptiveStatistics result = new DescriptiveStatistics();
    692         copy(this, result);
    693         return result;
    694     }
    695 
    696     /**
    697      * Copies source to dest.
    698      * <p>Neither source nor dest can be null.</p>
    699      *
    700      * @param source DescriptiveStatistics to copy
    701      * @param dest DescriptiveStatistics to copy to
    702      * @throws NullPointerException if either source or dest is null
    703      */
    704     public static void copy(DescriptiveStatistics source, DescriptiveStatistics dest) {
    705         // Copy data and window size
    706         dest.eDA = source.eDA.copy();
    707         dest.windowSize = source.windowSize;
    708 
    709         // Copy implementations
    710         dest.maxImpl = source.maxImpl.copy();
    711         dest.meanImpl = source.meanImpl.copy();
    712         dest.minImpl = source.minImpl.copy();
    713         dest.sumImpl = source.sumImpl.copy();
    714         dest.varianceImpl = source.varianceImpl.copy();
    715         dest.sumsqImpl = source.sumsqImpl.copy();
    716         dest.geometricMeanImpl = source.geometricMeanImpl.copy();
    717         dest.kurtosisImpl = source.kurtosisImpl;
    718         dest.skewnessImpl = source.skewnessImpl;
    719         dest.percentileImpl = source.percentileImpl;
    720     }
    721 }
    722