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