Home | History | Annotate | Download | only in moment
      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 
     18 package org.apache.commons.math.stat.descriptive.moment;
     19 
     20 import java.io.Serializable;
     21 import org.apache.commons.math.exception.NullArgumentException;
     22 import org.apache.commons.math.exception.util.LocalizedFormats;
     23 import org.apache.commons.math.stat.descriptive.AbstractUnivariateStatistic;
     24 
     25 /**
     26  * <p>Computes the semivariance of a set of values with respect to a given cutoff value.
     27  * We define the <i>downside semivariance</i> of a set of values <code>x</code>
     28  * against the <i>cutoff value</i> <code>cutoff</code> to be <br/>
     29  * <code>&Sigma; (x[i] - target)<sup>2</sup> / df</code> <br/>
     30  * where the sum is taken over all <code>i</code> such that <code>x[i] < cutoff</code>
     31  * and <code>df</code> is the length of <code>x</code> (non-bias-corrected) or
     32  * one less than this number (bias corrected).  The <i>upside semivariance</i>
     33  * is defined similarly, with the sum taken over values of <code>x</code> that
     34  * exceed the cutoff value.</p>
     35  *
     36  * <p>The cutoff value defaults to the mean, bias correction defaults to <code>true</code>
     37  * and the "variance direction" (upside or downside) defaults to downside.  The variance direction
     38  * and bias correction may be set using property setters or their values can provided as
     39  * parameters to {@link #evaluate(double[], double, Direction, boolean, int, int)}.</p>
     40  *
     41  * <p>If the input array is null, <code>evaluate</code> methods throw
     42  * <code>IllegalArgumentException.</code>  If the array has length 1, <code>0</code>
     43  * is returned, regardless of the value of the <code>cutoff.</code>
     44  *
     45  * <p><strong>Note that this class is not intended to be threadsafe.</strong> If
     46  * multiple threads access an instance of this class concurrently, and one or
     47  * more of these threads invoke property setters, external synchronization must
     48  * be provided to ensure correct results.</p>
     49  *
     50  * @version $Revision: 1006299 $ $Date: 2010-10-10 16:47:17 +0200 (dim. 10 oct. 2010) $
     51  * @since 2.1
     52  */
     53 
     54 public class SemiVariance extends AbstractUnivariateStatistic implements Serializable {
     55 
     56     /**
     57      * The UPSIDE Direction is used to specify that the observations above the
     58      * cutoff point will be used to calculate SemiVariance.
     59      */
     60     public static final Direction UPSIDE_VARIANCE = Direction.UPSIDE;
     61 
     62     /**
     63      * The DOWNSIDE Direction is used to specify that the observations below
     64      * the cutoff point will be used to calculate SemiVariance
     65      */
     66     public static final Direction DOWNSIDE_VARIANCE = Direction.DOWNSIDE;
     67 
     68     /** Serializable version identifier */
     69     private static final long serialVersionUID = -2653430366886024994L;
     70 
     71     /**
     72      * Determines whether or not bias correction is applied when computing the
     73      * value of the statisic.  True means that bias is corrected.
     74      */
     75     private boolean biasCorrected = true;
     76 
     77     /**
     78      * Determines whether to calculate downside or upside SemiVariance.
     79      */
     80     private Direction varianceDirection = Direction.DOWNSIDE;
     81 
     82     /**
     83      * Constructs a SemiVariance with default (true) <code>biasCorrected</code>
     84      * property and default (Downside) <code>varianceDirection</code> property.
     85      */
     86     public SemiVariance() {
     87     }
     88 
     89     /**
     90      * Constructs a SemiVariance with the specified <code>biasCorrected</code>
     91      * property and default (Downside) <code>varianceDirection</code> property.
     92      *
     93      * @param biasCorrected  setting for bias correction - true means
     94      * bias will be corrected and is equivalent to using the argumentless
     95      * constructor
     96      */
     97     public SemiVariance(final boolean biasCorrected) {
     98         this.biasCorrected = biasCorrected;
     99     }
    100 
    101 
    102     /**
    103      * Constructs a SemiVariance with the specified <code>Direction</code> property
    104      * and default (true) <code>biasCorrected</code> property
    105      *
    106      * @param direction  setting for the direction of the SemiVariance
    107      * to calculate
    108      */
    109     public SemiVariance(final Direction direction) {
    110         this.varianceDirection = direction;
    111     }
    112 
    113 
    114     /**
    115      * Constructs a SemiVariance with the specified <code>isBiasCorrected</code>
    116      * property and the specified <code>Direction</code> property.
    117      *
    118      * @param corrected  setting for bias correction - true means
    119      * bias will be corrected and is equivalent to using the argumentless
    120      * constructor
    121      *
    122      * @param direction  setting for the direction of the SemiVariance
    123      * to calculate
    124      */
    125     public SemiVariance(final boolean corrected, final Direction direction) {
    126         this.biasCorrected = corrected;
    127         this.varianceDirection = direction;
    128     }
    129 
    130 
    131     /**
    132      * Copy constructor, creates a new {@code SemiVariance} identical
    133      * to the {@code original}
    134      *
    135      * @param original the {@code SemiVariance} instance to copy
    136      */
    137     public SemiVariance(final SemiVariance original) {
    138         copy(original, this);
    139     }
    140 
    141 
    142     /**
    143      * {@inheritDoc}
    144      */
    145     @Override
    146     public SemiVariance copy() {
    147         SemiVariance result = new SemiVariance();
    148         copy(this, result);
    149         return result;
    150     }
    151 
    152 
    153     /**
    154      * Copies source to dest.
    155      * <p>Neither source nor dest can be null.</p>
    156      *
    157      * @param source SemiVariance to copy
    158      * @param dest SemiVariance to copy to
    159      * @throws NullPointerException if either source or dest is null
    160      */
    161     public static void copy(final SemiVariance source, SemiVariance dest) {
    162         dest.setData(source.getDataRef());
    163         dest.biasCorrected = source.biasCorrected;
    164         dest.varianceDirection = source.varianceDirection;
    165     }
    166 
    167 
    168     /**
    169      * This method calculates {@link SemiVariance} for the entire array against the mean, using
    170      * instance properties varianceDirection and biasCorrection.
    171      *
    172      * @param values the input array
    173      * @return the SemiVariance
    174      * @throws IllegalArgumentException if values is null
    175      *
    176      */
    177     @Override
    178     public double evaluate(final double[] values) {
    179         if (values == null) {
    180             throw new NullArgumentException(LocalizedFormats.INPUT_ARRAY);
    181          }
    182         return evaluate(values, 0, values.length);
    183     }
    184 
    185 
    186     /**
    187       * <p>Returns the {@link SemiVariance} of the designated values against the mean, using
    188       * instance properties varianceDirection and biasCorrection.</p>
    189       *
    190       * <p>Returns <code>NaN</code> if the array is empty and throws
    191       * <code>IllegalArgumentException</code> if the array is null.</p>
    192       *
    193       * @param values the input array
    194       * @param start index of the first array element to include
    195       * @param length the number of elements to include
    196       * @return the SemiVariance
    197       * @throws IllegalArgumentException if the parameters are not valid
    198       *
    199       */
    200       @Override
    201       public double evaluate(final double[] values, final int start, final int length) {
    202         double m = (new Mean()).evaluate(values, start, length);
    203         return evaluate(values, m, varianceDirection, biasCorrected, 0, values.length);
    204       }
    205 
    206 
    207       /**
    208        * This method calculates {@link SemiVariance} for the entire array against the mean, using
    209        * the current value of the biasCorrection instance property.
    210        *
    211        * @param values the input array
    212        * @param direction the {@link Direction} of the semivariance
    213        * @return the SemiVariance
    214        * @throws IllegalArgumentException if values is null
    215        *
    216        */
    217       public double evaluate(final double[] values, Direction direction) {
    218           double m = (new Mean()).evaluate(values);
    219           return evaluate (values, m, direction, biasCorrected, 0, values.length);
    220       }
    221 
    222       /**
    223        * <p>Returns the {@link SemiVariance} of the designated values against the cutoff, using
    224        * instance properties variancDirection and biasCorrection.</p>
    225        *
    226        * <p>Returns <code>NaN</code> if the array is empty and throws
    227        * <code>IllegalArgumentException</code> if the array is null.</p>
    228        *
    229        * @param values the input array
    230        * @param cutoff the reference point
    231        * @return the SemiVariance
    232        * @throws IllegalArgumentException if values is null
    233        */
    234       public double evaluate(final double[] values, final double cutoff) {
    235           return evaluate(values, cutoff, varianceDirection, biasCorrected, 0, values.length);
    236       }
    237 
    238       /**
    239        * <p>Returns the {@link SemiVariance} of the designated values against the cutoff in the
    240        * given direction, using the current value of the biasCorrection instance property.</p>
    241        *
    242        * <p>Returns <code>NaN</code> if the array is empty and throws
    243        * <code>IllegalArgumentException</code> if the array is null.</p>
    244        *
    245        * @param values the input array
    246        * @param cutoff the reference point
    247        * @param direction the {@link Direction} of the semivariance
    248        * @return the SemiVariance
    249        * @throws IllegalArgumentException if values is null
    250        */
    251       public double evaluate(final double[] values, final double cutoff, final Direction direction) {
    252           return evaluate(values, cutoff, direction, biasCorrected, 0, values.length);
    253       }
    254 
    255 
    256      /**
    257       * <p>Returns the {@link SemiVariance} of the designated values against the cutoff
    258       * in the given direction with the provided bias correction.</p>
    259       *
    260       * <p>Returns <code>NaN</code> if the array is empty and throws
    261       * <code>IllegalArgumentException</code> if the array is null.</p>
    262       *
    263       * @param values the input array
    264       * @param cutoff the reference point
    265       * @param direction the {@link Direction} of the semivariance
    266       * @param corrected the BiasCorrection flag
    267       * @param start index of the first array element to include
    268       * @param length the number of elements to include
    269       * @return the SemiVariance
    270       * @throws IllegalArgumentException if the parameters are not valid
    271       *
    272       */
    273     public double evaluate (final double[] values, final double cutoff, final Direction direction,
    274             final boolean corrected, final int start, final int length) {
    275 
    276         test(values, start, length);
    277         if (values.length == 0) {
    278             return Double.NaN;
    279         } else {
    280             if (values.length == 1) {
    281                 return 0.0;
    282             } else {
    283                 final boolean booleanDirection = direction.getDirection();
    284 
    285                 double dev = 0.0;
    286                 double sumsq = 0.0;
    287                 for (int i = start; i < length; i++) {
    288                     if ((values[i] > cutoff) == booleanDirection) {
    289                        dev = values[i] - cutoff;
    290                        sumsq += dev * dev;
    291                     }
    292                 }
    293 
    294                 if (corrected) {
    295                     return sumsq / (length - 1.0);
    296                 } else {
    297                     return sumsq / length;
    298                 }
    299             }
    300         }
    301     }
    302 
    303     /**
    304      * Returns true iff biasCorrected property is set to true.
    305      *
    306      * @return the value of biasCorrected.
    307      */
    308     public boolean isBiasCorrected() {
    309         return biasCorrected;
    310     }
    311 
    312     /**
    313      * Sets the biasCorrected property.
    314      *
    315      * @param biasCorrected new biasCorrected property value
    316      */
    317     public void setBiasCorrected(boolean biasCorrected) {
    318         this.biasCorrected = biasCorrected;
    319     }
    320 
    321     /**
    322      * Returns the varianceDirection property.
    323      *
    324      * @return the varianceDirection
    325      */
    326     public Direction getVarianceDirection () {
    327         return varianceDirection;
    328     }
    329 
    330     /**
    331      * Sets the variance direction
    332      *
    333      * @param varianceDirection the direction of the semivariance
    334      */
    335     public void setVarianceDirection(Direction varianceDirection) {
    336         this.varianceDirection = varianceDirection;
    337     }
    338 
    339     /**
    340      * The direction of the semivariance - either upside or downside. The direction
    341      * is represented by boolean, with true corresponding to UPSIDE semivariance.
    342      */
    343     public enum Direction {
    344         /**
    345          * The UPSIDE Direction is used to specify that the observations above the
    346          * cutoff point will be used to calculate SemiVariance
    347          */
    348         UPSIDE (true),
    349 
    350         /**
    351          * The DOWNSIDE Direction is used to specify that the observations below
    352          * the cutoff point will be used to calculate SemiVariance
    353          */
    354         DOWNSIDE (false);
    355 
    356         /**
    357          *   boolean value  UPSIDE <-> true
    358          */
    359         private boolean direction;
    360 
    361         /**
    362          * Create a Direction with the given value.
    363          *
    364          * @param b boolean value representing the Direction. True corresponds to UPSIDE.
    365          */
    366         Direction (boolean b) {
    367             direction = b;
    368         }
    369 
    370         /**
    371          * Returns the value of this Direction. True corresponds to UPSIDE.
    372          *
    373          * @return true if direction is UPSIDE; false otherwise
    374          */
    375         boolean getDirection () {
    376             return direction;
    377         }
    378     }
    379 }
    380