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      * @return The geometricMean, Double.NaN if no values have been added,
213      * or if the product of the available values is less than or equal to 0.
214      */
215     public double getGeometricMean() {
216         return apply(geometricMeanImpl);
217     }
218 
219     /**
220      * Returns the (sample) variance of the available values.
221      *
222      * <p>This method returns the bias-corrected sample variance (using {@code n - 1} in
223      * the denominator).  Use {@link #getPopulationVariance()} for the non-bias-corrected
224      * population variance.</p>
225      *
226      * @return The variance, Double.NaN if no values have been added
227      * or 0.0 for a single value set.
228      */
229     public double getVariance() {
230         return apply(varianceImpl);
231     }
232 
233     /**
234      * Returns the <a href="http://en.wikibooks.org/wiki/Statistics/Summary/Variance">
235      * population variance</a> of the available values.
236      *
237      * @return The population variance, Double.NaN if no values have been added,
238      * or 0.0 for a single value set.
239      */
240     public double getPopulationVariance() {
241         return apply(new Variance(false));
242     }
243 
244     /**
245      * Returns the standard deviation of the available values.
246      * @return The standard deviation, Double.NaN if no values have been added
247      * or 0.0 for a single value set.
248      */
249     public double getStandardDeviation() {
250         double stdDev = Double.NaN;
251         if (getN() > 0) {
252             if (getN() > 1) {
253                 stdDev = FastMath.sqrt(getVariance());
254             } else {
255                 stdDev = 0.0;
256             }
257         }
258         return stdDev;
259     }
260 
261     /**
262      * Returns the quadratic mean, a.k.a.
263      * <a href="http://mathworld.wolfram.com/Root-Mean-Square.html">
264      * root-mean-square</a> of the available values
265      * @return The quadratic mean or {@code Double.NaN} if no values
266      * have been added.
267      */
268     public double getQuadraticMean() {
269         final long n = getN();
270         return n > 0 ? FastMath.sqrt(getSumsq() / n) : Double.NaN;
271     }
272 
273     /**
274      * Returns the skewness of the available values. Skewness is a
275      * measure of the asymmetry of a given distribution.
276      * @return The skewness, Double.NaN if no values have been added
277      * or 0.0 for a value set &lt;=2.
278      */
279     public double getSkewness() {
280         return apply(skewnessImpl);
281     }
282 
283     /**
284      * Returns the Kurtosis of the available values. Kurtosis is a
285      * measure of the "peakedness" of a distribution
286      * @return The kurtosis, Double.NaN if no values have been added, or 0.0
287      * for a value set &lt;=3.
288      */
289     public double getKurtosis() {
290         return apply(kurtosisImpl);
291     }
292 
293     /**
294      * Returns the maximum of the available values
295      * @return The max or Double.NaN if no values have been added.
296      */
297     public double getMax() {
298         return apply(maxImpl);
299     }
300 
301     /**
302     * Returns the minimum of the available values
303     * @return The min or Double.NaN if no values have been added.
304     */
305     public double getMin() {
306         return apply(minImpl);
307     }
308 
309     /**
310      * Returns the number of available values
311      * @return The number of available values
312      */
313     public long getN() {
314         return eDA.getNumElements();
315     }
316 
317     /**
318      * Returns the sum of the values that have been added to Univariate.
319      * @return The sum or Double.NaN if no values have been added
320      */
321     public double getSum() {
322         return apply(sumImpl);
323     }
324 
325     /**
326      * Returns the sum of the squares of the available values.
327      * @return The sum of the squares or Double.NaN if no
328      * values have been added.
329      */
330     public double getSumsq() {
331         return apply(sumsqImpl);
332     }
333 
334     /**
335      * Resets all statistics and storage
336      */
337     public void clear() {
338         eDA.clear();
339     }
340 
341 
342     /**
343      * Returns the maximum number of values that can be stored in the
344      * dataset, or INFINITE_WINDOW (-1) if there is no limit.
345      *
346      * @return The current window size or -1 if its Infinite.
347      */
348     public int getWindowSize() {
349         return windowSize;
350     }
351 
352     /**
353      * WindowSize controls the number of values that contribute to the
354      * reported statistics.  For example, if windowSize is set to 3 and the
355      * values {1,2,3,4,5} have been added <strong> in that order</strong> then
356      * the <i>available values</i> are {3,4,5} and all reported statistics will
357      * be based on these values. If {@code windowSize} is decreased as a result
358      * of this call and there are more than the new value of elements in the
359      * current dataset, values from the front of the array are discarded to
360      * reduce the dataset to {@code windowSize} elements.
361      *
362      * @param windowSize sets the size of the window.
363      * @throws MathIllegalArgumentException if window size is less than 1 but
364      * not equal to {@link #INFINITE_WINDOW}
365      */
366     public void setWindowSize(int windowSize) throws MathIllegalArgumentException {
367         if (windowSize < 1 && windowSize != INFINITE_WINDOW) {
368             throw new MathIllegalArgumentException(
369                     LocalizedFormats.NOT_POSITIVE_WINDOW_SIZE, windowSize);
370         }
371 
372         this.windowSize = windowSize;
373 
374         // We need to check to see if we need to discard elements
375         // from the front of the array.  If the windowSize is less than
376         // the current number of elements.
377         if (windowSize != INFINITE_WINDOW && windowSize < eDA.getNumElements()) {
378             eDA.discardFrontElements(eDA.getNumElements() - windowSize);
379         }
380     }
381 
382     /**
383      * Returns the current set of values in an array of double primitives.
384      * The order of addition is preserved.  The returned array is a fresh
385      * copy of the underlying data -- i.e., it is not a reference to the
386      * stored data.
387      *
388      * @return returns the current set of numbers in the order in which they
389      *         were added to this set
390      */
391     public double[] getValues() {
392         return eDA.getElements();
393     }
394 
395     /**
396      * Returns the current set of values in an array of double primitives,
397      * sorted in ascending order.  The returned array is a fresh
398      * copy of the underlying data -- i.e., it is not a reference to the
399      * stored data.
400      * @return returns the current set of
401      * numbers sorted in ascending order
402      */
403     public double[] getSortedValues() {
404         double[] sort = getValues();
405         Arrays.sort(sort);
406         return sort;
407     }
408 
409     /**
410      * Returns the element at the specified index
411      * @param index The Index of the element
412      * @return return the element at the specified index
413      */
414     public double getElement(int index) {
415         return eDA.getElement(index);
416     }
417 
418     /**
419      * Returns an estimate for the pth percentile of the stored values.
420      * <p>
421      * The implementation provided here follows the first estimation procedure presented
422      * <a href="http://www.itl.nist.gov/div898/handbook/prc/section2/prc252.htm">here.</a>
423      * </p><p>
424      * <strong>Preconditions</strong>:<ul>
425      * <li><code>0 &lt; p &le; 100</code> (otherwise an
426      * <code>MathIllegalArgumentException</code> is thrown)</li>
427      * <li>at least one value must be stored (returns <code>Double.NaN
428      *     </code> otherwise)</li>
429      * </ul></p>
430      *
431      * @param p the requested percentile (scaled from 0 - 100)
432      * @return An estimate for the pth percentile of the stored data
433      * @throws MathIllegalStateException if percentile implementation has been
434      *  overridden and the supplied implementation does not support setQuantile
435      * @throws MathIllegalArgumentException if p is not a valid quantile
436      */
437     public double getPercentile(double p) throws MathIllegalStateException, MathIllegalArgumentException {
438         if (percentileImpl instanceof Percentile) {
439             ((Percentile) percentileImpl).setQuantile(p);
440         } else {
441             try {
442                 percentileImpl.getClass().getMethod(SET_QUANTILE_METHOD_NAME,
443                         new Class[] {Double.TYPE}).invoke(percentileImpl,
444                                 new Object[] {Double.valueOf(p)});
445             } catch (NoSuchMethodException e1) { // Setter guard should prevent
446                 throw new MathIllegalStateException(
447                       LocalizedFormats.PERCENTILE_IMPLEMENTATION_UNSUPPORTED_METHOD,
448                       percentileImpl.getClass().getName(), SET_QUANTILE_METHOD_NAME);
449             } catch (IllegalAccessException e2) {
450                 throw new MathIllegalStateException(
451                       LocalizedFormats.PERCENTILE_IMPLEMENTATION_CANNOT_ACCESS_METHOD,
452                       SET_QUANTILE_METHOD_NAME, percentileImpl.getClass().getName());
453             } catch (InvocationTargetException e3) {
454                 throw new IllegalStateException(e3.getCause());
455             }
456         }
457         return apply(percentileImpl);
458     }
459 
460     /**
461      * Generates a text report displaying univariate statistics from values
462      * that have been added.  Each statistic is displayed on a separate
463      * line.
464      *
465      * @return String with line feeds displaying statistics
466      */
467     @Override
468     public String toString() {
469         StringBuilder outBuffer = new StringBuilder();
470         String endl = "\n";
471         outBuffer.append("DescriptiveStatistics:").append(endl);
472         outBuffer.append("n: ").append(getN()).append(endl);
473         outBuffer.append("min: ").append(getMin()).append(endl);
474         outBuffer.append("max: ").append(getMax()).append(endl);
475         outBuffer.append("mean: ").append(getMean()).append(endl);
476         outBuffer.append("std dev: ").append(getStandardDeviation())
477             .append(endl);
478         try {
479             // No catch for MIAE because actual parameter is valid below
480             outBuffer.append("median: ").append(getPercentile(50)).append(endl);
481         } catch (MathIllegalStateException ex) {
482             outBuffer.append("median: unavailable").append(endl);
483         }
484         outBuffer.append("skewness: ").append(getSkewness()).append(endl);
485         outBuffer.append("kurtosis: ").append(getKurtosis()).append(endl);
486         return outBuffer.toString();
487     }
488 
489     /**
490      * Apply the given statistic to the data associated with this set of statistics.
491      * @param stat the statistic to apply
492      * @return the computed value of the statistic.
493      */
494     public double apply(UnivariateStatistic stat) {
495         // No try-catch or advertised exception here because arguments are guaranteed valid
496         return eDA.compute(stat);
497     }
498 
499     // Implementation getters and setter
500 
501     /**
502      * Returns the currently configured mean implementation.
503      *
504      * @return the UnivariateStatistic implementing the mean
505      * @since 1.2
506      */
507     public synchronized UnivariateStatistic getMeanImpl() {
508         return meanImpl;
509     }
510 
511     /**
512      * <p>Sets the implementation for the mean.</p>
513      *
514      * @param meanImpl the UnivariateStatistic instance to use
515      * for computing the mean
516      * @since 1.2
517      */
518     public synchronized void setMeanImpl(UnivariateStatistic meanImpl) {
519         this.meanImpl = meanImpl;
520     }
521 
522     /**
523      * Returns the currently configured geometric mean implementation.
524      *
525      * @return the UnivariateStatistic implementing the geometric mean
526      * @since 1.2
527      */
528     public synchronized UnivariateStatistic getGeometricMeanImpl() {
529         return geometricMeanImpl;
530     }
531 
532     /**
533      * <p>Sets the implementation for the gemoetric mean.</p>
534      *
535      * @param geometricMeanImpl the UnivariateStatistic instance to use
536      * for computing the geometric mean
537      * @since 1.2
538      */
539     public synchronized void setGeometricMeanImpl(
540             UnivariateStatistic geometricMeanImpl) {
541         this.geometricMeanImpl = geometricMeanImpl;
542     }
543 
544     /**
545      * Returns the currently configured kurtosis implementation.
546      *
547      * @return the UnivariateStatistic implementing the kurtosis
548      * @since 1.2
549      */
550     public synchronized UnivariateStatistic getKurtosisImpl() {
551         return kurtosisImpl;
552     }
553 
554     /**
555      * <p>Sets the implementation for the kurtosis.</p>
556      *
557      * @param kurtosisImpl the UnivariateStatistic instance to use
558      * for computing the kurtosis
559      * @since 1.2
560      */
561     public synchronized void setKurtosisImpl(UnivariateStatistic kurtosisImpl) {
562         this.kurtosisImpl = kurtosisImpl;
563     }
564 
565     /**
566      * Returns the currently configured maximum implementation.
567      *
568      * @return the UnivariateStatistic implementing the maximum
569      * @since 1.2
570      */
571     public synchronized UnivariateStatistic getMaxImpl() {
572         return maxImpl;
573     }
574 
575     /**
576      * <p>Sets the implementation for the maximum.</p>
577      *
578      * @param maxImpl the UnivariateStatistic instance to use
579      * for computing the maximum
580      * @since 1.2
581      */
582     public synchronized void setMaxImpl(UnivariateStatistic maxImpl) {
583         this.maxImpl = maxImpl;
584     }
585 
586     /**
587      * Returns the currently configured minimum implementation.
588      *
589      * @return the UnivariateStatistic implementing the minimum
590      * @since 1.2
591      */
592     public synchronized UnivariateStatistic getMinImpl() {
593         return minImpl;
594     }
595 
596     /**
597      * <p>Sets the implementation for the minimum.</p>
598      *
599      * @param minImpl the UnivariateStatistic instance to use
600      * for computing the minimum
601      * @since 1.2
602      */
603     public synchronized void setMinImpl(UnivariateStatistic minImpl) {
604         this.minImpl = minImpl;
605     }
606 
607     /**
608      * Returns the currently configured percentile implementation.
609      *
610      * @return the UnivariateStatistic implementing the percentile
611      * @since 1.2
612      */
613     public synchronized UnivariateStatistic getPercentileImpl() {
614         return percentileImpl;
615     }
616 
617     /**
618      * Sets the implementation to be used by {@link #getPercentile(double)}.
619      * The supplied <code>UnivariateStatistic</code> must provide a
620      * <code>setQuantile(double)</code> method; otherwise
621      * <code>IllegalArgumentException</code> is thrown.
622      *
623      * @param percentileImpl the percentileImpl to set
624      * @throws MathIllegalArgumentException if the supplied implementation does not
625      *  provide a <code>setQuantile</code> method
626      * @since 1.2
627      */
628     public synchronized void setPercentileImpl(UnivariateStatistic percentileImpl)
629     throws MathIllegalArgumentException {
630         try {
631             percentileImpl.getClass().getMethod(SET_QUANTILE_METHOD_NAME,
632                     new Class[] {Double.TYPE}).invoke(percentileImpl,
633                             new Object[] {Double.valueOf(50.0d)});
634         } catch (NoSuchMethodException e1) {
635             throw new MathIllegalArgumentException(
636                   LocalizedFormats.PERCENTILE_IMPLEMENTATION_UNSUPPORTED_METHOD,
637                   percentileImpl.getClass().getName(), SET_QUANTILE_METHOD_NAME);
638         } catch (IllegalAccessException e2) {
639             throw new MathIllegalArgumentException(
640                   LocalizedFormats.PERCENTILE_IMPLEMENTATION_CANNOT_ACCESS_METHOD,
641                   SET_QUANTILE_METHOD_NAME, percentileImpl.getClass().getName());
642         } catch (InvocationTargetException e3) {
643             throw new IllegalArgumentException(e3.getCause());
644         }
645         this.percentileImpl = percentileImpl;
646     }
647 
648     /**
649      * Returns the currently configured skewness implementation.
650      *
651      * @return the UnivariateStatistic implementing the skewness
652      * @since 1.2
653      */
654     public synchronized UnivariateStatistic getSkewnessImpl() {
655         return skewnessImpl;
656     }
657 
658     /**
659      * <p>Sets the implementation for the skewness.</p>
660      *
661      * @param skewnessImpl the UnivariateStatistic instance to use
662      * for computing the skewness
663      * @since 1.2
664      */
665     public synchronized void setSkewnessImpl(
666             UnivariateStatistic skewnessImpl) {
667         this.skewnessImpl = skewnessImpl;
668     }
669 
670     /**
671      * Returns the currently configured variance implementation.
672      *
673      * @return the UnivariateStatistic implementing the variance
674      * @since 1.2
675      */
676     public synchronized UnivariateStatistic getVarianceImpl() {
677         return varianceImpl;
678     }
679 
680     /**
681      * <p>Sets the implementation for the variance.</p>
682      *
683      * @param varianceImpl the UnivariateStatistic instance to use
684      * for computing the variance
685      * @since 1.2
686      */
687     public synchronized void setVarianceImpl(
688             UnivariateStatistic varianceImpl) {
689         this.varianceImpl = varianceImpl;
690     }
691 
692     /**
693      * Returns the currently configured sum of squares implementation.
694      *
695      * @return the UnivariateStatistic implementing the sum of squares
696      * @since 1.2
697      */
698     public synchronized UnivariateStatistic getSumsqImpl() {
699         return sumsqImpl;
700     }
701 
702     /**
703      * <p>Sets the implementation for the sum of squares.</p>
704      *
705      * @param sumsqImpl the UnivariateStatistic instance to use
706      * for computing the sum of squares
707      * @since 1.2
708      */
709     public synchronized void setSumsqImpl(UnivariateStatistic sumsqImpl) {
710         this.sumsqImpl = sumsqImpl;
711     }
712 
713     /**
714      * Returns the currently configured sum implementation.
715      *
716      * @return the UnivariateStatistic implementing the sum
717      * @since 1.2
718      */
719     public synchronized UnivariateStatistic getSumImpl() {
720         return sumImpl;
721     }
722 
723     /**
724      * <p>Sets the implementation for the sum.</p>
725      *
726      * @param sumImpl the UnivariateStatistic instance to use
727      * for computing the sum
728      * @since 1.2
729      */
730     public synchronized void setSumImpl(UnivariateStatistic sumImpl) {
731         this.sumImpl = sumImpl;
732     }
733 
734     /**
735      * Returns a copy of this DescriptiveStatistics instance with the same internal state.
736      *
737      * @return a copy of this
738      */
739     public DescriptiveStatistics copy() {
740         DescriptiveStatistics result = new DescriptiveStatistics();
741         // No try-catch or advertised exception because parms are guaranteed valid
742         copy(this, result);
743         return result;
744     }
745 
746     /**
747      * Copies source to dest.
748      * <p>Neither source nor dest can be null.</p>
749      *
750      * @param source DescriptiveStatistics to copy
751      * @param dest DescriptiveStatistics to copy to
752      * @throws NullArgumentException if either source or dest is null
753      */
754     public static void copy(DescriptiveStatistics source, DescriptiveStatistics dest)
755         throws NullArgumentException {
756         MathUtils.checkNotNull(source);
757         MathUtils.checkNotNull(dest);
758         // Copy data and window size
759         dest.eDA = source.eDA.copy();
760         dest.windowSize = source.windowSize;
761 
762         // Copy implementations
763         dest.maxImpl = source.maxImpl.copy();
764         dest.meanImpl = source.meanImpl.copy();
765         dest.minImpl = source.minImpl.copy();
766         dest.sumImpl = source.sumImpl.copy();
767         dest.varianceImpl = source.varianceImpl.copy();
768         dest.sumsqImpl = source.sumsqImpl.copy();
769         dest.geometricMeanImpl = source.geometricMeanImpl.copy();
770         dest.kurtosisImpl = source.kurtosisImpl;
771         dest.skewnessImpl = source.skewnessImpl;
772         dest.percentileImpl = source.percentileImpl;
773     }
774 }