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