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 */
17package org.apache.commons.math.stat.descriptive.moment;
18
19import java.io.Serializable;
20
21import org.apache.commons.math.stat.descriptive.AbstractStorelessUnivariateStatistic;
22import org.apache.commons.math.util.FastMath;
23
24/**
25 * Computes the skewness of the available values.
26 * <p>
27 * We use the following (unbiased) formula to define skewness:</p>
28 * <p>
29 * skewness = [n / (n -1) (n - 2)] sum[(x_i - mean)^3] / std^3 </p>
30 * <p>
31 * where n is the number of values, mean is the {@link Mean} and std is the
32 * {@link StandardDeviation} </p>
33 * <p>
34 * <strong>Note that this implementation is not synchronized.</strong> If
35 * multiple threads access an instance of this class concurrently, and at least
36 * one of the threads invokes the <code>increment()</code> or
37 * <code>clear()</code> method, it must be synchronized externally. </p>
38 *
39 * @version $Revision: 1006299 $ $Date: 2010-10-10 16:47:17 +0200 (dim. 10 oct. 2010) $
40 */
41public class Skewness extends AbstractStorelessUnivariateStatistic implements Serializable {
42
43    /** Serializable version identifier */
44    private static final long serialVersionUID = 7101857578996691352L;
45
46    /** Third moment on which this statistic is based */
47    protected ThirdMoment moment = null;
48
49     /**
50     * Determines whether or not this statistic can be incremented or cleared.
51     * <p>
52     * Statistics based on (constructed from) external moments cannot
53     * be incremented or cleared.</p>
54    */
55    protected boolean incMoment;
56
57    /**
58     * Constructs a Skewness
59     */
60    public Skewness() {
61        incMoment = true;
62        moment = new ThirdMoment();
63    }
64
65    /**
66     * Constructs a Skewness with an external moment
67     * @param m3 external moment
68     */
69    public Skewness(final ThirdMoment m3) {
70        incMoment = false;
71        this.moment = m3;
72    }
73
74    /**
75     * Copy constructor, creates a new {@code Skewness} identical
76     * to the {@code original}
77     *
78     * @param original the {@code Skewness} instance to copy
79     */
80    public Skewness(Skewness original) {
81        copy(original, this);
82    }
83
84    /**
85     * {@inheritDoc}
86     */
87    @Override
88    public void increment(final double d) {
89        if (incMoment) {
90            moment.increment(d);
91        }
92    }
93
94    /**
95     * Returns the value of the statistic based on the values that have been added.
96     * <p>
97     * See {@link Skewness} for the definition used in the computation.</p>
98     *
99     * @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}
214