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