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.MathRuntimeException;
022 import org.apache.commons.math.stat.descriptive.AbstractStorelessUnivariateStatistic;
023
024
025 /**
026 * Computes the Kurtosis of the available values.
027 * <p>
028 * We use the following (unbiased) formula to define kurtosis:</p>
029 * <p>
030 * 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)]
031 * </p><p>
032 * where n is the number of values, mean is the {@link Mean} and std is the
033 * {@link StandardDeviation}</p>
034 * <p>
035 * Note that this statistic is undefined for n < 4. <code>Double.Nan</code>
036 * is returned when there is not sufficient data to compute the statistic.</p>
037 * <p>
038 * <strong>Note that this implementation is not synchronized.</strong> If
039 * multiple threads access an instance of this class concurrently, and at least
040 * one of the threads invokes the <code>increment()</code> or
041 * <code>clear()</code> method, it must be synchronized externally.</p>
042 *
043 * @version $Revision: 811833 $ $Date: 2009-09-06 12:27:50 -0400 (Sun, 06 Sep 2009) $
044 */
045 public class Kurtosis extends AbstractStorelessUnivariateStatistic implements Serializable {
046
047 /** Serializable version identifier */
048 private static final long serialVersionUID = 2784465764798260919L;
049
050 /**Fourth Moment on which this statistic is based */
051 protected FourthMoment moment;
052
053 /**
054 * Determines whether or not this statistic can be incremented or cleared.
055 * <p>
056 * Statistics based on (constructed from) external moments cannot
057 * be incremented or cleared.</p>
058 */
059 protected boolean incMoment;
060
061 /**
062 * Construct a Kurtosis
063 */
064 public Kurtosis() {
065 incMoment = true;
066 moment = new FourthMoment();
067 }
068
069 /**
070 * Construct a Kurtosis from an external moment
071 *
072 * @param m4 external Moment
073 */
074 public Kurtosis(final FourthMoment m4) {
075 incMoment = false;
076 this.moment = m4;
077 }
078
079 /**
080 * Copy constructor, creates a new {@code Kurtosis} identical
081 * to the {@code original}
082 *
083 * @param original the {@code Kurtosis} instance to copy
084 */
085 public Kurtosis(Kurtosis original) {
086 copy(original, this);
087 }
088
089 /**
090 * {@inheritDoc}
091 */
092 @Override
093 public void increment(final double d) {
094 if (incMoment) {
095 moment.increment(d);
096 } else {
097 throw MathRuntimeException.createIllegalStateException(
098 "statistics constructed from external moments cannot be incremented");
099 }
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 }