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 018package org.apache.commons.math3.stat.descriptive.moment; 019 020import java.io.Serializable; 021 022import org.apache.commons.math3.exception.MathIllegalArgumentException; 023import org.apache.commons.math3.exception.NullArgumentException; 024import org.apache.commons.math3.stat.descriptive.AbstractUnivariateStatistic; 025import 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 */ 054public 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 * @throws NullArgumentException if original is null 137 */ 138 public SemiVariance(final SemiVariance original) throws NullArgumentException { 139 copy(original, this); 140 } 141 142 143 /** 144 * {@inheritDoc} 145 */ 146 @Override 147 public SemiVariance copy() { 148 SemiVariance result = new SemiVariance(); 149 // No try-catch or advertised exception because args are guaranteed non-null 150 copy(this, result); 151 return result; 152 } 153 154 155 /** 156 * Copies source to dest. 157 * <p>Neither source nor dest can be null.</p> 158 * 159 * @param source SemiVariance to copy 160 * @param dest SemiVariance to copy to 161 * @throws NullArgumentException if either source or dest is null 162 */ 163 public static void copy(final SemiVariance source, SemiVariance dest) 164 throws NullArgumentException { 165 MathUtils.checkNotNull(source); 166 MathUtils.checkNotNull(dest); 167 dest.setData(source.getDataRef()); 168 dest.biasCorrected = source.biasCorrected; 169 dest.varianceDirection = source.varianceDirection; 170 } 171 172 /** 173 * <p>Returns the {@link SemiVariance} of the designated values against the mean, using 174 * instance properties varianceDirection and biasCorrection.</p> 175 * 176 * <p>Returns <code>NaN</code> if the array is empty and throws 177 * <code>IllegalArgumentException</code> if the array is null.</p> 178 * 179 * @param values the input array 180 * @param start index of the first array element to include 181 * @param length the number of elements to include 182 * @return the SemiVariance 183 * @throws MathIllegalArgumentException if the parameters are not valid 184 * 185 */ 186 @Override 187 public double evaluate(final double[] values, final int start, final int length) 188 throws MathIllegalArgumentException { 189 double m = (new Mean()).evaluate(values, start, length); 190 return evaluate(values, m, varianceDirection, biasCorrected, 0, values.length); 191 } 192 193 194 /** 195 * This method calculates {@link SemiVariance} for the entire array against the mean, using 196 * the current value of the biasCorrection instance property. 197 * 198 * @param values the input array 199 * @param direction the {@link Direction} of the semivariance 200 * @return the SemiVariance 201 * @throws MathIllegalArgumentException if values is null 202 * 203 */ 204 public double evaluate(final double[] values, Direction direction) 205 throws MathIllegalArgumentException { 206 double m = (new Mean()).evaluate(values); 207 return evaluate (values, m, direction, biasCorrected, 0, values.length); 208 } 209 210 /** 211 * <p>Returns the {@link SemiVariance} of the designated values against the cutoff, using 212 * instance properties variancDirection and biasCorrection.</p> 213 * 214 * <p>Returns <code>NaN</code> if the array is empty and throws 215 * <code>MathIllegalArgumentException</code> if the array is null.</p> 216 * 217 * @param values the input array 218 * @param cutoff the reference point 219 * @return the SemiVariance 220 * @throws MathIllegalArgumentException if values is null 221 */ 222 public double evaluate(final double[] values, final double cutoff) 223 throws MathIllegalArgumentException { 224 return evaluate(values, cutoff, varianceDirection, biasCorrected, 0, values.length); 225 } 226 227 /** 228 * <p>Returns the {@link SemiVariance} of the designated values against the cutoff in the 229 * given direction, using the current value of the biasCorrection instance property.</p> 230 * 231 * <p>Returns <code>NaN</code> if the array is empty and throws 232 * <code>MathIllegalArgumentException</code> if the array is null.</p> 233 * 234 * @param values the input array 235 * @param cutoff the reference point 236 * @param direction the {@link Direction} of the semivariance 237 * @return the SemiVariance 238 * @throws MathIllegalArgumentException if values is null 239 */ 240 public double evaluate(final double[] values, final double cutoff, final Direction direction) 241 throws MathIllegalArgumentException { 242 return evaluate(values, cutoff, direction, biasCorrected, 0, values.length); 243 } 244 245 246 /** 247 * <p>Returns the {@link SemiVariance} of the designated values against the cutoff 248 * in the given direction with the provided bias correction.</p> 249 * 250 * <p>Returns <code>NaN</code> if the array is empty and throws 251 * <code>IllegalArgumentException</code> if the array is null.</p> 252 * 253 * @param values the input array 254 * @param cutoff the reference point 255 * @param direction the {@link Direction} of the semivariance 256 * @param corrected the BiasCorrection flag 257 * @param start index of the first array element to include 258 * @param length the number of elements to include 259 * @return the SemiVariance 260 * @throws MathIllegalArgumentException if the parameters are not valid 261 * 262 */ 263 public double evaluate (final double[] values, final double cutoff, final Direction direction, 264 final boolean corrected, final int start, final int length) throws MathIllegalArgumentException { 265 266 test(values, start, length); 267 if (values.length == 0) { 268 return Double.NaN; 269 } else { 270 if (values.length == 1) { 271 return 0.0; 272 } else { 273 final boolean booleanDirection = direction.getDirection(); 274 275 double dev = 0.0; 276 double sumsq = 0.0; 277 for (int i = start; i < length; i++) { 278 if ((values[i] > cutoff) == booleanDirection) { 279 dev = values[i] - cutoff; 280 sumsq += dev * dev; 281 } 282 } 283 284 if (corrected) { 285 return sumsq / (length - 1.0); 286 } else { 287 return sumsq / length; 288 } 289 } 290 } 291 } 292 293 /** 294 * Returns true iff biasCorrected property is set to true. 295 * 296 * @return the value of biasCorrected. 297 */ 298 public boolean isBiasCorrected() { 299 return biasCorrected; 300 } 301 302 /** 303 * Sets the biasCorrected property. 304 * 305 * @param biasCorrected new biasCorrected property value 306 */ 307 public void setBiasCorrected(boolean biasCorrected) { 308 this.biasCorrected = biasCorrected; 309 } 310 311 /** 312 * Returns the varianceDirection property. 313 * 314 * @return the varianceDirection 315 */ 316 public Direction getVarianceDirection () { 317 return varianceDirection; 318 } 319 320 /** 321 * Sets the variance direction 322 * 323 * @param varianceDirection the direction of the semivariance 324 */ 325 public void setVarianceDirection(Direction varianceDirection) { 326 this.varianceDirection = varianceDirection; 327 } 328 329 /** 330 * The direction of the semivariance - either upside or downside. The direction 331 * is represented by boolean, with true corresponding to UPSIDE semivariance. 332 */ 333 public enum Direction { 334 /** 335 * The UPSIDE Direction is used to specify that the observations above the 336 * cutoff point will be used to calculate SemiVariance 337 */ 338 UPSIDE (true), 339 340 /** 341 * The DOWNSIDE Direction is used to specify that the observations below 342 * the cutoff point will be used to calculate SemiVariance 343 */ 344 DOWNSIDE (false); 345 346 /** 347 * boolean value UPSIDE <-> true 348 */ 349 private boolean direction; 350 351 /** 352 * Create a Direction with the given value. 353 * 354 * @param b boolean value representing the Direction. True corresponds to UPSIDE. 355 */ 356 Direction (boolean b) { 357 direction = b; 358 } 359 360 /** 361 * Returns the value of this Direction. True corresponds to UPSIDE. 362 * 363 * @return true if direction is UPSIDE; false otherwise 364 */ 365 boolean getDirection () { 366 return direction; 367 } 368 } 369}