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 */
17 package org.apache.commons.math.stat.descriptive.moment;
18
19 import org.apache.commons.math.stat.descriptive.AbstractStorelessUnivariateStatistic;
20
21 /**
22 * Computes the Kurtosis of the available values.
23 * <p>
24 * We use the following (unbiased) formula to define kurtosis:</p>
25 * <p>
26 * kurtosis = { [n(n+1) / (n -1)(n - 2)(n-3)] sum[(x_i - mean)^4] / std^4 } - [3(n-1)^2 / (n-2)(n-3)]
27 * </p><p>
28 * where n is the number of values, mean is the {@link Mean} and std is the
29 * {@link StandardDeviation}</p>
30 * <p>
31 * Note that this statistic is undefined for n < 4. <code>Double.Nan</code>
32 * is returned when there is not sufficient data to compute the statistic.</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: 617953 $ $Date: 2008-02-02 22:54:00 -0700 (Sat, 02 Feb 2008) $
40 */
41 public class Kurtosis extends AbstractStorelessUnivariateStatistic {
42
43 /** Serializable version identifier */
44 private static final long serialVersionUID = 2784465764798260919L;
45
46 /**Fourth Moment on which this statistic is based */
47 protected FourthMoment moment;
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 * Construct a Kurtosis
59 */
60 public Kurtosis() {
61 incMoment = true;
62 moment = new FourthMoment();
63 }
64
65 /**
66 * Construct a Kurtosis from an external moment
67 *
68 * @param m4 external Moment
69 */
70 public Kurtosis(final FourthMoment m4) {
71 incMoment = false;
72 this.moment = m4;
73 }
74
75 /**
76 * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#increment(double)
77 */
78 public void increment(final double d) {
79 if (incMoment) {
80 moment.increment(d);
81 } else {
82 throw new IllegalStateException
83 ("Statistics constructed from external moments cannot be incremented");
84 }
85 }
86
87 /**
88 * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#getResult()
89 */
90 public double getResult() {
91 double kurtosis = Double.NaN;
92 if (moment.getN() > 3) {
93 double variance = moment.m2 / (double) (moment.n - 1);
94 if (moment.n <= 3 || variance < 10E-20) {
95 kurtosis = 0.0;
96 } else {
97 double n = (double) moment.n;
98 kurtosis =
99 (n * (n + 1) * moment.m4 -
100 3 * moment.m2 * moment.m2 * (n - 1)) /
101 ((n - 1) * (n -2) * (n -3) * variance * variance);
102 }
103 }
104 return kurtosis;
105 }
106
107 /**
108 * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#clear()
109 */
110 public void clear() {
111 if (incMoment) {
112 moment.clear();
113 } else {
114 throw new IllegalStateException
115 ("Statistics constructed from external moments cannot be cleared");
116 }
117 }
118
119 /**
120 * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#getN()
121 */
122 public long getN() {
123 return moment.getN();
124 }
125
126 /* UnvariateStatistic Approach */
127
128 /**
129 * Returns the kurtosis of the entries in the specified portion of the
130 * input array.
131 * <p>
132 * See {@link Kurtosis} for details on the computing algorithm.</p>
133 * <p>
134 * Throws <code>IllegalArgumentException</code> if the array is null.</p>
135 *
136 * @param values the input array
137 * @param begin index of the first array element to include
138 * @param length the number of elements to include
139 * @return the kurtosis of the values or Double.NaN if length is less than
140 * 4
141 * @throws IllegalArgumentException if the input array is null or the array
142 * index parameters are not valid
143 */
144 public double evaluate(final double[] values,final int begin, final int length) {
145 // Initialize the kurtosis
146 double kurt = Double.NaN;
147
148 if (test(values, begin, length) && length > 3) {
149
150 // Compute the mean and standard deviation
151 Variance variance = new Variance();
152 variance.incrementAll(values, begin, length);
153 double mean = variance.moment.m1;
154 double stdDev = Math.sqrt(variance.getResult());
155
156 // Sum the ^4 of the distance from the mean divided by the
157 // standard deviation
158 double accum3 = 0.0;
159 for (int i = begin; i < begin + length; i++) {
160 accum3 += Math.pow((values[i] - mean), 4.0);
161 }
162 accum3 /= Math.pow(stdDev, 4.0d);
163
164 // Get N
165 double n0 = length;
166
167 double coefficientOne =
168 (n0 * (n0 + 1)) / ((n0 - 1) * (n0 - 2) * (n0 - 3));
169 double termTwo =
170 ((3 * Math.pow(n0 - 1, 2.0)) / ((n0 - 2) * (n0 - 3)));
171
172 // Calculate kurtosis
173 kurt = (coefficientOne * accum3) - termTwo;
174 }
175 return kurt;
176 }
177
178 }