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
18package org.apache.commons.math.stat.descriptive.moment;
19
20import java.io.Serializable;
21import org.apache.commons.math.exception.NullArgumentException;
22import org.apache.commons.math.exception.util.LocalizedFormats;
23import 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
54public 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