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 java.io.Serializable;
20
21 import org.apache.commons.math.MathRuntimeException;
22 import org.apache.commons.math.stat.descriptive.AbstractStorelessUnivariateStatistic;
23
24
25 /**
26 * Computes the Kurtosis of the available values.
27 * <p>
28 * We use the following (unbiased) formula to define kurtosis:</p>
29 * <p>
30 * 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)]
31 * </p><p>
32 * where n is the number of values, mean is the {@link Mean} and std is the
33 * {@link StandardDeviation}</p>
34 * <p>
35 * Note that this statistic is undefined for n < 4. <code>Double.Nan</code>
36 * is returned when there is not sufficient data to compute the statistic.</p>
37 * <p>
38 * <strong>Note that this implementation is not synchronized.</strong> If
39 * multiple threads access an instance of this class concurrently, and at least
40 * one of the threads invokes the <code>increment()</code> or
41 * <code>clear()</code> method, it must be synchronized externally.</p>
42 *
43 * @version $Revision: 780541 $ $Date: 2009-05-31 20:47:02 -0400 (Sun, 31 May 2009) $
44 */
45 public class Kurtosis extends AbstractStorelessUnivariateStatistic implements Serializable {
46
47 /** Serializable version identifier */
48 private static final long serialVersionUID = 2784465764798260919L;
49
50 /**Fourth Moment on which this statistic is based */
51 protected FourthMoment moment;
52
53 /**
54 * Determines whether or not this statistic can be incremented or cleared.
55 * <p>
56 * Statistics based on (constructed from) external moments cannot
57 * be incremented or cleared.</p>
58 */
59 protected boolean incMoment;
60
61 /**
62 * Construct a Kurtosis
63 */
64 public Kurtosis() {
65 incMoment = true;
66 moment = new FourthMoment();
67 }
68
69 /**
70 * Construct a Kurtosis from an external moment
71 *
72 * @param m4 external Moment
73 */
74 public Kurtosis(final FourthMoment m4) {
75 incMoment = false;
76 this.moment = m4;
77 }
78
79 /**
80 * Copy constructor, creates a new {@code Kurtosis} identical
81 * to the {@code original}
82 *
83 * @param original the {@code Kurtosis} instance to copy
84 */
85 public Kurtosis(Kurtosis original) {
86 copy(original, this);
87 }
88
89 /**
90 * {@inheritDoc}
91 */
92 @Override
93 public void increment(final double d) {
94 if (incMoment) {
95 moment.increment(d);
96 } else {
97 throw MathRuntimeException.createIllegalStateException(
98 "statistics constructed from external moments cannot be incremented");
99 }
100 }
101
102 /**
103 * {@inheritDoc}
104 */
105 @Override
106 public double getResult() {
107 double kurtosis = Double.NaN;
108 if (moment.getN() > 3) {
109 double variance = moment.m2 / (moment.n - 1);
110 if (moment.n <= 3 || variance < 10E-20) {
111 kurtosis = 0.0;
112 } else {
113 double n = moment.n;
114 kurtosis =
115 (n * (n + 1) * moment.m4 -
116 3 * moment.m2 * moment.m2 * (n - 1)) /
117 ((n - 1) * (n -2) * (n -3) * variance * variance);
118 }
119 }
120 return kurtosis;
121 }
122
123 /**
124 * {@inheritDoc}
125 */
126 @Override
127 public void clear() {
128 if (incMoment) {
129 moment.clear();
130 } else {
131 throw MathRuntimeException.createIllegalStateException(
132 "statistics constructed from external moments cannot be cleared");
133 }
134 }
135
136 /**
137 * {@inheritDoc}
138 */
139 public long getN() {
140 return moment.getN();
141 }
142
143 /* UnvariateStatistic Approach */
144
145 /**
146 * Returns the kurtosis of the entries in the specified portion of the
147 * input array.
148 * <p>
149 * See {@link Kurtosis} for details on the computing algorithm.</p>
150 * <p>
151 * Throws <code>IllegalArgumentException</code> if the array is null.</p>
152 *
153 * @param values the input array
154 * @param begin index of the first array element to include
155 * @param length the number of elements to include
156 * @return the kurtosis of the values or Double.NaN if length is less than
157 * 4
158 * @throws IllegalArgumentException if the input array is null or the array
159 * index parameters are not valid
160 */
161 @Override
162 public double evaluate(final double[] values,final int begin, final int length) {
163 // Initialize the kurtosis
164 double kurt = Double.NaN;
165
166 if (test(values, begin, length) && length > 3) {
167
168 // Compute the mean and standard deviation
169 Variance variance = new Variance();
170 variance.incrementAll(values, begin, length);
171 double mean = variance.moment.m1;
172 double stdDev = Math.sqrt(variance.getResult());
173
174 // Sum the ^4 of the distance from the mean divided by the
175 // standard deviation
176 double accum3 = 0.0;
177 for (int i = begin; i < begin + length; i++) {
178 accum3 += Math.pow((values[i] - mean), 4.0);
179 }
180 accum3 /= Math.pow(stdDev, 4.0d);
181
182 // Get N
183 double n0 = length;
184
185 double coefficientOne =
186 (n0 * (n0 + 1)) / ((n0 - 1) * (n0 - 2) * (n0 - 3));
187 double termTwo =
188 ((3 * Math.pow(n0 - 1, 2.0)) / ((n0 - 2) * (n0 - 3)));
189
190 // Calculate kurtosis
191 kurt = (coefficientOne * accum3) - termTwo;
192 }
193 return kurt;
194 }
195
196 /**
197 * {@inheritDoc}
198 */
199 @Override
200 public Kurtosis copy() {
201 Kurtosis result = new Kurtosis();
202 copy(this, result);
203 return result;
204 }
205
206 /**
207 * Copies source to dest.
208 * <p>Neither source nor dest can be null.</p>
209 *
210 * @param source Kurtosis to copy
211 * @param dest Kurtosis to copy to
212 * @throws NullPointerException if either source or dest is null
213 */
214 public static void copy(Kurtosis source, Kurtosis dest) {
215 dest.moment = source.moment.copy();
216 dest.incMoment = source.incMoment;
217 }
218
219 }