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