SemiVariance.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.  *      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.math4.legacy.stat.descriptive.moment;

  18. import org.apache.commons.math4.legacy.exception.MathIllegalArgumentException;
  19. import org.apache.commons.math4.legacy.exception.NullArgumentException;
  20. import org.apache.commons.math4.legacy.stat.descriptive.AbstractUnivariateStatistic;
  21. import org.apache.commons.math4.legacy.core.MathArrays;

  22. /**
  23.  * <p>Computes the semivariance of a set of values with respect to a given cutoff value.
  24.  * We define the <i>downside semivariance</i> of a set of values <code>x</code>
  25.  * against the <i>cutoff value</i> <code>cutoff</code> to be <br>
  26.  * <code>&Sigma; (x[i] - target)<sup>2</sup> / df</code> <br>
  27.  * where the sum is taken over all <code>i</code> such that {@code x[i] < cutoff}
  28.  * and <code>df</code> is the length of <code>x</code> (non-bias-corrected) or
  29.  * one less than this number (bias corrected).  The <i>upside semivariance</i>
  30.  * is defined similarly, with the sum taken over values of <code>x</code> that
  31.  * exceed the cutoff value.</p>
  32.  *
  33.  * <p>The cutoff value defaults to the mean, bias correction defaults to <code>true</code>
  34.  * and the "variance direction" (upside or downside) defaults to downside.  The variance direction
  35.  * and bias correction may be set using property setters or their values can provided as
  36.  * parameters to {@link #evaluate(double[], double, Direction, boolean, int, int)}.</p>
  37.  *
  38.  * <p>If the input array is null, <code>evaluate</code> methods throw
  39.  * <code>IllegalArgumentException.</code>  If the array has length 1, <code>0</code>
  40.  * is returned, regardless of the value of the <code>cutoff.</code>
  41.  *
  42.  * <p><strong>Note that this class is not intended to be threadsafe.</strong> If
  43.  * multiple threads access an instance of this class concurrently, and one or
  44.  * more of these threads invoke property setters, external synchronization must
  45.  * be provided to ensure correct results.</p>
  46.  *
  47.  * @since 2.1
  48.  */
  49. public class SemiVariance extends AbstractUnivariateStatistic {

  50.     /**
  51.      * The UPSIDE Direction is used to specify that the observations above the
  52.      * cutoff point will be used to calculate SemiVariance.
  53.      */
  54.     public static final Direction UPSIDE_VARIANCE = Direction.UPSIDE;

  55.     /**
  56.      * The DOWNSIDE Direction is used to specify that the observations below.
  57.      * the cutoff point will be used to calculate SemiVariance
  58.      */
  59.     public static final Direction DOWNSIDE_VARIANCE = Direction.DOWNSIDE;

  60.     /**
  61.      * Determines whether or not bias correction is applied when computing the
  62.      * value of the statistic.  True means that bias is corrected.
  63.      */
  64.     private boolean biasCorrected = true;

  65.     /**
  66.      * Determines whether to calculate downside or upside SemiVariance.
  67.      */
  68.     private Direction varianceDirection = Direction.DOWNSIDE;

  69.     /**
  70.      * Constructs a SemiVariance with default (true) <code>biasCorrected</code>
  71.      * property and default (Downside) <code>varianceDirection</code> property.
  72.      */
  73.     public SemiVariance() {
  74.     }

  75.     /**
  76.      * Constructs a SemiVariance with the specified <code>biasCorrected</code>
  77.      * property and default (Downside) <code>varianceDirection</code> property.
  78.      *
  79.      * @param biasCorrected  setting for bias correction - true means
  80.      * bias will be corrected and is equivalent to using the "no arg"
  81.      * constructor
  82.      */
  83.     public SemiVariance(final boolean biasCorrected) {
  84.         this.biasCorrected = biasCorrected;
  85.     }

  86.     /**
  87.      * Constructs a SemiVariance with the specified <code>Direction</code> property.
  88.      * and default (true) <code>biasCorrected</code> property
  89.      *
  90.      * @param direction  setting for the direction of the SemiVariance
  91.      * to calculate
  92.      */
  93.     public SemiVariance(final Direction direction) {
  94.         this.varianceDirection = direction;
  95.     }

  96.     /**
  97.      * Constructs a SemiVariance with the specified <code>isBiasCorrected</code>
  98.      * property and the specified <code>Direction</code> property.
  99.      *
  100.      * @param corrected  setting for bias correction - true means
  101.      * bias will be corrected and is equivalent to using the "no arg"
  102.      * constructor
  103.      *
  104.      * @param direction  setting for the direction of the SemiVariance
  105.      * to calculate
  106.      */
  107.     public SemiVariance(final boolean corrected, final Direction direction) {
  108.         this.biasCorrected = corrected;
  109.         this.varianceDirection = direction;
  110.     }

  111.     /**
  112.      * Copy constructor, creates a new {@code SemiVariance} identical
  113.      * to the {@code original}.
  114.      *
  115.      * @param original the {@code SemiVariance} instance to copy
  116.      * @throws NullArgumentException  if original is null
  117.      */
  118.     public SemiVariance(final SemiVariance original) throws NullArgumentException {
  119.         copy(original, this);
  120.     }

  121.     /**
  122.      * {@inheritDoc}
  123.      */
  124.     @Override
  125.     public SemiVariance copy() {
  126.         SemiVariance result = new SemiVariance();
  127.         // No try-catch or advertised exception because args are guaranteed non-null
  128.         copy(this, result);
  129.         return result;
  130.     }

  131.     /**
  132.      * Copies source to dest.
  133.      * <p>Neither source nor dest can be null.</p>
  134.      *
  135.      * @param source SemiVariance to copy
  136.      * @param dest SemiVariance to copy to
  137.      * @throws NullArgumentException if either source or dest is null
  138.      */
  139.     public static void copy(final SemiVariance source, SemiVariance dest)
  140.         throws NullArgumentException {
  141.         NullArgumentException.check(source);
  142.         NullArgumentException.check(dest);
  143.         dest.biasCorrected = source.biasCorrected;
  144.         dest.varianceDirection = source.varianceDirection;
  145.     }

  146.     /**
  147.      * <p>Returns the {@link SemiVariance} of the designated values against the mean, using
  148.      * instance properties varianceDirection and biasCorrection.</p>
  149.      *
  150.      * <p>Returns <code>NaN</code> if the array is empty and throws
  151.      * <code>IllegalArgumentException</code> if the array is null.</p>
  152.      *
  153.      * @param values the input array
  154.      * @param start index of the first array element to include
  155.      * @param length the number of elements to include
  156.      * @return the SemiVariance
  157.      * @throws MathIllegalArgumentException if the parameters are not valid
  158.      *
  159.      */
  160.      @Override
  161.      public double evaluate(final double[] values, final int start, final int length)
  162.          throws MathIllegalArgumentException {
  163.          double m = (new Mean()).evaluate(values, start, length);
  164.          return evaluate(values, m, varianceDirection, biasCorrected, 0, values.length);
  165.      }

  166.      /**
  167.       * This method calculates {@link SemiVariance} for the entire array against the mean, using
  168.       * the current value of the biasCorrection instance property.
  169.       *
  170.       * @param values the input array
  171.       * @param direction the {@link Direction} of the semivariance
  172.       * @return the SemiVariance
  173.       * @throws MathIllegalArgumentException if values is null
  174.       *
  175.       */
  176.      public double evaluate(final double[] values, Direction direction)
  177.          throws MathIllegalArgumentException {
  178.          double m = (new Mean()).evaluate(values);
  179.          return evaluate(values, m, direction, biasCorrected, 0, values.length);
  180.      }

  181.      /**
  182.       * <p>Returns the {@link SemiVariance} of the designated values against the cutoff, using
  183.       * instance properties variancDirection and biasCorrection.</p>
  184.       *
  185.       * <p>Returns <code>NaN</code> if the array is empty and throws
  186.       * <code>MathIllegalArgumentException</code> if the array is null.</p>
  187.       *
  188.       * @param values the input array
  189.       * @param cutoff the reference point
  190.       * @return the SemiVariance
  191.       * @throws MathIllegalArgumentException if values is null
  192.       */
  193.      public double evaluate(final double[] values, final double cutoff)
  194.          throws MathIllegalArgumentException {
  195.          return evaluate(values, cutoff, varianceDirection, biasCorrected, 0, values.length);
  196.      }

  197.      /**
  198.       * <p>Returns the {@link SemiVariance} of the designated values against the cutoff in the
  199.       * given direction, using the current value of the biasCorrection instance property.</p>
  200.       *
  201.       * <p>Returns <code>NaN</code> if the array is empty and throws
  202.       * <code>MathIllegalArgumentException</code> if the array is null.</p>
  203.       *
  204.       * @param values the input array
  205.       * @param cutoff the reference point
  206.       * @param direction the {@link Direction} of the semivariance
  207.       * @return the SemiVariance
  208.       * @throws MathIllegalArgumentException if values is null
  209.       */
  210.      public double evaluate(final double[] values, final double cutoff, final Direction direction)
  211.          throws MathIllegalArgumentException {
  212.          return evaluate(values, cutoff, direction, biasCorrected, 0, values.length);
  213.      }

  214.      /**
  215.       * <p>Returns the {@link SemiVariance} of the designated values against the cutoff
  216.       * in the given direction with the provided bias correction.</p>
  217.       *
  218.       * <p>Returns <code>NaN</code> if the array is empty and throws
  219.       * <code>IllegalArgumentException</code> if the array is null.</p>
  220.       *
  221.       * @param values the input array
  222.       * @param cutoff the reference point
  223.       * @param direction the {@link Direction} of the semivariance
  224.       * @param corrected the BiasCorrection flag
  225.       * @param start index of the first array element to include
  226.       * @param length the number of elements to include
  227.       * @return the SemiVariance
  228.       * @throws MathIllegalArgumentException if the parameters are not valid
  229.       *
  230.       */
  231.      public double evaluate (final double[] values, final double cutoff, final Direction direction,
  232.                              final boolean corrected, final int start, final int length)
  233.          throws MathIllegalArgumentException {

  234.          MathArrays.verifyValues(values, start, length);
  235.          if (values.length == 0) {
  236.              return Double.NaN;
  237.          } else {
  238.              if (values.length == 1) {
  239.                  return 0.0;
  240.              } else {
  241.                  final boolean booleanDirection = direction.getDirection();

  242.                  double dev = 0.0;
  243.                  double sumsq = 0.0;
  244.                  for (int i = start; i < length; i++) {
  245.                      if ((values[i] > cutoff) == booleanDirection) {
  246.                          dev = values[i] - cutoff;
  247.                          sumsq += dev * dev;
  248.                      }
  249.                  }

  250.                  if (corrected) {
  251.                      return sumsq / (length - 1.0);
  252.                  } else {
  253.                      return sumsq / length;
  254.                  }
  255.              }
  256.          }
  257.      }

  258.      /**
  259.       * Returns true iff biasCorrected property is set to true.
  260.       *
  261.       * @return the value of biasCorrected.
  262.       */
  263.      public boolean isBiasCorrected() {
  264.          return biasCorrected;
  265.      }

  266.      /**
  267.       * Sets the biasCorrected property.
  268.       *
  269.       * @param biasCorrected new biasCorrected property value
  270.       */
  271.      public void setBiasCorrected(boolean biasCorrected) {
  272.          this.biasCorrected = biasCorrected;
  273.      }

  274.      /**
  275.       * Returns the varianceDirection property.
  276.       *
  277.       * @return the varianceDirection
  278.       */
  279.      public Direction getVarianceDirection () {
  280.          return varianceDirection;
  281.      }

  282.      /**
  283.       * Sets the variance direction.
  284.       *
  285.       * @param varianceDirection the direction of the semivariance
  286.       */
  287.      public void setVarianceDirection(Direction varianceDirection) {
  288.          this.varianceDirection = varianceDirection;
  289.      }

  290.      /**
  291.       * The direction of the semivariance - either upside or downside. The direction
  292.       * is represented by boolean, with true corresponding to UPSIDE semivariance.
  293.       */
  294.      public enum Direction {
  295.          /**
  296.           * The UPSIDE Direction is used to specify that the observations above the.
  297.           * cutoff point will be used to calculate SemiVariance
  298.           */
  299.          UPSIDE (true),

  300.          /**
  301.           * The DOWNSIDE Direction is used to specify that the observations below.
  302.           * the cutoff point will be used to calculate SemiVariance
  303.           */
  304.          DOWNSIDE (false);

  305.          /**
  306.           * boolean value  UPSIDE <-> true.
  307.           */
  308.          private boolean direction;

  309.          /**
  310.           * Create a Direction with the given value.
  311.           *
  312.           * @param b boolean value representing the Direction. True corresponds to UPSIDE.
  313.           */
  314.          Direction (boolean b) {
  315.              direction = b;
  316.          }

  317.          /**
  318.           * Returns the value of this Direction. True corresponds to UPSIDE.
  319.           *
  320.           * @return true if direction is UPSIDE; false otherwise
  321.           */
  322.          boolean getDirection () {
  323.              return direction;
  324.          }
  325.      }
  326. }