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