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 * https://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 /* 19 * This is not the original file distributed by the Apache Software Foundation 20 * It has been modified by the Hipparchus project 21 */ 22 23 package org.hipparchus.stat.descriptive.moment; 24 25 import java.io.Serializable; 26 27 import org.hipparchus.exception.MathIllegalArgumentException; 28 import org.hipparchus.exception.NullArgumentException; 29 import org.hipparchus.stat.StatUtils; 30 import org.hipparchus.stat.descriptive.AbstractUnivariateStatistic; 31 import org.hipparchus.util.MathArrays; 32 33 /** 34 * Computes the semivariance of a set of values with respect to a given cutoff value. 35 * <p> 36 * We define the <i>downside semivariance</i> of a set of values <code>x</code> 37 * against the <i>cutoff value</i> <code>cutoff</code> to be <br> 38 * <code>Σ (x[i] - target)<sup>2</sup> / df</code> <br> 39 * where the sum is taken over all <code>i</code> such that <code>x[i] < cutoff</code> 40 * and <code>df</code> is the length of <code>x</code> (non-bias-corrected) or 41 * one less than this number (bias corrected). The <i>upside semivariance</i> 42 * is defined similarly, with the sum taken over values of <code>x</code> that 43 * exceed the cutoff value. 44 * <p> 45 * The cutoff value defaults to the mean, bias correction defaults to <code>true</code> 46 * and the "variance direction" (upside or downside) defaults to downside. The variance direction 47 * and bias correction may be set using property setters or their values can provided as 48 * parameters to {@link #evaluate(double[], double, Direction, boolean, int, int)}. 49 * <p> 50 * If the input array is null, <code>evaluate</code> methods throw 51 * <code>IllegalArgumentException.</code> If the array has length 1, <code>0</code> 52 * is returned, regardless of the value of the <code>cutoff.</code> 53 * <p> 54 * <strong>Note that this class is not intended to be threadsafe.</strong> If 55 * multiple threads access an instance of this class concurrently, and one or 56 * more of these threads invoke property setters, external synchronization must 57 * be provided to ensure correct results. 58 */ 59 public class SemiVariance extends AbstractUnivariateStatistic implements Serializable { 60 61 /** 62 * The UPSIDE Direction is used to specify that the observations above the 63 * cutoff point will be used to calculate SemiVariance. 64 */ 65 public static final Direction UPSIDE_VARIANCE = Direction.UPSIDE; 66 67 /** 68 * The DOWNSIDE Direction is used to specify that the observations below 69 * the cutoff point will be used to calculate SemiVariance 70 */ 71 public static final Direction DOWNSIDE_VARIANCE = Direction.DOWNSIDE; 72 73 /** Serializable version identifier */ 74 private static final long serialVersionUID = 20150412L; 75 76 /** 77 * Determines whether or not bias correction is applied when computing the 78 * value of the statistic. True means that bias is corrected. 79 */ 80 private final boolean biasCorrected; 81 82 /** 83 * Determines whether to calculate downside or upside SemiVariance. 84 */ 85 private final Direction varianceDirection; 86 87 /** 88 * Constructs a SemiVariance with default (true) <code>biasCorrected</code> 89 * property and default (Downside) <code>varianceDirection</code> property. 90 */ 91 public SemiVariance() { 92 this(true, Direction.DOWNSIDE); 93 } 94 95 /** 96 * Constructs a SemiVariance with the specified <code>biasCorrected</code> 97 * property and default (Downside) <code>varianceDirection</code> property. 98 * 99 * @param biasCorrected setting for bias correction - true means 100 * bias will be corrected and is equivalent to using the argumentless 101 * constructor 102 */ 103 public SemiVariance(final boolean biasCorrected) { 104 this(biasCorrected, Direction.DOWNSIDE); 105 } 106 107 /** 108 * Constructs a SemiVariance with the specified <code>Direction</code> property 109 * and default (true) <code>biasCorrected</code> property 110 * 111 * @param direction setting for the direction of the SemiVariance 112 * to calculate 113 */ 114 public SemiVariance(final Direction direction) { 115 this(true, direction); 116 } 117 118 /** 119 * Constructs a SemiVariance with the specified <code>isBiasCorrected</code> 120 * property and the specified <code>Direction</code> property. 121 * 122 * @param corrected setting for bias correction - true means 123 * bias will be corrected and is equivalent to using the argumentless 124 * constructor 125 * 126 * @param direction setting for the direction of the SemiVariance 127 * to calculate 128 */ 129 public SemiVariance(final boolean corrected, final Direction direction) { 130 this.biasCorrected = corrected; 131 this.varianceDirection = direction; 132 } 133 134 /** 135 * Copy constructor, creates a new {@code SemiVariance} identical 136 * to the {@code original}. 137 * 138 * @param original the {@code SemiVariance} instance to copy 139 * @throws NullArgumentException if original is null 140 */ 141 public SemiVariance(final SemiVariance original) throws NullArgumentException { 142 super(original); 143 this.biasCorrected = original.biasCorrected; 144 this.varianceDirection = original.varianceDirection; 145 } 146 147 /** {@inheritDoc} */ 148 @Override 149 public SemiVariance copy() { 150 return new SemiVariance(this); 151 } 152 153 /** 154 * Returns the {@link SemiVariance} of the designated values against the mean, using 155 * instance properties varianceDirection and biasCorrection. 156 * <p> 157 * Returns <code>NaN</code> if the array is empty and throws 158 * <code>IllegalArgumentException</code> if the array is null. 159 * 160 * @param values the input array 161 * @param start index of the first array element to include 162 * @param length the number of elements to include 163 * @return the SemiVariance 164 * @throws MathIllegalArgumentException if the parameters are not valid 165 */ 166 @Override 167 public double evaluate(final double[] values, final int start, final int length) 168 throws MathIllegalArgumentException { 169 double m = StatUtils.mean(values, start, length); 170 return evaluate(values, m, varianceDirection, biasCorrected, start, length); 171 } 172 173 /** 174 * This method calculates {@link SemiVariance} for the entire array against the mean, 175 * using the current value of the biasCorrection instance property. 176 * 177 * @param values the input array 178 * @param direction the {@link Direction} of the semivariance 179 * @return the SemiVariance 180 * @throws MathIllegalArgumentException if values is null 181 */ 182 public double evaluate(final double[] values, Direction direction) 183 throws MathIllegalArgumentException { 184 double m = StatUtils.mean(values); 185 return evaluate(values, m, direction, biasCorrected, 0, values.length); 186 } 187 188 /** 189 * Returns the {@link SemiVariance} of the designated values against the cutoff, 190 * using instance properties variancDirection and biasCorrection. 191 * <p> 192 * Returns <code>NaN</code> if the array is empty. 193 * 194 * @param values the input array 195 * @param cutoff the reference point 196 * @return the SemiVariance 197 * @throws MathIllegalArgumentException if values is null 198 */ 199 public double evaluate(final double[] values, final double cutoff) 200 throws MathIllegalArgumentException { 201 return evaluate(values, cutoff, varianceDirection, biasCorrected, 0, values.length); 202 } 203 204 /** 205 * Returns the {@link SemiVariance} of the designated values against the cutoff in the 206 * given direction, using the current value of the biasCorrection instance property. 207 * <p> 208 * Returns <code>NaN</code> if the array is empty. 209 * 210 * @param values the input array 211 * @param cutoff the reference point 212 * @param direction the {@link Direction} of the semivariance 213 * @return the SemiVariance 214 * @throws MathIllegalArgumentException if values is null 215 */ 216 public double evaluate(final double[] values, final double cutoff, final Direction direction) 217 throws MathIllegalArgumentException { 218 return evaluate(values, cutoff, direction, biasCorrected, 0, values.length); 219 } 220 221 /** 222 * Returns the {@link SemiVariance} of the designated values against the cutoff 223 * in the given direction with the provided bias correction. 224 * <p> 225 * Returns <code>NaN</code> if the array is empty. 226 * 227 * @param values the input array 228 * @param cutoff the reference point 229 * @param direction the {@link Direction} of the semivariance 230 * @param corrected the BiasCorrection flag 231 * @param start index of the first array element to include 232 * @param length the number of elements to include 233 * @return the SemiVariance 234 * @throws MathIllegalArgumentException if the parameters are not valid 235 */ 236 public double evaluate(final double[] values, final double cutoff, final Direction direction, 237 final boolean corrected, final int start, final int length) 238 throws MathIllegalArgumentException { 239 240 MathArrays.verifyValues(values, start, length); 241 if (values.length == 0) { 242 return Double.NaN; 243 } else { 244 if (values.length == 1) { 245 return 0.0; 246 } else { 247 248 double sumsq = 0.0; 249 final int end = start + length; 250 for (int i = start; i < end; i++) { 251 if (direction.considerObservation(values[i], cutoff)) { 252 final double dev = values[i] - cutoff; 253 sumsq += dev * dev; 254 } 255 } 256 257 if (corrected) { 258 return sumsq / (length - 1.0); 259 } else { 260 return sumsq / length; 261 } 262 } 263 } 264 } 265 266 /** 267 * Returns true iff biasCorrected property is set to true. 268 * 269 * @return the value of biasCorrected. 270 */ 271 public boolean isBiasCorrected() { 272 return biasCorrected; 273 } 274 275 /** 276 * Returns a copy of this instance with the given biasCorrected setting. 277 * 278 * @param isBiasCorrected new biasCorrected property value 279 * @return a copy of this instance with the given bias correction setting 280 */ 281 public SemiVariance withBiasCorrected(boolean isBiasCorrected) { 282 return new SemiVariance(isBiasCorrected, this.varianceDirection); 283 } 284 285 /** 286 * Returns the varianceDirection property. 287 * 288 * @return the varianceDirection 289 */ 290 public Direction getVarianceDirection () { 291 return varianceDirection; 292 } 293 294 /** 295 * Returns a copy of this instance with the given direction setting. 296 * 297 * @param direction the direction of the semivariance 298 * @return a copy of this instance with the given direction setting 299 */ 300 public SemiVariance withVarianceDirection(Direction direction) { 301 return new SemiVariance(this.biasCorrected, direction); 302 } 303 304 /** 305 * The direction of the semivariance - either upside or downside. The direction 306 * is represented by boolean, with true corresponding to UPSIDE semivariance. 307 */ 308 public enum Direction { 309 /** 310 * The UPSIDE Direction is used to specify that the observations above the 311 * cutoff point will be used to calculate SemiVariance 312 */ 313 UPSIDE (true), 314 315 /** 316 * The DOWNSIDE Direction is used to specify that the observations below 317 * the cutoff point will be used to calculate SemiVariance 318 */ 319 DOWNSIDE (false); 320 321 /** 322 * boolean value UPSIDE <-> true 323 */ 324 private final boolean direction; 325 326 /** 327 * Create a Direction with the given value. 328 * 329 * @param b boolean value representing the Direction. True corresponds to UPSIDE. 330 */ 331 Direction (boolean b) { 332 direction = b; 333 } 334 335 /** Check if observation should be considered. 336 * @param value observation value 337 * @param cutoff cutoff point 338 * @return true if observation should be considered. 339 * @since 1.4 340 */ 341 boolean considerObservation(final double value, final double cutoff) { 342 return value > cutoff == direction; 343 } 344 345 } 346 }