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.  *      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.StatUtils;
  26. import org.hipparchus.stat.descriptive.AbstractUnivariateStatistic;
  27. import org.hipparchus.util.MathArrays;

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

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

  60.     /**
  61.      * The DOWNSIDE Direction is used to specify that the observations below
  62.      * the cutoff point will be used to calculate SemiVariance
  63.      */
  64.     public static final Direction DOWNSIDE_VARIANCE = Direction.DOWNSIDE;

  65.     /** Serializable version identifier */
  66.     private static final long serialVersionUID = 20150412L;

  67.     /**
  68.      * Determines whether or not bias correction is applied when computing the
  69.      * value of the statistic.  True means that bias is corrected.
  70.      */
  71.     private final boolean biasCorrected;

  72.     /**
  73.      * Determines whether to calculate downside or upside SemiVariance.
  74.      */
  75.     private final Direction varianceDirection;

  76.     /**
  77.      * Constructs a SemiVariance with default (true) <code>biasCorrected</code>
  78.      * property and default (Downside) <code>varianceDirection</code> property.
  79.      */
  80.     public SemiVariance() {
  81.         this(true, Direction.DOWNSIDE);
  82.     }

  83.     /**
  84.      * Constructs a SemiVariance with the specified <code>biasCorrected</code>
  85.      * property and default (Downside) <code>varianceDirection</code> property.
  86.      *
  87.      * @param biasCorrected  setting for bias correction - true means
  88.      * bias will be corrected and is equivalent to using the argumentless
  89.      * constructor
  90.      */
  91.     public SemiVariance(final boolean biasCorrected) {
  92.         this(biasCorrected, Direction.DOWNSIDE);
  93.     }

  94.     /**
  95.      * Constructs a SemiVariance with the specified <code>Direction</code> property
  96.      * and default (true) <code>biasCorrected</code> property
  97.      *
  98.      * @param direction  setting for the direction of the SemiVariance
  99.      * to calculate
  100.      */
  101.     public SemiVariance(final Direction direction) {
  102.         this(true, direction);
  103.     }

  104.     /**
  105.      * Constructs a SemiVariance with the specified <code>isBiasCorrected</code>
  106.      * property and the specified <code>Direction</code> property.
  107.      *
  108.      * @param corrected  setting for bias correction - true means
  109.      * bias will be corrected and is equivalent to using the argumentless
  110.      * constructor
  111.      *
  112.      * @param direction  setting for the direction of the SemiVariance
  113.      * to calculate
  114.      */
  115.     public SemiVariance(final boolean corrected, final Direction direction) {
  116.         this.biasCorrected     = corrected;
  117.         this.varianceDirection = direction;
  118.     }

  119.     /**
  120.      * Copy constructor, creates a new {@code SemiVariance} identical
  121.      * to the {@code original}.
  122.      *
  123.      * @param original the {@code SemiVariance} instance to copy
  124.      * @throws NullArgumentException  if original is null
  125.      */
  126.     public SemiVariance(final SemiVariance original) throws NullArgumentException {
  127.         super(original);
  128.         this.biasCorrected     = original.biasCorrected;
  129.         this.varianceDirection = original.varianceDirection;
  130.     }

  131.     /** {@inheritDoc} */
  132.     @Override
  133.     public SemiVariance copy() {
  134.         return new SemiVariance(this);
  135.     }

  136.     /**
  137.      * Returns the {@link SemiVariance} of the designated values against the mean, using
  138.      * instance properties varianceDirection and biasCorrection.
  139.      * <p>
  140.      * Returns <code>NaN</code> if the array is empty and throws
  141.      * <code>IllegalArgumentException</code> if the array is null.
  142.      *
  143.      * @param values the input array
  144.      * @param start index of the first array element to include
  145.      * @param length the number of elements to include
  146.      * @return the SemiVariance
  147.      * @throws MathIllegalArgumentException if the parameters are not valid
  148.      */
  149.      @Override
  150.      public double evaluate(final double[] values, final int start, final int length)
  151.          throws MathIllegalArgumentException {
  152.          double m = StatUtils.mean(values, start, length);
  153.          return evaluate(values, m, varianceDirection, biasCorrected, start, length);
  154.      }

  155.      /**
  156.       * This method calculates {@link SemiVariance} for the entire array against the mean,
  157.       * using the current value of the biasCorrection instance property.
  158.       *
  159.       * @param values the input array
  160.       * @param direction the {@link Direction} of the semivariance
  161.       * @return the SemiVariance
  162.       * @throws MathIllegalArgumentException if values is null
  163.       */
  164.      public double evaluate(final double[] values, Direction direction)
  165.          throws MathIllegalArgumentException {
  166.          double m = StatUtils.mean(values);
  167.          return evaluate(values, m, direction, biasCorrected, 0, values.length);
  168.      }

  169.      /**
  170.       * Returns the {@link SemiVariance} of the designated values against the cutoff,
  171.       * using instance properties variancDirection and biasCorrection.
  172.       * <p>
  173.       * Returns <code>NaN</code> if the array is empty.
  174.       *
  175.       * @param values the input array
  176.       * @param cutoff the reference point
  177.       * @return the SemiVariance
  178.       * @throws MathIllegalArgumentException if values is null
  179.       */
  180.      public double evaluate(final double[] values, final double cutoff)
  181.          throws MathIllegalArgumentException {
  182.          return evaluate(values, cutoff, varianceDirection, biasCorrected, 0, values.length);
  183.      }

  184.      /**
  185.       * Returns the {@link SemiVariance} of the designated values against the cutoff in the
  186.       * given direction, using the current value of the biasCorrection instance property.
  187.       * <p>
  188.       * Returns <code>NaN</code> if the array is empty.
  189.       *
  190.       * @param values the input array
  191.       * @param cutoff the reference point
  192.       * @param direction the {@link Direction} of the semivariance
  193.       * @return the SemiVariance
  194.       * @throws MathIllegalArgumentException if values is null
  195.       */
  196.      public double evaluate(final double[] values, final double cutoff, final Direction direction)
  197.          throws MathIllegalArgumentException {
  198.          return evaluate(values, cutoff, direction, biasCorrected, 0, values.length);
  199.      }

  200.      /**
  201.       * Returns the {@link SemiVariance} of the designated values against the cutoff
  202.       * in the given direction with the provided bias correction.
  203.       * <p>
  204.       * Returns <code>NaN</code> if the array is empty.
  205.       *
  206.       * @param values the input array
  207.       * @param cutoff the reference point
  208.       * @param direction the {@link Direction} of the semivariance
  209.       * @param corrected the BiasCorrection flag
  210.       * @param start index of the first array element to include
  211.       * @param length the number of elements to include
  212.       * @return the SemiVariance
  213.       * @throws MathIllegalArgumentException if the parameters are not valid
  214.       */
  215.      public double evaluate(final double[] values, final double cutoff, final Direction direction,
  216.                             final boolean corrected, final int start, final int length)
  217.          throws MathIllegalArgumentException {

  218.          MathArrays.verifyValues(values, start, length);
  219.          if (values.length == 0) {
  220.              return Double.NaN;
  221.          } else {
  222.              if (values.length == 1) {
  223.                  return 0.0;
  224.              } else {

  225.                  double sumsq = 0.0;
  226.                  final int end = start + length;
  227.                  for (int i = start; i < end; i++) {
  228.                      if (direction.considerObservation(values[i], cutoff)) {
  229.                          final double dev = values[i] - cutoff;
  230.                          sumsq += dev * dev;
  231.                      }
  232.                  }

  233.                  if (corrected) {
  234.                      return sumsq / (length - 1.0);
  235.                  } else {
  236.                      return sumsq / length;
  237.                  }
  238.              }
  239.          }
  240.      }

  241.      /**
  242.       * Returns true iff biasCorrected property is set to true.
  243.       *
  244.       * @return the value of biasCorrected.
  245.       */
  246.      public boolean isBiasCorrected() {
  247.          return biasCorrected;
  248.      }

  249.      /**
  250.       * Returns a copy of this instance with the given biasCorrected setting.
  251.       *
  252.       * @param isBiasCorrected new biasCorrected property value
  253.       * @return a copy of this instance with the given bias correction setting
  254.       */
  255.      public SemiVariance withBiasCorrected(boolean isBiasCorrected) {
  256.          return new SemiVariance(isBiasCorrected, this.varianceDirection);
  257.      }

  258.      /**
  259.       * Returns the varianceDirection property.
  260.       *
  261.       * @return the varianceDirection
  262.       */
  263.      public Direction getVarianceDirection () {
  264.          return varianceDirection;
  265.      }

  266.      /**
  267.       * Returns a copy of this instance with the given direction setting.
  268.       *
  269.       * @param direction the direction of the semivariance
  270.       * @return a copy of this instance with the given direction setting
  271.       */
  272.      public SemiVariance withVarianceDirection(Direction direction) {
  273.          return new SemiVariance(this.biasCorrected, direction);
  274.      }

  275.      /**
  276.       * The direction of the semivariance - either upside or downside. The direction
  277.       * is represented by boolean, with true corresponding to UPSIDE semivariance.
  278.       */
  279.      public enum Direction {
  280.          /**
  281.           * The UPSIDE Direction is used to specify that the observations above the
  282.           * cutoff point will be used to calculate SemiVariance
  283.           */
  284.          UPSIDE (true),

  285.          /**
  286.           * The DOWNSIDE Direction is used to specify that the observations below
  287.           * the cutoff point will be used to calculate SemiVariance
  288.           */
  289.          DOWNSIDE (false);

  290.          /**
  291.           * boolean value  UPSIDE <-> true
  292.           */
  293.          private final boolean direction;

  294.          /**
  295.           * Create a Direction with the given value.
  296.           *
  297.           * @param b boolean value representing the Direction. True corresponds to UPSIDE.
  298.           */
  299.          Direction (boolean b) {
  300.              direction = b;
  301.          }

  302.          /** Check if observation should be considered.
  303.           * @param value observation value
  304.           * @param cutoff cutoff point
  305.           * @return true if observation should be considered.
  306.           * @since 1.4
  307.           */
  308.          boolean considerObservation(final double value, final double cutoff) {
  309.              return value > cutoff == direction;
  310.          }

  311.      }
  312. }