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 18 package org.apache.commons.math4.legacy.stat.descriptive.moment; 19 20 import org.apache.commons.math4.legacy.exception.MathIllegalArgumentException; 21 import org.apache.commons.math4.legacy.exception.NullArgumentException; 22 import org.apache.commons.math4.legacy.stat.descriptive.AbstractUnivariateStatistic; 23 import org.apache.commons.math4.legacy.core.MathArrays; 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} 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 * @since 2.1 51 */ 52 public class SemiVariance extends AbstractUnivariateStatistic { 53 54 /** 55 * The UPSIDE Direction is used to specify that the observations above the 56 * cutoff point will be used to calculate SemiVariance. 57 */ 58 public static final Direction UPSIDE_VARIANCE = Direction.UPSIDE; 59 60 /** 61 * The DOWNSIDE Direction is used to specify that the observations below. 62 * the cutoff point will be used to calculate SemiVariance 63 */ 64 public static final Direction DOWNSIDE_VARIANCE = Direction.DOWNSIDE; 65 66 /** 67 * Determines whether or not bias correction is applied when computing the 68 * value of the statistic. True means that bias is corrected. 69 */ 70 private boolean biasCorrected = true; 71 72 /** 73 * Determines whether to calculate downside or upside SemiVariance. 74 */ 75 private Direction varianceDirection = Direction.DOWNSIDE; 76 77 /** 78 * Constructs a SemiVariance with default (true) <code>biasCorrected</code> 79 * property and default (Downside) <code>varianceDirection</code> property. 80 */ 81 public SemiVariance() { 82 } 83 84 /** 85 * Constructs a SemiVariance with the specified <code>biasCorrected</code> 86 * property and default (Downside) <code>varianceDirection</code> property. 87 * 88 * @param biasCorrected setting for bias correction - true means 89 * bias will be corrected and is equivalent to using the "no arg" 90 * constructor 91 */ 92 public SemiVariance(final boolean biasCorrected) { 93 this.biasCorrected = biasCorrected; 94 } 95 96 /** 97 * Constructs a SemiVariance with the specified <code>Direction</code> property. 98 * and default (true) <code>biasCorrected</code> property 99 * 100 * @param direction setting for the direction of the SemiVariance 101 * to calculate 102 */ 103 public SemiVariance(final Direction direction) { 104 this.varianceDirection = direction; 105 } 106 107 /** 108 * Constructs a SemiVariance with the specified <code>isBiasCorrected</code> 109 * property and the specified <code>Direction</code> property. 110 * 111 * @param corrected setting for bias correction - true means 112 * bias will be corrected and is equivalent to using the "no arg" 113 * constructor 114 * 115 * @param direction setting for the direction of the SemiVariance 116 * to calculate 117 */ 118 public SemiVariance(final boolean corrected, final Direction direction) { 119 this.biasCorrected = corrected; 120 this.varianceDirection = direction; 121 } 122 123 /** 124 * Copy constructor, creates a new {@code SemiVariance} identical 125 * to the {@code original}. 126 * 127 * @param original the {@code SemiVariance} instance to copy 128 * @throws NullArgumentException if original is null 129 */ 130 public SemiVariance(final SemiVariance original) throws NullArgumentException { 131 copy(original, this); 132 } 133 134 /** 135 * {@inheritDoc} 136 */ 137 @Override 138 public SemiVariance copy() { 139 SemiVariance result = new SemiVariance(); 140 // No try-catch or advertised exception because args are guaranteed non-null 141 copy(this, result); 142 return result; 143 } 144 145 /** 146 * Copies source to dest. 147 * <p>Neither source nor dest can be null.</p> 148 * 149 * @param source SemiVariance to copy 150 * @param dest SemiVariance to copy to 151 * @throws NullArgumentException if either source or dest is null 152 */ 153 public static void copy(final SemiVariance source, SemiVariance dest) 154 throws NullArgumentException { 155 NullArgumentException.check(source); 156 NullArgumentException.check(dest); 157 dest.biasCorrected = source.biasCorrected; 158 dest.varianceDirection = source.varianceDirection; 159 } 160 161 /** 162 * <p>Returns the {@link SemiVariance} of the designated values against the mean, using 163 * instance properties varianceDirection and biasCorrection.</p> 164 * 165 * <p>Returns <code>NaN</code> if the array is empty and throws 166 * <code>IllegalArgumentException</code> if the array is null.</p> 167 * 168 * @param values the input array 169 * @param start index of the first array element to include 170 * @param length the number of elements to include 171 * @return the SemiVariance 172 * @throws MathIllegalArgumentException if the parameters are not valid 173 * 174 */ 175 @Override 176 public double evaluate(final double[] values, final int start, final int length) 177 throws MathIllegalArgumentException { 178 double m = (new Mean()).evaluate(values, start, length); 179 return evaluate(values, m, varianceDirection, biasCorrected, 0, values.length); 180 } 181 182 /** 183 * This method calculates {@link SemiVariance} for the entire array against the mean, using 184 * the current value of the biasCorrection instance property. 185 * 186 * @param values the input array 187 * @param direction the {@link Direction} of the semivariance 188 * @return the SemiVariance 189 * @throws MathIllegalArgumentException if values is null 190 * 191 */ 192 public double evaluate(final double[] values, Direction direction) 193 throws MathIllegalArgumentException { 194 double m = (new Mean()).evaluate(values); 195 return evaluate(values, m, direction, biasCorrected, 0, values.length); 196 } 197 198 /** 199 * <p>Returns the {@link SemiVariance} of the designated values against the cutoff, using 200 * instance properties variancDirection and biasCorrection.</p> 201 * 202 * <p>Returns <code>NaN</code> if the array is empty and throws 203 * <code>MathIllegalArgumentException</code> if the array is null.</p> 204 * 205 * @param values the input array 206 * @param cutoff the reference point 207 * @return the SemiVariance 208 * @throws MathIllegalArgumentException if values is null 209 */ 210 public double evaluate(final double[] values, final double cutoff) 211 throws MathIllegalArgumentException { 212 return evaluate(values, cutoff, varianceDirection, biasCorrected, 0, values.length); 213 } 214 215 /** 216 * <p>Returns the {@link SemiVariance} of the designated values against the cutoff in the 217 * given direction, using the current value of the biasCorrection instance property.</p> 218 * 219 * <p>Returns <code>NaN</code> if the array is empty and throws 220 * <code>MathIllegalArgumentException</code> if the array is null.</p> 221 * 222 * @param values the input array 223 * @param cutoff the reference point 224 * @param direction the {@link Direction} of the semivariance 225 * @return the SemiVariance 226 * @throws MathIllegalArgumentException if values is null 227 */ 228 public double evaluate(final double[] values, final double cutoff, final Direction direction) 229 throws MathIllegalArgumentException { 230 return evaluate(values, cutoff, direction, biasCorrected, 0, values.length); 231 } 232 233 /** 234 * <p>Returns the {@link SemiVariance} of the designated values against the cutoff 235 * in the given direction with the provided bias correction.</p> 236 * 237 * <p>Returns <code>NaN</code> if the array is empty and throws 238 * <code>IllegalArgumentException</code> if the array is null.</p> 239 * 240 * @param values the input array 241 * @param cutoff the reference point 242 * @param direction the {@link Direction} of the semivariance 243 * @param corrected the BiasCorrection flag 244 * @param start index of the first array element to include 245 * @param length the number of elements to include 246 * @return the SemiVariance 247 * @throws MathIllegalArgumentException if the parameters are not valid 248 * 249 */ 250 public double evaluate (final double[] values, final double cutoff, final Direction direction, 251 final boolean corrected, final int start, final int length) 252 throws MathIllegalArgumentException { 253 254 MathArrays.verifyValues(values, start, length); 255 if (values.length == 0) { 256 return Double.NaN; 257 } else { 258 if (values.length == 1) { 259 return 0.0; 260 } else { 261 final boolean booleanDirection = direction.getDirection(); 262 263 double dev = 0.0; 264 double sumsq = 0.0; 265 for (int i = start; i < length; i++) { 266 if ((values[i] > cutoff) == booleanDirection) { 267 dev = values[i] - cutoff; 268 sumsq += dev * dev; 269 } 270 } 271 272 if (corrected) { 273 return sumsq / (length - 1.0); 274 } else { 275 return sumsq / length; 276 } 277 } 278 } 279 } 280 281 /** 282 * Returns true iff biasCorrected property is set to true. 283 * 284 * @return the value of biasCorrected. 285 */ 286 public boolean isBiasCorrected() { 287 return biasCorrected; 288 } 289 290 /** 291 * Sets the biasCorrected property. 292 * 293 * @param biasCorrected new biasCorrected property value 294 */ 295 public void setBiasCorrected(boolean biasCorrected) { 296 this.biasCorrected = biasCorrected; 297 } 298 299 /** 300 * Returns the varianceDirection property. 301 * 302 * @return the varianceDirection 303 */ 304 public Direction getVarianceDirection () { 305 return varianceDirection; 306 } 307 308 /** 309 * Sets the variance direction. 310 * 311 * @param varianceDirection the direction of the semivariance 312 */ 313 public void setVarianceDirection(Direction varianceDirection) { 314 this.varianceDirection = varianceDirection; 315 } 316 317 /** 318 * The direction of the semivariance - either upside or downside. The direction 319 * is represented by boolean, with true corresponding to UPSIDE semivariance. 320 */ 321 public enum Direction { 322 /** 323 * The UPSIDE Direction is used to specify that the observations above the. 324 * cutoff point will be used to calculate SemiVariance 325 */ 326 UPSIDE (true), 327 328 /** 329 * The DOWNSIDE Direction is used to specify that the observations below. 330 * the cutoff point will be used to calculate SemiVariance 331 */ 332 DOWNSIDE (false); 333 334 /** 335 * boolean value UPSIDE <-> true. 336 */ 337 private boolean direction; 338 339 /** 340 * Create a Direction with the given value. 341 * 342 * @param b boolean value representing the Direction. True corresponds to UPSIDE. 343 */ 344 Direction (boolean b) { 345 direction = b; 346 } 347 348 /** 349 * Returns the value of this Direction. True corresponds to UPSIDE. 350 * 351 * @return true if direction is UPSIDE; false otherwise 352 */ 353 boolean getDirection () { 354 return direction; 355 } 356 } 357 }