1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package org.apache.commons.math.stat.descriptive.moment; 19 20import java.io.Serializable; 21import org.apache.commons.math.exception.NullArgumentException; 22import org.apache.commons.math.exception.util.LocalizedFormats; 23import org.apache.commons.math.stat.descriptive.AbstractUnivariateStatistic; 24 25/** 26 * <p>Computes the semivariance of a set of values with respect to a given cutoff value. 27 * We define the <i>downside semivariance</i> of a set of values <code>x</code> 28 * against the <i>cutoff value</i> <code>cutoff</code> to be <br/> 29 * <code>Σ (x[i] - target)<sup>2</sup> / df</code> <br/> 30 * where the sum is taken over all <code>i</code> such that <code>x[i] < cutoff</code> 31 * and <code>df</code> is the length of <code>x</code> (non-bias-corrected) or 32 * one less than this number (bias corrected). The <i>upside semivariance</i> 33 * is defined similarly, with the sum taken over values of <code>x</code> that 34 * exceed the cutoff value.</p> 35 * 36 * <p>The cutoff value defaults to the mean, bias correction defaults to <code>true</code> 37 * and the "variance direction" (upside or downside) defaults to downside. The variance direction 38 * and bias correction may be set using property setters or their values can provided as 39 * parameters to {@link #evaluate(double[], double, Direction, boolean, int, int)}.</p> 40 * 41 * <p>If the input array is null, <code>evaluate</code> methods throw 42 * <code>IllegalArgumentException.</code> If the array has length 1, <code>0</code> 43 * is returned, regardless of the value of the <code>cutoff.</code> 44 * 45 * <p><strong>Note that this class is not intended to be threadsafe.</strong> If 46 * multiple threads access an instance of this class concurrently, and one or 47 * more of these threads invoke property setters, external synchronization must 48 * be provided to ensure correct results.</p> 49 * 50 * @version $Revision: 1006299 $ $Date: 2010-10-10 16:47:17 +0200 (dim. 10 oct. 2010) $ 51 * @since 2.1 52 */ 53 54public class SemiVariance extends AbstractUnivariateStatistic implements Serializable { 55 56 /** 57 * The UPSIDE Direction is used to specify that the observations above the 58 * cutoff point will be used to calculate SemiVariance. 59 */ 60 public static final Direction UPSIDE_VARIANCE = Direction.UPSIDE; 61 62 /** 63 * The DOWNSIDE Direction is used to specify that the observations below 64 * the cutoff point will be used to calculate SemiVariance 65 */ 66 public static final Direction DOWNSIDE_VARIANCE = Direction.DOWNSIDE; 67 68 /** Serializable version identifier */ 69 private static final long serialVersionUID = -2653430366886024994L; 70 71 /** 72 * Determines whether or not bias correction is applied when computing the 73 * value of the statisic. True means that bias is corrected. 74 */ 75 private boolean biasCorrected = true; 76 77 /** 78 * Determines whether to calculate downside or upside SemiVariance. 79 */ 80 private Direction varianceDirection = Direction.DOWNSIDE; 81 82 /** 83 * Constructs a SemiVariance with default (true) <code>biasCorrected</code> 84 * property and default (Downside) <code>varianceDirection</code> property. 85 */ 86 public SemiVariance() { 87 } 88 89 /** 90 * Constructs a SemiVariance with the specified <code>biasCorrected</code> 91 * property and default (Downside) <code>varianceDirection</code> property. 92 * 93 * @param biasCorrected setting for bias correction - true means 94 * bias will be corrected and is equivalent to using the argumentless 95 * constructor 96 */ 97 public SemiVariance(final boolean biasCorrected) { 98 this.biasCorrected = biasCorrected; 99 } 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} 380