Skewness.java

  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.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */
  21. package org.hipparchus.stat.descriptive.moment;

  22. import java.io.Serializable;

  23. import org.hipparchus.exception.MathIllegalArgumentException;
  24. import org.hipparchus.exception.NullArgumentException;
  25. import org.hipparchus.stat.descriptive.AbstractStorelessUnivariateStatistic;
  26. import org.hipparchus.util.FastMath;
  27. import org.hipparchus.util.MathArrays;
  28. import org.hipparchus.util.MathUtils;

  29. /**
  30.  * Computes the skewness of the available values.
  31.  * <p>
  32.  * We use the following (unbiased) formula to define skewness:
  33.  * <p>
  34.  * skewness = [n / (n -1) (n - 2)] sum[(x_i - mean)^3] / std^3
  35.  * <p>
  36.  * where n is the number of values, mean is the {@link Mean} and std is the
  37.  * {@link StandardDeviation}.
  38.  * <p>
  39.  * Note that this statistic is undefined for n &lt; 3.  <code>Double.Nan</code>
  40.  * is returned when there is not sufficient data to compute the statistic.
  41.  * Double.NaN may also be returned if the input includes NaN and / or
  42.  * infinite values.
  43.  * <p>
  44.  * <strong>Note that this implementation is not synchronized.</strong> If
  45.  * multiple threads access an instance of this class concurrently, and at least
  46.  * one of the threads invokes the <code>increment()</code> or
  47.  * <code>clear()</code> method, it must be synchronized externally.
  48.  */
  49. public class Skewness extends AbstractStorelessUnivariateStatistic implements Serializable {

  50.     /** Serializable version identifier */
  51.     private static final long serialVersionUID = 20150412L;

  52.     /** Third moment on which this statistic is based */
  53.     protected final ThirdMoment moment;

  54.      /**
  55.      * Determines whether or not this statistic can be incremented or cleared.
  56.      * <p>
  57.      * Statistics based on (constructed from) external moments cannot
  58.      * be incremented or cleared.
  59.     */
  60.     protected final boolean incMoment;

  61.     /**
  62.      * Constructs a Skewness.
  63.      */
  64.     public Skewness() {
  65.         moment = new ThirdMoment();
  66.         incMoment = true;
  67.     }

  68.     /**
  69.      * Constructs a Skewness with an external moment.
  70.      * @param m3 external moment
  71.      */
  72.     public Skewness(final ThirdMoment m3) {
  73.         this.moment = m3;
  74.         incMoment = false;
  75.     }

  76.     /**
  77.      * Copy constructor, creates a new {@code Skewness} identical
  78.      * to the {@code original}.
  79.      *
  80.      * @param original the {@code Skewness} instance to copy
  81.      * @throws NullArgumentException if original is null
  82.      */
  83.     public Skewness(Skewness original) throws NullArgumentException {
  84.         MathUtils.checkNotNull(original);
  85.         this.moment    = original.moment.copy();
  86.         this.incMoment = original.incMoment;
  87.     }

  88.     /**
  89.      * {@inheritDoc}
  90.      * <p>Note that when {@link #Skewness(ThirdMoment)} is used to
  91.      * create a Skewness, this method does nothing. In that case, the
  92.      * ThirdMoment should be incremented directly.
  93.      */
  94.     @Override
  95.     public void increment(final double d) {
  96.         if (incMoment) {
  97.             moment.increment(d);
  98.         }
  99.     }

  100.     /**
  101.      * Returns the value of the statistic based on the values that have been added.
  102.      * <p>
  103.      * See {@link Skewness} for the definition used in the computation.
  104.      *
  105.      * @return the skewness of the available values.
  106.      */
  107.     @Override
  108.     public double getResult() {

  109.         if (moment.n < 3) {
  110.             return Double.NaN;
  111.         }
  112.         double variance = moment.m2 / (moment.n - 1);
  113.         if (variance < 10E-20) {
  114.             return 0.0d;
  115.         } else {
  116.             double n0 = moment.getN();
  117.             return  (n0 * moment.m3) /
  118.             ((n0 - 1) * (n0 -2) * FastMath.sqrt(variance) * variance);
  119.         }
  120.     }

  121.     /** {@inheritDoc} */
  122.     @Override
  123.     public long getN() {
  124.         return moment.getN();
  125.     }

  126.     /** {@inheritDoc} */
  127.     @Override
  128.     public void clear() {
  129.         if (incMoment) {
  130.             moment.clear();
  131.         }
  132.     }

  133.     /**
  134.      * Returns the Skewness of the entries in the specified portion of the
  135.      * input array.
  136.      * <p>
  137.      * See {@link Skewness} for the definition used in the computation.
  138.      * <p>
  139.      * Throws <code>IllegalArgumentException</code> if the array is null.
  140.      *
  141.      * @param values the input array
  142.      * @param begin the index of the first array element to include
  143.      * @param length the number of elements to include
  144.      * @return the skewness of the values or Double.NaN if length is less than 3
  145.      * @throws MathIllegalArgumentException if the array is null or the array index
  146.      *  parameters are not valid
  147.      */
  148.     @Override
  149.     public double evaluate(final double[] values, final int begin, final int length)
  150.         throws MathIllegalArgumentException {

  151.         // Initialize the skewness
  152.         double skew = Double.NaN;

  153.         if (MathArrays.verifyValues(values, begin, length) && length > 2 ) {
  154.             Mean mean = new Mean();
  155.             // Get the mean and the standard deviation
  156.             double m = mean.evaluate(values, begin, length);

  157.             // Calc the std, this is implemented here instead
  158.             // of using the standardDeviation method eliminate
  159.             // a duplicate pass to get the mean
  160.             double accum = 0.0;
  161.             double accum2 = 0.0;
  162.             for (int i = begin; i < begin + length; i++) {
  163.                 final double d = values[i] - m;
  164.                 accum  += d * d;
  165.                 accum2 += d;
  166.             }
  167.             final double variance = (accum - (accum2 * accum2 / length)) / (length - 1);

  168.             double accum3 = 0.0;
  169.             for (int i = begin; i < begin + length; i++) {
  170.                 final double d = values[i] - m;
  171.                 accum3 += d * d * d;
  172.             }
  173.             accum3 /= variance * FastMath.sqrt(variance);

  174.             // Get N
  175.             double n0 = length;

  176.             // Calculate skewness
  177.             skew = (n0 / ((n0 - 1) * (n0 - 2))) * accum3;
  178.         }
  179.         return skew;
  180.     }

  181.     /** {@inheritDoc} */
  182.     @Override
  183.     public Skewness copy() {
  184.         return new Skewness(this);
  185.     }

  186. }