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.math4.legacy.stat.descriptive.moment; 019 020import org.apache.commons.math4.legacy.exception.MathIllegalArgumentException; 021import org.apache.commons.math4.legacy.exception.NullArgumentException; 022import org.apache.commons.math4.legacy.stat.descriptive.AbstractUnivariateStatistic; 023import org.apache.commons.math4.legacy.core.MathArrays; 024 025/** 026 * <p>Computes the semivariance of a set of values with respect to a given cutoff value. 027 * We define the <i>downside semivariance</i> of a set of values <code>x</code> 028 * against the <i>cutoff value</i> <code>cutoff</code> to be <br> 029 * <code>Σ (x[i] - target)<sup>2</sup> / df</code> <br> 030 * where the sum is taken over all <code>i</code> such that {@code x[i] < cutoff} 031 * and <code>df</code> is the length of <code>x</code> (non-bias-corrected) or 032 * one less than this number (bias corrected). The <i>upside semivariance</i> 033 * is defined similarly, with the sum taken over values of <code>x</code> that 034 * exceed the cutoff value.</p> 035 * 036 * <p>The cutoff value defaults to the mean, bias correction defaults to <code>true</code> 037 * and the "variance direction" (upside or downside) defaults to downside. The variance direction 038 * and bias correction may be set using property setters or their values can provided as 039 * parameters to {@link #evaluate(double[], double, Direction, boolean, int, int)}.</p> 040 * 041 * <p>If the input array is null, <code>evaluate</code> methods throw 042 * <code>IllegalArgumentException.</code> If the array has length 1, <code>0</code> 043 * is returned, regardless of the value of the <code>cutoff.</code> 044 * 045 * <p><strong>Note that this class is not intended to be threadsafe.</strong> If 046 * multiple threads access an instance of this class concurrently, and one or 047 * more of these threads invoke property setters, external synchronization must 048 * be provided to ensure correct results.</p> 049 * 050 * @since 2.1 051 */ 052public class SemiVariance extends AbstractUnivariateStatistic { 053 054 /** 055 * The UPSIDE Direction is used to specify that the observations above the 056 * cutoff point will be used to calculate SemiVariance. 057 */ 058 public static final Direction UPSIDE_VARIANCE = Direction.UPSIDE; 059 060 /** 061 * The DOWNSIDE Direction is used to specify that the observations below. 062 * the cutoff point will be used to calculate SemiVariance 063 */ 064 public static final Direction DOWNSIDE_VARIANCE = Direction.DOWNSIDE; 065 066 /** 067 * Determines whether or not bias correction is applied when computing the 068 * value of the statistic. True means that bias is corrected. 069 */ 070 private boolean biasCorrected = true; 071 072 /** 073 * Determines whether to calculate downside or upside SemiVariance. 074 */ 075 private Direction varianceDirection = Direction.DOWNSIDE; 076 077 /** 078 * Constructs a SemiVariance with default (true) <code>biasCorrected</code> 079 * property and default (Downside) <code>varianceDirection</code> property. 080 */ 081 public SemiVariance() { 082 } 083 084 /** 085 * Constructs a SemiVariance with the specified <code>biasCorrected</code> 086 * property and default (Downside) <code>varianceDirection</code> property. 087 * 088 * @param biasCorrected setting for bias correction - true means 089 * bias will be corrected and is equivalent to using the "no arg" 090 * constructor 091 */ 092 public SemiVariance(final boolean biasCorrected) { 093 this.biasCorrected = biasCorrected; 094 } 095 096 /** 097 * Constructs a SemiVariance with the specified <code>Direction</code> property. 098 * and default (true) <code>biasCorrected</code> property 099 * 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}