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