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 }