    001    /*
    002     * Licensed to the Apache Software Foundation (ASF) under one or more
    003     * contributor license agreements.  See the NOTICE file distributed with
    004     * this work for additional information regarding copyright ownership.
    005     * The ASF licenses this file to You under the Apache License, Version 2.0
    006     * (the "License"); you may not use this file except in compliance with
    007     * the License.  You may obtain a copy of the License at
    008     *
    009     *      http://www.apache.org/licenses/LICENSE-2.0
    010     *
    011     * Unless required by applicable law or agreed to in writing, software
    012     * distributed under the License is distributed on an "AS IS" BASIS,
    013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014     * See the License for the specific language governing permissions and
    015     * limitations under the License.
    016     */
    017    package org.apache.commons.math3.stat.descriptive;
    018
    019    import java.io.Serializable;
    020    import java.lang.reflect.InvocationTargetException;
    021    import java.util.Arrays;
    022
    023    import org.apache.commons.math3.exception.MathIllegalArgumentException;
    024    import org.apache.commons.math3.exception.NullArgumentException;
    025    import org.apache.commons.math3.exception.MathIllegalStateException;
    026    import org.apache.commons.math3.exception.util.LocalizedFormats;
    027    import org.apache.commons.math3.stat.descriptive.moment.GeometricMean;
    028    import org.apache.commons.math3.stat.descriptive.moment.Kurtosis;
    029    import org.apache.commons.math3.stat.descriptive.moment.Mean;
    030    import org.apache.commons.math3.stat.descriptive.moment.Skewness;
    031    import org.apache.commons.math3.stat.descriptive.moment.Variance;
    032    import org.apache.commons.math3.stat.descriptive.rank.Max;
    033    import org.apache.commons.math3.stat.descriptive.rank.Min;
    034    import org.apache.commons.math3.stat.descriptive.rank.Percentile;
    035    import org.apache.commons.math3.stat.descriptive.summary.Sum;
    036    import org.apache.commons.math3.stat.descriptive.summary.SumOfSquares;
    037    import org.apache.commons.math3.util.MathUtils;
    038    import org.apache.commons.math3.util.ResizableDoubleArray;
    039    import org.apache.commons.math3.util.FastMath;
    040
    041
    042    /**
    043     * Maintains a dataset of values of a single variable and computes descriptive
    044     * statistics based on stored data. The {@link #getWindowSize() windowSize}
    045     * property sets a limit on the number of values that can be stored in the
    046     * dataset.  The default value, INFINITE_WINDOW, puts no limit on the size of
    047     * the dataset.  This value should be used with caution, as the backing store
    048     * will grow without bound in this case.  For very large datasets,
    049     * {@link SummaryStatistics}, which does not store the dataset, should be used
    050     * instead of this class. If <code>windowSize</code> is not INFINITE_WINDOW and
    051     * more values are added than can be stored in the dataset, new values are
    052     * added in a "rolling" manner, with new values replacing the "oldest" values
    053     * in the dataset.
    054     *
    055     * <p>Note: this class is not threadsafe.  Use
    056     * {@link SynchronizedDescriptiveStatistics} if concurrent access from multiple
    057     * threads is required.</p>
    058     *
    059     * @version $Id: DescriptiveStatistics.java 1422354 2012-12-15 20:59:01Z psteitz $
    060     */
    061    public class DescriptiveStatistics implements StatisticalSummary, Serializable {
    062
    063        /**
    064         * Represents an infinite window size.  When the {@link #getWindowSize()}
    065         * returns this value, there is no limit to the number of data values
    066         * that can be stored in the dataset.
    067         */
    068        public static final int INFINITE_WINDOW = -1;
    069
    070        /** Serialization UID */
    071        private static final long serialVersionUID = 4133067267405273064L;
    072
    073        /** Name of the setQuantile method. */
    074        private static final String SET_QUANTILE_METHOD_NAME = "setQuantile";
    075
    076        /** hold the window size **/
    077        protected int windowSize = INFINITE_WINDOW;
    078
    079        /**
    080         *  Stored data values
    081         */
    082        private ResizableDoubleArray eDA = new ResizableDoubleArray();
    083
    084        /** Mean statistic implementation - can be reset by setter. */
    085        private UnivariateStatistic meanImpl = new Mean();
    086
    087        /** Geometric mean statistic implementation - can be reset by setter. */
    088        private UnivariateStatistic geometricMeanImpl = new GeometricMean();
    089
    090        /** Kurtosis statistic implementation - can be reset by setter. */
    091        private UnivariateStatistic kurtosisImpl = new Kurtosis();
    092
    093        /** Maximum statistic implementation - can be reset by setter. */
    094        private UnivariateStatistic maxImpl = new Max();
    095
    096        /** Minimum statistic implementation - can be reset by setter. */
    097        private UnivariateStatistic minImpl = new Min();
    098
    099        /** Percentile statistic implementation - can be reset by setter. */
    100        private UnivariateStatistic percentileImpl = new Percentile();
    101
    102        /** Skewness statistic implementation - can be reset by setter. */
    103        private UnivariateStatistic skewnessImpl = new Skewness();
    104
    105        /** Variance statistic implementation - can be reset by setter. */
    106        private UnivariateStatistic varianceImpl = new Variance();
    107
    108        /** Sum of squares statistic implementation - can be reset by setter. */
    109        private UnivariateStatistic sumsqImpl = new SumOfSquares();
    110
    111        /** Sum statistic implementation - can be reset by setter. */
    112        private UnivariateStatistic sumImpl = new Sum();
    113
    114        /**
    115         * Construct a DescriptiveStatistics instance with an infinite window
    116         */
    117        public DescriptiveStatistics() {
    118        }
    119
    120        /**
    121         * Construct a DescriptiveStatistics instance with the specified window
    122         *
    123         * @param window the window size.
    124         * @throws MathIllegalArgumentException if window size is less than 1 but
    125         * not equal to {@link #INFINITE_WINDOW}
    126         */
    127        public DescriptiveStatistics(int window) throws MathIllegalArgumentException {
    128            setWindowSize(window);
    129        }
    130
    131        /**
    132         * Construct a DescriptiveStatistics instance with an infinite window
    133         * and the initial data values in double[] initialDoubleArray.
    134         * If initialDoubleArray is null, then this constructor corresponds to
    135         * DescriptiveStatistics()
    136         *
    137         * @param initialDoubleArray the initial double[].
    138         */
    139        public DescriptiveStatistics(double[] initialDoubleArray) {
    140            if (initialDoubleArray != null) {
    141                eDA = new ResizableDoubleArray(initialDoubleArray);
    142            }
    143        }
    144
    145        /**
    146         * Copy constructor.  Construct a new DescriptiveStatistics instance that
    147         * is a copy of original.
    148         *
    149         * @param original DescriptiveStatistics instance to copy
    150         * @throws NullArgumentException if original is null
    151         */
    152        public DescriptiveStatistics(DescriptiveStatistics original) throws NullArgumentException {
    153            copy(original, this);
    154        }
    155
    156        /**
    157         * Adds the value to the dataset. If the dataset is at the maximum size
    158         * (i.e., the number of stored elements equals the currently configured
    159         * windowSize), the first (oldest) element in the dataset is discarded
    160         * to make room for the new value.
    161         *
    162         * @param v the value to be added
    163         */
    164        public void addValue(double v) {
    165            if (windowSize != INFINITE_WINDOW) {
    166                if (getN() == windowSize) {
    167                    eDA.addElementRolling(v);
    168                } else if (getN() < windowSize) {
    169                    eDA.addElement(v);
    170                }
    171            } else {
    172                eDA.addElement(v);
    173            }
    174        }
    175
    176        /**
    177         * Removes the most recent value from the dataset.
    178         *
    179         * @throws MathIllegalStateException if there are no elements stored
    180         */
    181        public void removeMostRecentValue() throws MathIllegalStateException {
    182            try {
    183                eDA.discardMostRecentElements(1);
    184            } catch (MathIllegalArgumentException ex) {
    185                throw new MathIllegalStateException(LocalizedFormats.NO_DATA);
    186            }
    187        }
    188
    189        /**
    190         * Replaces the most recently stored value with the given value.
    191         * There must be at least one element stored to call this method.
    192         *
    193         * @param v the value to replace the most recent stored value
    194         * @return replaced value
    195         * @throws MathIllegalStateException if there are no elements stored
    196         */
    197        public double replaceMostRecentValue(double v) throws MathIllegalStateException {
    198            return eDA.substituteMostRecentElement(v);
    199        }
    200
    201        /**
    202         * Returns the <a href="http://www.xycoon.com/arithmetic_mean.htm">
    203         * arithmetic mean </a> of the available values
    204         * @return The mean or Double.NaN if no values have been added.
    205         */
    206        public double getMean() {
    207            return apply(meanImpl);
    208        }
    209
    210        /**
    211         * Returns the <a href="http://www.xycoon.com/geometric_mean.htm">
    212         * geometric mean </a> of the available values
    213         * @return The geometricMean, Double.NaN if no values have been added,
    214         * or if the product of the available values is less than or equal to 0.
    215         */
    216        public double getGeometricMean() {
    217            return apply(geometricMeanImpl);
    218        }
    219
    220        /**
    221         * Returns the (sample) variance of the available values.
    222         *
    223         * <p>This method returns the bias-corrected sample variance (using {@code n - 1} in
    224         * the denominator).  Use {@link #getPopulationVariance()} for the non-bias-corrected
    225         * population variance.</p>
    226         *
    227         * @return The variance, Double.NaN if no values have been added
    228         * or 0.0 for a single value set.
    229         */
    230        public double getVariance() {
    231            return apply(varianceImpl);
    232        }
    233
    234        /**
    235         * Returns the <a href="http://en.wikibooks.org/wiki/Statistics/Summary/Variance">
    236         * population variance</a> of the available values.
    237         *
    238         * @return The population variance, Double.NaN if no values have been added,
    239         * or 0.0 for a single value set.
    240         */
    241        public double getPopulationVariance() {
    242            return apply(new Variance(false));
    243        }
    244
    245        /**
    246         * Returns the standard deviation of the available values.
    247         * @return The standard deviation, Double.NaN if no values have been added
    248         * or 0.0 for a single value set.
    249         */
    250        public double getStandardDeviation() {
    251            double stdDev = Double.NaN;
    252            if (getN() > 0) {
    253                if (getN() > 1) {
    254                    stdDev = FastMath.sqrt(getVariance());
    255                } else {
    256                    stdDev = 0.0;
    257                }
    258            }
    259            return stdDev;
    260        }
    261
    262        /**
    263         * Returns the skewness of the available values. Skewness is a
    264         * measure of the asymmetry of a given distribution.
    265         * @return The skewness, Double.NaN if no values have been added
    266         * or 0.0 for a value set &lt;=2.
    267         */
    268        public double getSkewness() {
    269            return apply(skewnessImpl);
    270        }
    271
    272        /**
    273         * Returns the Kurtosis of the available values. Kurtosis is a
    274         * measure of the "peakedness" of a distribution
    275         * @return The kurtosis, Double.NaN if no values have been added, or 0.0
    276         * for a value set &lt;=3.
    277         */
    278        public double getKurtosis() {
    279            return apply(kurtosisImpl);
    280        }
    281
    282        /**
    283         * Returns the maximum of the available values
    284         * @return The max or Double.NaN if no values have been added.
    285         */
    286        public double getMax() {
    287            return apply(maxImpl);
    288        }
    289
    290        /**
    291        * Returns the minimum of the available values
    292        * @return The min or Double.NaN if no values have been added.
    293        */
    294        public double getMin() {
    295            return apply(minImpl);
    296        }
    297
    298        /**
    299         * Returns the number of available values
    300         * @return The number of available values
    301         */
    302        public long getN() {
    303            return eDA.getNumElements();
    304        }
    305
    306        /**
    307         * Returns the sum of the values that have been added to Univariate.
    308         * @return The sum or Double.NaN if no values have been added
    309         */
    310        public double getSum() {
    311            return apply(sumImpl);
    312        }
    313
    314        /**
    315         * Returns the sum of the squares of the available values.
    316         * @return The sum of the squares or Double.NaN if no
    317         * values have been added.
    318         */
    319        public double getSumsq() {
    320            return apply(sumsqImpl);
    321        }
    322
    323        /**
    324         * Resets all statistics and storage
    325         */
    326        public void clear() {
    327            eDA.clear();
    328        }
    329
    330
    331        /**
    332         * Returns the maximum number of values that can be stored in the
    333         * dataset, or INFINITE_WINDOW (-1) if there is no limit.
    334         *
    335         * @return The current window size or -1 if its Infinite.
    336         */
    337        public int getWindowSize() {
    338            return windowSize;
    339        }
    340
    341        /**
    342         * WindowSize controls the number of values that contribute to the
    343         * reported statistics.  For example, if windowSize is set to 3 and the
    344         * values {1,2,3,4,5} have been added <strong> in that order</strong> then
    345         * the <i>available values</i> are {3,4,5} and all reported statistics will
    346         * be based on these values. If {@code windowSize} is decreased as a result
    347         * of this call and there are more than the new value of elements in the
    348         * current dataset, values from the front of the array are discarded to
    349         * reduce the dataset to {@code windowSize} elements.
    350         *
    351         * @param windowSize sets the size of the window.
    352         * @throws MathIllegalArgumentException if window size is less than 1 but
    353         * not equal to {@link #INFINITE_WINDOW}
    354         */
    355        public void setWindowSize(int windowSize) throws MathIllegalArgumentException {
    356            if (windowSize < 1 && windowSize != INFINITE_WINDOW) {
    357                throw new MathIllegalArgumentException(
    358                        LocalizedFormats.NOT_POSITIVE_WINDOW_SIZE, windowSize);
    359            }
    360
    361            this.windowSize = windowSize;
    362
    363            // We need to check to see if we need to discard elements
    364            // from the front of the array.  If the windowSize is less than
    365            // the current number of elements.
    366            if (windowSize != INFINITE_WINDOW && windowSize < eDA.getNumElements()) {
    367                eDA.discardFrontElements(eDA.getNumElements() - windowSize);
    368            }
    369        }
    370
    371        /**
    372         * Returns the current set of values in an array of double primitives.
    373         * The order of addition is preserved.  The returned array is a fresh
    374         * copy of the underlying data -- i.e., it is not a reference to the
    375         * stored data.
    376         *
    377         * @return returns the current set of numbers in the order in which they
    378         *         were added to this set
    379         */
    380        public double[] getValues() {
    381            return eDA.getElements();
    382        }
    383
    384        /**
    385         * Returns the current set of values in an array of double primitives,
    386         * sorted in ascending order.  The returned array is a fresh
    387         * copy of the underlying data -- i.e., it is not a reference to the
    388         * stored data.
    389         * @return returns the current set of
    390         * numbers sorted in ascending order
    391         */
    392        public double[] getSortedValues() {
    393            double[] sort = getValues();
    394            Arrays.sort(sort);
    395            return sort;
    396        }
    397
    398        /**
    399         * Returns the element at the specified index
    400         * @param index The Index of the element
    401         * @return return the element at the specified index
    402         */
    403        public double getElement(int index) {
    404            return eDA.getElement(index);
    405        }
    406
    407        /**
    408         * Returns an estimate for the pth percentile of the stored values.
    409         * <p>
    410         * The implementation provided here follows the first estimation procedure presented
    411         * <a href="http://www.itl.nist.gov/div898/handbook/prc/section2/prc252.htm">here.</a>
    412         * </p><p>
    413         * <strong>Preconditions</strong>:<ul>
    414         * <li><code>0 &lt; p &le; 100</code> (otherwise an
    415         * <code>MathIllegalArgumentException</code> is thrown)</li>
    416         * <li>at least one value must be stored (returns <code>Double.NaN
    417         *     </code> otherwise)</li>
    418         * </ul></p>
    419         *
    420         * @param p the requested percentile (scaled from 0 - 100)
    421         * @return An estimate for the pth percentile of the stored data
    422         * @throws MathIllegalStateException if percentile implementation has been
    423         *  overridden and the supplied implementation does not support setQuantile
    424         * @throws MathIllegalArgumentException if p is not a valid quantile
    425         */
    426        public double getPercentile(double p) throws MathIllegalStateException, MathIllegalArgumentException {
    427            if (percentileImpl instanceof Percentile) {
    428                ((Percentile) percentileImpl).setQuantile(p);
    429            } else {
    430                try {
    431                    percentileImpl.getClass().getMethod(SET_QUANTILE_METHOD_NAME,
    432                            new Class[] {Double.TYPE}).invoke(percentileImpl,
    433                                    new Object[] {Double.valueOf(p)});
    434                } catch (NoSuchMethodException e1) { // Setter guard should prevent
    435                    throw new MathIllegalStateException(
    436                          LocalizedFormats.PERCENTILE_IMPLEMENTATION_UNSUPPORTED_METHOD,
    437                          percentileImpl.getClass().getName(), SET_QUANTILE_METHOD_NAME);
    438                } catch (IllegalAccessException e2) {
    439                    throw new MathIllegalStateException(
    440                          LocalizedFormats.PERCENTILE_IMPLEMENTATION_CANNOT_ACCESS_METHOD,
    441                          SET_QUANTILE_METHOD_NAME, percentileImpl.getClass().getName());
    442                } catch (InvocationTargetException e3) {
    443                    throw new IllegalStateException(e3.getCause());
    444                }
    445            }
    446            return apply(percentileImpl);
    447        }
    448
    449        /**
    450         * Generates a text report displaying univariate statistics from values
    451         * that have been added.  Each statistic is displayed on a separate
    452         * line.
    453         *
    454         * @return String with line feeds displaying statistics
    455         */
    456        @Override
    457        public String toString() {
    458            StringBuilder outBuffer = new StringBuilder();
    459            String endl = "\n";
    460            outBuffer.append("DescriptiveStatistics:").append(endl);
    461            outBuffer.append("n: ").append(getN()).append(endl);
    462            outBuffer.append("min: ").append(getMin()).append(endl);
    463            outBuffer.append("max: ").append(getMax()).append(endl);
    464            outBuffer.append("mean: ").append(getMean()).append(endl);
    465            outBuffer.append("std dev: ").append(getStandardDeviation())
    466                .append(endl);
    467            try {
    468                // No catch for MIAE because actual parameter is valid below
    469                outBuffer.append("median: ").append(getPercentile(50)).append(endl);
    470            } catch (MathIllegalStateException ex) {
    471                outBuffer.append("median: unavailable").append(endl);
    472            }
    473            outBuffer.append("skewness: ").append(getSkewness()).append(endl);
    474            outBuffer.append("kurtosis: ").append(getKurtosis()).append(endl);
    475            return outBuffer.toString();
    476        }
    477
    478        /**
    479         * Apply the given statistic to the data associated with this set of statistics.
    480         * @param stat the statistic to apply
    481         * @return the computed value of the statistic.
    482         */
    483        public double apply(UnivariateStatistic stat) {
    484            // No try-catch or advertised exception here because arguments are guaranteed valid
    485            return eDA.compute(stat);
    486        }
    487
    488        // Implementation getters and setter
    489
    490        /**
    491         * Returns the currently configured mean implementation.
    492         *
    493         * @return the UnivariateStatistic implementing the mean
    494         * @since 1.2
    495         */
    496        public synchronized UnivariateStatistic getMeanImpl() {
    497            return meanImpl;
    498        }
    499
    500        /**
    501         * <p>Sets the implementation for the mean.</p>
    502         *
    503         * @param meanImpl the UnivariateStatistic instance to use
    504         * for computing the mean
    505         * @since 1.2
    506         */
    507        public synchronized void setMeanImpl(UnivariateStatistic meanImpl) {
    508            this.meanImpl = meanImpl;
    509        }
    510
    511        /**
    512         * Returns the currently configured geometric mean implementation.
    513         *
    514         * @return the UnivariateStatistic implementing the geometric mean
    515         * @since 1.2
    516         */
    517        public synchronized UnivariateStatistic getGeometricMeanImpl() {
    518            return geometricMeanImpl;
    519        }
    520
    521        /**
    522         * <p>Sets the implementation for the gemoetric mean.</p>
    523         *
    524         * @param geometricMeanImpl the UnivariateStatistic instance to use
    525         * for computing the geometric mean
    526         * @since 1.2
    527         */
    528        public synchronized void setGeometricMeanImpl(
    529                UnivariateStatistic geometricMeanImpl) {
    530            this.geometricMeanImpl = geometricMeanImpl;
    531        }
    532
    533        /**
    534         * Returns the currently configured kurtosis implementation.
    535         *
    536         * @return the UnivariateStatistic implementing the kurtosis
    537         * @since 1.2
    538         */
    539        public synchronized UnivariateStatistic getKurtosisImpl() {
    540            return kurtosisImpl;
    541        }
    542
    543        /**
    544         * <p>Sets the implementation for the kurtosis.</p>
    545         *
    546         * @param kurtosisImpl the UnivariateStatistic instance to use
    547         * for computing the kurtosis
    548         * @since 1.2
    549         */
    550        public synchronized void setKurtosisImpl(UnivariateStatistic kurtosisImpl) {
    551            this.kurtosisImpl = kurtosisImpl;
    552        }
    553
    554        /**
    555         * Returns the currently configured maximum implementation.
    556         *
    557         * @return the UnivariateStatistic implementing the maximum
    558         * @since 1.2
    559         */
    560        public synchronized UnivariateStatistic getMaxImpl() {
    561            return maxImpl;
    562        }
    563
    564        /**
    565         * <p>Sets the implementation for the maximum.</p>
    566         *
    567         * @param maxImpl the UnivariateStatistic instance to use
    568         * for computing the maximum
    569         * @since 1.2
    570         */
    571        public synchronized void setMaxImpl(UnivariateStatistic maxImpl) {
    572            this.maxImpl = maxImpl;
    573        }
    574
    575        /**
    576         * Returns the currently configured minimum implementation.
    577         *
    578         * @return the UnivariateStatistic implementing the minimum
    579         * @since 1.2
    580         */
    581        public synchronized UnivariateStatistic getMinImpl() {
    582            return minImpl;
    583        }
    584
    585        /**
    586         * <p>Sets the implementation for the minimum.</p>
    587         *
    588         * @param minImpl the UnivariateStatistic instance to use
    589         * for computing the minimum
    590         * @since 1.2
    591         */
    592        public synchronized void setMinImpl(UnivariateStatistic minImpl) {
    593            this.minImpl = minImpl;
    594        }
    595
    596        /**
    597         * Returns the currently configured percentile implementation.
    598         *
    599         * @return the UnivariateStatistic implementing the percentile
    600         * @since 1.2
    601         */
    602        public synchronized UnivariateStatistic getPercentileImpl() {
    603            return percentileImpl;
    604        }
    605
    606        /**
    607         * Sets the implementation to be used by {@link #getPercentile(double)}.
    608         * The supplied <code>UnivariateStatistic</code> must provide a
    609         * <code>setQuantile(double)</code> method; otherwise
    610         * <code>IllegalArgumentException</code> is thrown.
    611         *
    612         * @param percentileImpl the percentileImpl to set
    613         * @throws MathIllegalArgumentException if the supplied implementation does not
    614         *  provide a <code>setQuantile</code> method
    615         * @since 1.2
    616         */
    617        public synchronized void setPercentileImpl(UnivariateStatistic percentileImpl)
    618        throws MathIllegalArgumentException {
    619            try {
    620                percentileImpl.getClass().getMethod(SET_QUANTILE_METHOD_NAME,
    621                        new Class[] {Double.TYPE}).invoke(percentileImpl,
    622                                new Object[] {Double.valueOf(50.0d)});
    623            } catch (NoSuchMethodException e1) {
    624                throw new MathIllegalArgumentException(
    625                      LocalizedFormats.PERCENTILE_IMPLEMENTATION_UNSUPPORTED_METHOD,
    626                      percentileImpl.getClass().getName(), SET_QUANTILE_METHOD_NAME);
    627            } catch (IllegalAccessException e2) {
    628                throw new MathIllegalArgumentException(
    629                      LocalizedFormats.PERCENTILE_IMPLEMENTATION_CANNOT_ACCESS_METHOD,
    630                      SET_QUANTILE_METHOD_NAME, percentileImpl.getClass().getName());
    631            } catch (InvocationTargetException e3) {
    632                throw new IllegalArgumentException(e3.getCause());
    633            }
    634            this.percentileImpl = percentileImpl;
    635        }
    636
    637        /**
    638         * Returns the currently configured skewness implementation.
    639         *
    640         * @return the UnivariateStatistic implementing the skewness
    641         * @since 1.2
    642         */
    643        public synchronized UnivariateStatistic getSkewnessImpl() {
    644            return skewnessImpl;
    645        }
    646
    647        /**
    648         * <p>Sets the implementation for the skewness.</p>
    649         *
    650         * @param skewnessImpl the UnivariateStatistic instance to use
    651         * for computing the skewness
    652         * @since 1.2
    653         */
    654        public synchronized void setSkewnessImpl(
    655                UnivariateStatistic skewnessImpl) {
    656            this.skewnessImpl = skewnessImpl;
    657        }
    658
    659        /**
    660         * Returns the currently configured variance implementation.
    661         *
    662         * @return the UnivariateStatistic implementing the variance
    663         * @since 1.2
    664         */
    665        public synchronized UnivariateStatistic getVarianceImpl() {
    666            return varianceImpl;
    667        }
    668
    669        /**
    670         * <p>Sets the implementation for the variance.</p>
    671         *
    672         * @param varianceImpl the UnivariateStatistic instance to use
    673         * for computing the variance
    674         * @since 1.2
    675         */
    676        public synchronized void setVarianceImpl(
    677                UnivariateStatistic varianceImpl) {
    678            this.varianceImpl = varianceImpl;
    679        }
    680
    681        /**
    682         * Returns the currently configured sum of squares implementation.
    683         *
    684         * @return the UnivariateStatistic implementing the sum of squares
    685         * @since 1.2
    686         */
    687        public synchronized UnivariateStatistic getSumsqImpl() {
    688            return sumsqImpl;
    689        }
    690
    691        /**
    692         * <p>Sets the implementation for the sum of squares.</p>
    693         *
    694         * @param sumsqImpl the UnivariateStatistic instance to use
    695         * for computing the sum of squares
    696         * @since 1.2
    697         */
    698        public synchronized void setSumsqImpl(UnivariateStatistic sumsqImpl) {
    699            this.sumsqImpl = sumsqImpl;
    700        }
    701
    702        /**
    703         * Returns the currently configured sum implementation.
    704         *
    705         * @return the UnivariateStatistic implementing the sum
    706         * @since 1.2
    707         */
    708        public synchronized UnivariateStatistic getSumImpl() {
    709            return sumImpl;
    710        }
    711
    712        /**
    713         * <p>Sets the implementation for the sum.</p>
    714         *
    715         * @param sumImpl the UnivariateStatistic instance to use
    716         * for computing the sum
    717         * @since 1.2
    718         */
    719        public synchronized void setSumImpl(UnivariateStatistic sumImpl) {
    720            this.sumImpl = sumImpl;
    721        }
    722
    723        /**
    724         * Returns a copy of this DescriptiveStatistics instance with the same internal state.
    725         *
    726         * @return a copy of this
    727         */
    728        public DescriptiveStatistics copy() {
    729            DescriptiveStatistics result = new DescriptiveStatistics();
    730            // No try-catch or advertised exception because parms are guaranteed valid
    731            copy(this, result);
    732            return result;
    733        }
    734
    735        /**
    736         * Copies source to dest.
    737         * <p>Neither source nor dest can be null.</p>
    738         *
    739         * @param source DescriptiveStatistics to copy
    740         * @param dest DescriptiveStatistics to copy to
    741         * @throws NullArgumentException if either source or dest is null
    742         */
    743        public static void copy(DescriptiveStatistics source, DescriptiveStatistics dest)
    744            throws NullArgumentException {
    745            MathUtils.checkNotNull(source);
    746            MathUtils.checkNotNull(dest);
    747            // Copy data and window size
    748            dest.eDA = source.eDA.copy();
    749            dest.windowSize = source.windowSize;
    750
    751            // Copy implementations
    752            dest.maxImpl = source.maxImpl.copy();
    753            dest.meanImpl = source.meanImpl.copy();
    754            dest.minImpl = source.minImpl.copy();
    755            dest.sumImpl = source.sumImpl.copy();
    756            dest.varianceImpl = source.varianceImpl.copy();
    757            dest.sumsqImpl = source.sumsqImpl.copy();
    758            dest.geometricMeanImpl = source.geometricMeanImpl.copy();
    759            dest.kurtosisImpl = source.kurtosisImpl;
    760            dest.skewnessImpl = source.skewnessImpl;
    761            dest.percentileImpl = source.percentileImpl;
    762        }
    763    }