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.exception.NullArgumentException;
022    import org.apache.commons.math.exception.util.LocalizedFormats;
023    import org.apache.commons.math.stat.descriptive.AbstractUnivariateStatistic;
024    
025    /**
026     * <p>Computes the semivariance of a set of values with respect to a given cutoff value.
027     * We define the <i>downside semivariance</i> of a set of values <code>x</code>
028     * against the <i>cutoff value</i> <code>cutoff</code> to be <br/>
029     * <code>&Sigma; (x[i] - target)<sup>2</sup> / df</code> <br/>
030     * where the sum is taken over all <code>i</code> such that <code>x[i] < cutoff</code>
031     * and <code>df</code> is the length of <code>x</code> (non-bias-corrected) or
032     * one less than this number (bias corrected).  The <i>upside semivariance</i>
033     * is defined similarly, with the sum taken over values of <code>x</code> that
034     * exceed the cutoff value.</p>
035     *
036     * <p>The cutoff value defaults to the mean, bias correction defaults to <code>true</code>
037     * and the "variance direction" (upside or downside) defaults to downside.  The variance direction
038     * and bias correction may be set using property setters or their values can provided as
039     * parameters to {@link #evaluate(double[], double, Direction, boolean, int, int)}.</p>
040     *
041     * <p>If the input array is null, <code>evaluate</code> methods throw
042     * <code>IllegalArgumentException.</code>  If the array has length 1, <code>0</code>
043     * is returned, regardless of the value of the <code>cutoff.</code>
044     *
045     * <p><strong>Note that this class is not intended to be threadsafe.</strong> If
046     * multiple threads access an instance of this class concurrently, and one or
047     * more of these threads invoke property setters, external synchronization must
048     * be provided to ensure correct results.</p>
049     *
050     * @version $Revision: 1006299 $ $Date: 2010-10-10 16:47:17 +0200 (dim. 10 oct. 2010) $
051     * @since 2.1
052     */
053    
054    public class SemiVariance extends AbstractUnivariateStatistic implements Serializable {
055    
056        /**
057         * The UPSIDE Direction is used to specify that the observations above the
058         * cutoff point will be used to calculate SemiVariance.
059         */
060        public static final Direction UPSIDE_VARIANCE = Direction.UPSIDE;
061    
062        /**
063         * The DOWNSIDE Direction is used to specify that the observations below
064         * the cutoff point will be used to calculate SemiVariance
065         */
066        public static final Direction DOWNSIDE_VARIANCE = Direction.DOWNSIDE;
067    
068        /** Serializable version identifier */
069        private static final long serialVersionUID = -2653430366886024994L;
070    
071        /**
072         * Determines whether or not bias correction is applied when computing the
073         * value of the statisic.  True means that bias is corrected.
074         */
075        private boolean biasCorrected = true;
076    
077        /**
078         * Determines whether to calculate downside or upside SemiVariance.
079         */
080        private Direction varianceDirection = Direction.DOWNSIDE;
081    
082        /**
083         * Constructs a SemiVariance with default (true) <code>biasCorrected</code>
084         * property and default (Downside) <code>varianceDirection</code> property.
085         */
086        public SemiVariance() {
087        }
088    
089        /**
090         * Constructs a SemiVariance with the specified <code>biasCorrected</code>
091         * property and default (Downside) <code>varianceDirection</code> property.
092         *
093         * @param biasCorrected  setting for bias correction - true means
094         * bias will be corrected and is equivalent to using the argumentless
095         * constructor
096         */
097        public SemiVariance(final boolean biasCorrected) {
098            this.biasCorrected = biasCorrected;
099        }
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    }