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