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.moment;
018    
019    import java.io.Serializable;
020    
021    import org.apache.commons.math.stat.descriptive.AbstractStorelessUnivariateStatistic;
022    import org.apache.commons.math.util.FastMath;
023    
024    /**
025     * Computes the skewness of the available values.
026     * <p>
027     * We use the following (unbiased) formula to define skewness:</p>
028     * <p>
029     * skewness = [n / (n -1) (n - 2)] sum[(x_i - mean)^3] / std^3 </p>
030     * <p>
031     * where n is the number of values, mean is the {@link Mean} and std is the
032     * {@link StandardDeviation} </p>
033     * <p>
034     * <strong>Note that this implementation is not synchronized.</strong> If
035     * multiple threads access an instance of this class concurrently, and at least
036     * one of the threads invokes the <code>increment()</code> or
037     * <code>clear()</code> method, it must be synchronized externally. </p>
038     *
039     * @version $Revision: 1006299 $ $Date: 2010-10-10 16:47:17 +0200 (dim. 10 oct. 2010) $
040     */
041    public class Skewness extends AbstractStorelessUnivariateStatistic implements Serializable {
042    
043        /** Serializable version identifier */
044        private static final long serialVersionUID = 7101857578996691352L;
045    
046        /** Third moment on which this statistic is based */
047        protected ThirdMoment moment = null;
048    
049         /**
050         * Determines whether or not this statistic can be incremented or cleared.
051         * <p>
052         * Statistics based on (constructed from) external moments cannot
053         * be incremented or cleared.</p>
054        */
055        protected boolean incMoment;
056    
057        /**
058         * Constructs a Skewness
059         */
060        public Skewness() {
061            incMoment = true;
062            moment = new ThirdMoment();
063        }
064    
065        /**
066         * Constructs a Skewness with an external moment
067         * @param m3 external moment
068         */
069        public Skewness(final ThirdMoment m3) {
070            incMoment = false;
071            this.moment = m3;
072        }
073    
074        /**
075         * Copy constructor, creates a new {@code Skewness} identical
076         * to the {@code original}
077         *
078         * @param original the {@code Skewness} instance to copy
079         */
080        public Skewness(Skewness original) {
081            copy(original, this);
082        }
083    
084        /**
085         * {@inheritDoc}
086         */
087        @Override
088        public void increment(final double d) {
089            if (incMoment) {
090                moment.increment(d);
091            }
092        }
093    
094        /**
095         * Returns the value of the statistic based on the values that have been added.
096         * <p>
097         * See {@link Skewness} for the definition used in the computation.</p>
098         *
099         * @return the skewness of the available values.
100         */
101        @Override
102        public double getResult() {
103    
104            if (moment.n < 3) {
105                return Double.NaN;
106            }
107            double variance = moment.m2 / (moment.n - 1);
108            if (variance < 10E-20) {
109                return 0.0d;
110            } else {
111                double n0 = moment.getN();
112                return  (n0 * moment.m3) /
113                ((n0 - 1) * (n0 -2) * FastMath.sqrt(variance) * variance);
114            }
115        }
116    
117        /**
118         * {@inheritDoc}
119         */
120        public long getN() {
121            return moment.getN();
122        }
123    
124        /**
125         * {@inheritDoc}
126         */
127        @Override
128        public void clear() {
129            if (incMoment) {
130                moment.clear();
131            }
132        }
133    
134        /**
135         * Returns the Skewness of the entries in the specifed portion of the
136         * input array.
137         * <p>
138         * See {@link Skewness} for the definition used in the computation.</p>
139         * <p>
140         * Throws <code>IllegalArgumentException</code> if the array is null.</p>
141         *
142         * @param values the input array
143         * @param begin the index of the first array element to include
144         * @param length the number of elements to include
145         * @return the skewness of the values or Double.NaN if length is less than
146         * 3
147         * @throws IllegalArgumentException if the array is null or the array index
148         *  parameters are not valid
149         */
150        @Override
151        public double evaluate(final double[] values,final int begin,
152                final int length) {
153    
154            // Initialize the skewness
155            double skew = Double.NaN;
156    
157            if (test(values, begin, length) && length > 2 ){
158                Mean mean = new Mean();
159                // Get the mean and the standard deviation
160                double m = mean.evaluate(values, begin, length);
161    
162                // Calc the std, this is implemented here instead
163                // of using the standardDeviation method eliminate
164                // a duplicate pass to get the mean
165                double accum = 0.0;
166                double accum2 = 0.0;
167                for (int i = begin; i < begin + length; i++) {
168                    final double d = values[i] - m;
169                    accum  += d * d;
170                    accum2 += d;
171                }
172                final double variance = (accum - (accum2 * accum2 / length)) / (length - 1);
173    
174                double accum3 = 0.0;
175                for (int i = begin; i < begin + length; i++) {
176                    final double d = values[i] - m;
177                    accum3 += d * d * d;
178                }
179                accum3 /= variance * FastMath.sqrt(variance);
180    
181                // Get N
182                double n0 = length;
183    
184                // Calculate skewness
185                skew = (n0 / ((n0 - 1) * (n0 - 2))) * accum3;
186            }
187            return skew;
188        }
189    
190        /**
191         * {@inheritDoc}
192         */
193        @Override
194        public Skewness copy() {
195            Skewness result = new Skewness();
196            copy(this, result);
197            return result;
198        }
199    
200        /**
201         * Copies source to dest.
202         * <p>Neither source nor dest can be null.</p>
203         *
204         * @param source Skewness to copy
205         * @param dest Skewness to copy to
206         * @throws NullPointerException if either source or dest is null
207         */
208        public static void copy(Skewness source, Skewness dest) {
209            dest.setData(source.getDataRef());
210            dest.moment = new ThirdMoment(source.moment.copy());
211            dest.incMoment = source.incMoment;
212        }
213    }