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 * https://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 18 /* 19 * This is not the original file distributed by the Apache Software Foundation 20 * It has been modified by the Hipparchus project 21 */ 22 package org.hipparchus.stat.descriptive.moment; 23 24 import java.io.Serializable; 25 26 import org.hipparchus.exception.MathIllegalArgumentException; 27 import org.hipparchus.exception.NullArgumentException; 28 import org.hipparchus.stat.descriptive.AbstractStorelessUnivariateStatistic; 29 import org.hipparchus.util.FastMath; 30 import org.hipparchus.util.MathArrays; 31 import org.hipparchus.util.MathUtils; 32 33 /** 34 * Computes the skewness of the available values. 35 * <p> 36 * We use the following (unbiased) formula to define skewness: 37 * <p> 38 * skewness = [n / (n -1) (n - 2)] sum[(x_i - mean)^3] / std^3 39 * <p> 40 * where n is the number of values, mean is the {@link Mean} and std is the 41 * {@link StandardDeviation}. 42 * <p> 43 * Note that this statistic is undefined for n < 3. <code>Double.Nan</code> 44 * is returned when there is not sufficient data to compute the statistic. 45 * Double.NaN may also be returned if the input includes NaN and / or 46 * infinite values. 47 * <p> 48 * <strong>Note that this implementation is not synchronized.</strong> If 49 * multiple threads access an instance of this class concurrently, and at least 50 * one of the threads invokes the <code>increment()</code> or 51 * <code>clear()</code> method, it must be synchronized externally. 52 */ 53 public class Skewness extends AbstractStorelessUnivariateStatistic implements Serializable { 54 55 /** Serializable version identifier */ 56 private static final long serialVersionUID = 20150412L; 57 58 /** Third moment on which this statistic is based */ 59 protected final ThirdMoment moment; 60 61 /** 62 * Determines whether or not this statistic can be incremented or cleared. 63 * <p> 64 * Statistics based on (constructed from) external moments cannot 65 * be incremented or cleared. 66 */ 67 protected final boolean incMoment; 68 69 /** 70 * Constructs a Skewness. 71 */ 72 public Skewness() { 73 moment = new ThirdMoment(); 74 incMoment = true; 75 } 76 77 /** 78 * Constructs a Skewness with an external moment. 79 * @param m3 external moment 80 */ 81 public Skewness(final ThirdMoment m3) { 82 this.moment = m3; 83 incMoment = false; 84 } 85 86 /** 87 * Copy constructor, creates a new {@code Skewness} identical 88 * to the {@code original}. 89 * 90 * @param original the {@code Skewness} instance to copy 91 * @throws NullArgumentException if original is null 92 */ 93 public Skewness(Skewness original) throws NullArgumentException { 94 MathUtils.checkNotNull(original); 95 this.moment = original.moment.copy(); 96 this.incMoment = original.incMoment; 97 } 98 99 /** 100 * {@inheritDoc} 101 * <p>Note that when {@link #Skewness(ThirdMoment)} is used to 102 * create a Skewness, this method does nothing. In that case, the 103 * ThirdMoment should be incremented directly. 104 */ 105 @Override 106 public void increment(final double d) { 107 if (incMoment) { 108 moment.increment(d); 109 } 110 } 111 112 /** 113 * Returns the value of the statistic based on the values that have been added. 114 * <p> 115 * See {@link Skewness} for the definition used in the computation. 116 * 117 * @return the skewness of the available values. 118 */ 119 @Override 120 public double getResult() { 121 122 if (moment.n < 3) { 123 return Double.NaN; 124 } 125 double variance = moment.m2 / (moment.n - 1); 126 if (variance < 10E-20) { 127 return 0.0d; 128 } else { 129 double n0 = moment.getN(); 130 return (n0 * moment.m3) / 131 ((n0 - 1) * (n0 -2) * FastMath.sqrt(variance) * variance); 132 } 133 } 134 135 /** {@inheritDoc} */ 136 @Override 137 public long getN() { 138 return moment.getN(); 139 } 140 141 /** {@inheritDoc} */ 142 @Override 143 public void clear() { 144 if (incMoment) { 145 moment.clear(); 146 } 147 } 148 149 /** 150 * Returns the Skewness of the entries in the specified portion of the 151 * input array. 152 * <p> 153 * See {@link Skewness} for the definition used in the computation. 154 * <p> 155 * Throws <code>IllegalArgumentException</code> if the array is null. 156 * 157 * @param values the input array 158 * @param begin the index of the first array element to include 159 * @param length the number of elements to include 160 * @return the skewness of the values or Double.NaN if length is less than 3 161 * @throws MathIllegalArgumentException if the array is null or the array index 162 * parameters are not valid 163 */ 164 @Override 165 public double evaluate(final double[] values, final int begin, final int length) 166 throws MathIllegalArgumentException { 167 168 // Initialize the skewness 169 double skew = Double.NaN; 170 171 if (MathArrays.verifyValues(values, begin, length) && length > 2 ) { 172 Mean mean = new Mean(); 173 // Get the mean and the standard deviation 174 double m = mean.evaluate(values, begin, length); 175 176 // Calc the std, this is implemented here instead 177 // of using the standardDeviation method eliminate 178 // a duplicate pass to get the mean 179 double accum = 0.0; 180 double accum2 = 0.0; 181 for (int i = begin; i < begin + length; i++) { 182 final double d = values[i] - m; 183 accum += d * d; 184 accum2 += d; 185 } 186 final double variance = (accum - (accum2 * accum2 / length)) / (length - 1); 187 188 double accum3 = 0.0; 189 for (int i = begin; i < begin + length; i++) { 190 final double d = values[i] - m; 191 accum3 += d * d * d; 192 } 193 accum3 /= variance * FastMath.sqrt(variance); 194 195 // Get N 196 double n0 = length; 197 198 // Calculate skewness 199 skew = (n0 / ((n0 - 1) * (n0 - 2))) * accum3; 200 } 201 return skew; 202 } 203 204 /** {@inheritDoc} */ 205 @Override 206 public Skewness copy() { 207 return new Skewness(this); 208 } 209 210 }