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