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