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