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>Σ (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 }