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 }