001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    package org.apache.commons.math.stat.descriptive.moment;
019    
020    import java.io.Serializable;
021    import org.apache.commons.math.MathRuntimeException;
022    import org.apache.commons.math.stat.descriptive.AbstractUnivariateStatistic;
023    
024    /**
025     * <p>Computes the semivariance of a set of values with respect to a given cutoff value.
026     * We define the <i>downside semivariance</i> of a set of values <code>x</code>
027     * against the <i>cutoff value</i> <code>cutoff</code> to be <br/>
028     * <code>&Sigma; (x[i] - target)<sup>2</sup> / df</code> <br/>
029     * where the sum is taken over all <code>i</code> such that <code>x[i] < cutoff</code>
030     * and <code>df</code> is the length of <code>x</code> (non-bias-corrected) or
031     * one less than this number (bias corrected).  The <i>upside semivariance</i>
032     * is defined similarly, with the sum taken over values of <code>x</code> that
033     * exceed the cutoff value.</p>
034     *
035     * <p>The cutoff value defaults to the mean, bias correction defaults to <code>true</code>
036     * and the "variance direction" (upside or downside) defaults to downside.  The variance direction
037     * and bias correction may be set using property setters or their values can provided as
038     * parameters to {@link #evaluate(double[], double, Direction, boolean, int, int)}.</p>
039     *
040     * <p>If the input array is null, <code>evaluate</code> methods throw
041     * <code>IllegalArgumentException.</code>  If the array has length 1, <code>0</code>
042     * is returned, regardless of the value of the <code>cutoff.</code>
043     *
044     * <p><strong>Note that this class is not intended to be threadsafe.</strong> If
045     * multiple threads access an instance of this class concurrently, and one or
046     * more of these threads invoke property setters, external synchronization must
047     * be provided to ensure correct results.</p>
048     *
049     * @version $Revision: 917275 $ $Date: 2010-02-28 14:43:11 -0500 (Sun, 28 Feb 2010) $
050     * @since 2.1
051     */
052    
053    public class SemiVariance extends AbstractUnivariateStatistic implements Serializable {
054    
055        /**
056         * The UPSIDE Direction is used to specify that the observations above the
057         * cutoff point will be used to calculate SemiVariance.
058         */
059        public static final Direction UPSIDE_VARIANCE = Direction.UPSIDE;
060    
061        /**
062         * The DOWNSIDE Direction is used to specify that the observations below
063         * the cutoff point will be used to calculate SemiVariance
064         */
065        public static final Direction DOWNSIDE_VARIANCE = Direction.DOWNSIDE;
066    
067        /** Serializable version identifier */
068        private static final long serialVersionUID = -2653430366886024994L;
069    
070        /**
071         * Determines whether or not bias correction is applied when computing the
072         * value of the statisic.  True means that bias is corrected.
073         */
074        private boolean biasCorrected = true;
075    
076        /**
077         * Determines whether to calculate downside or upside SemiVariance.
078         */
079        private Direction varianceDirection = Direction.DOWNSIDE;
080    
081        /**
082         * Constructs a SemiVariance with default (true) <code>biasCorrected</code>
083         * property and default (Downside) <code>varianceDirection</code> property.
084         */
085        public SemiVariance() {
086        }
087    
088        /**
089         * Constructs a SemiVariance with the specified <code>biasCorrected</code>
090         * property and default (Downside) <code>varianceDirection</code> property.
091         *
092         * @param biasCorrected  setting for bias correction - true means
093         * bias will be corrected and is equivalent to using the argumentless
094         * constructor
095         */
096        public SemiVariance(final boolean biasCorrected) {
097            this.biasCorrected = biasCorrected;
098        }
099    
100    
101        /**
102         * Constructs a SemiVariance with the specified <code>Direction</code> property
103         * and default (true) <code>biasCorrected</code> property
104         *
105         * @param direction  setting for the direction of the SemiVariance
106         * to calculate
107         */
108        public SemiVariance(final Direction direction) {
109            this.varianceDirection = direction;
110        }
111    
112    
113        /**
114         * Constructs a SemiVariance with the specified <code>isBiasCorrected</code>
115         * property and the specified <code>Direction</code> property.
116         *
117         * @param corrected  setting for bias correction - true means
118         * bias will be corrected and is equivalent to using the argumentless
119         * constructor
120         *
121         * @param direction  setting for the direction of the SemiVariance
122         * to calculate
123         */
124        public SemiVariance(final boolean corrected, final Direction direction) {
125            this.biasCorrected = corrected;
126            this.varianceDirection = direction;
127        }
128    
129    
130        /**
131         * Copy constructor, creates a new {@code SemiVariance} identical
132         * to the {@code original}
133         *
134         * @param original the {@code SemiVariance} instance to copy
135         */
136        public SemiVariance(final SemiVariance original) {
137            copy(original, this);
138        }
139    
140    
141        /**
142         * {@inheritDoc}
143         */
144        @Override
145        public SemiVariance copy() {
146            SemiVariance result = new SemiVariance();
147            copy(this, result);
148            return result;
149        }
150    
151    
152        /**
153         * Copies source to dest.
154         * <p>Neither source nor dest can be null.</p>
155         *
156         * @param source SemiVariance to copy
157         * @param dest SemiVariance to copy to
158         * @throws NullPointerException if either source or dest is null
159         */
160        public static void copy(final SemiVariance source, SemiVariance dest) {
161            dest.biasCorrected = source.biasCorrected;
162            dest.varianceDirection = source.varianceDirection;
163        }
164    
165    
166        /**
167         * This method calculates {@link SemiVariance} for the entire array against the mean, using
168         * instance properties varianceDirection and biasCorrection.
169         *
170         * @param values the input array
171         * @return the SemiVariance
172         * @throws IllegalArgumentException if values is null
173         *
174         */
175        @Override
176        public double evaluate(final double[] values) {
177            if (values == null) {
178                throw MathRuntimeException.createIllegalArgumentException("input values array is null");
179             }
180            return evaluate(values, 0, values.length);
181        }
182    
183    
184        /**
185          * <p>Returns the {@link SemiVariance} of the designated values against the mean, using
186          * instance properties varianceDirection and biasCorrection.</p>
187          *
188          * <p>Returns <code>NaN</code> if the array is empty and throws
189          * <code>IllegalArgumentException</code> if the array is null.</p>
190          *
191          * @param values the input array
192          * @param start index of the first array element to include
193          * @param length the number of elements to include
194          * @return the SemiVariance
195          * @throws IllegalArgumentException if the parameters are not valid
196          *
197          */
198          @Override
199          public double evaluate(final double[] values, final int start, final int length) {
200            double m = (new Mean()).evaluate(values, start, length);
201            return evaluate(values, m, varianceDirection, biasCorrected, 0, values.length);
202          }
203    
204    
205          /**
206           * This method calculates {@link SemiVariance} for the entire array against the mean, using
207           * the current value of the biasCorrection instance property.
208           *
209           * @param values the input array
210           * @param direction the {@link Direction} of the semivariance
211           * @return the SemiVariance
212           * @throws IllegalArgumentException if values is null
213           *
214           */
215          public double evaluate(final double[] values, Direction direction) {
216              double m = (new Mean()).evaluate(values);
217              return evaluate (values, m, direction, biasCorrected, 0, values.length);
218          }
219    
220          /**
221           * <p>Returns the {@link SemiVariance} of the designated values against the cutoff, using
222           * instance properties variancDirection and biasCorrection.</p>
223           *
224           * <p>Returns <code>NaN</code> if the array is empty and throws
225           * <code>IllegalArgumentException</code> if the array is null.</p>
226           *
227           * @param values the input array
228           * @param cutoff the reference point
229           * @return the SemiVariance
230           * @throws IllegalArgumentException if values is null
231           */
232          public double evaluate(final double[] values, final double cutoff) {
233              return evaluate(values, cutoff, varianceDirection, biasCorrected, 0, values.length);
234          }
235    
236          /**
237           * <p>Returns the {@link SemiVariance} of the designated values against the cutoff in the
238           * given direction, using the current value of the biasCorrection instance property.</p>
239           *
240           * <p>Returns <code>NaN</code> if the array is empty and throws
241           * <code>IllegalArgumentException</code> if the array is null.</p>
242           *
243           * @param values the input array
244           * @param cutoff the reference point
245           * @param direction the {@link Direction} of the semivariance
246           * @return the SemiVariance
247           * @throws IllegalArgumentException if values is null
248           */
249          public double evaluate(final double[] values, final double cutoff, final Direction direction) {
250              return evaluate(values, cutoff, direction, biasCorrected, 0, values.length);
251          }
252    
253    
254         /**
255          * <p>Returns the {@link SemiVariance} of the designated values against the cutoff
256          * in the given direction with the provided bias correction.</p>
257          *
258          * <p>Returns <code>NaN</code> if the array is empty and throws
259          * <code>IllegalArgumentException</code> if the array is null.</p>
260          *
261          * @param values the input array
262          * @param cutoff the reference point
263          * @param direction the {@link Direction} of the semivariance
264          * @param corrected the BiasCorrection flag
265          * @param start index of the first array element to include
266          * @param length the number of elements to include
267          * @return the SemiVariance
268          * @throws IllegalArgumentException if the parameters are not valid
269          *
270          */
271        public double evaluate (final double[] values, final double cutoff, final Direction direction,
272                final boolean corrected, final int start, final int length) {
273    
274            test(values, start, length);
275            if (values.length == 0) {
276                return Double.NaN;
277            } else {
278                if (values.length == 1) {
279                    return 0.0;
280                } else {
281                    final boolean booleanDirection = direction.getDirection();
282    
283                    double dev = 0.0;
284                    double sumsq = 0.0;
285                    for (int i = start; i < length; i++) {
286                        if ((values[i] > cutoff) == booleanDirection) {
287                           dev = values[i] - cutoff;
288                           sumsq += dev * dev;
289                        }
290                    }
291    
292                    if (corrected) {
293                        return sumsq / (length - 1.0);
294                    } else {
295                        return sumsq / length;
296                    }
297                }
298            }
299        }
300    
301        /**
302         * Returns true iff biasCorrected property is set to true.
303         *
304         * @return the value of biasCorrected.
305         */
306        public boolean isBiasCorrected() {
307            return biasCorrected;
308        }
309    
310        /**
311         * Sets the biasCorrected property.
312         *
313         * @param biasCorrected new biasCorrected property value
314         */
315        public void setBiasCorrected(boolean biasCorrected) {
316            this.biasCorrected = biasCorrected;
317        }
318    
319        /**
320         * Returns the varianceDirection property.
321         *
322         * @return the varianceDirection
323         */
324        public Direction getVarianceDirection () {
325            return varianceDirection;
326        }
327    
328        /**
329         * Sets the variance direction
330         *
331         * @param varianceDirection the direction of the semivariance
332         */
333        public void setVarianceDirection(Direction varianceDirection) {
334            this.varianceDirection = varianceDirection;
335        }
336    
337        /**
338         * The direction of the semivariance - either upside or downside. The direction
339         * is represented by boolean, with true corresponding to UPSIDE semivariance.
340         */
341        public enum Direction {
342            /**
343             * The UPSIDE Direction is used to specify that the observations above the
344             * cutoff point will be used to calculate SemiVariance
345             */
346            UPSIDE (true),
347    
348            /**
349             * The DOWNSIDE Direction is used to specify that the observations below
350             * the cutoff point will be used to calculate SemiVariance
351             */
352            DOWNSIDE (false);
353    
354            /**
355             *   boolean value  UPSIDE <-> true
356             */
357            private boolean direction;
358    
359            /**
360             * Create a Direction with the given value.
361             *
362             * @param b boolean value representing the Direction. True corresponds to UPSIDE.
363             */
364            Direction (boolean b) {
365                direction = b;
366            }
367    
368            /**
369             * Returns the value of this Direction. True corresponds to UPSIDE.
370             *
371             * @return true if direction is UPSIDE; false otherwise
372             */
373            boolean getDirection () {
374                return direction;
375            }
376        }
377    }