SemiVariance.java
- /*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package org.apache.commons.math4.legacy.stat.descriptive.moment;
- import org.apache.commons.math4.legacy.exception.MathIllegalArgumentException;
- import org.apache.commons.math4.legacy.exception.NullArgumentException;
- import org.apache.commons.math4.legacy.stat.descriptive.AbstractUnivariateStatistic;
- import org.apache.commons.math4.legacy.core.MathArrays;
- /**
- * <p>Computes the semivariance of a set of values with respect to a given cutoff value.
- * We define the <i>downside semivariance</i> of a set of values <code>x</code>
- * against the <i>cutoff value</i> <code>cutoff</code> to be <br>
- * <code>Σ (x[i] - target)<sup>2</sup> / df</code> <br>
- * where the sum is taken over all <code>i</code> such that {@code x[i] < cutoff}
- * and <code>df</code> is the length of <code>x</code> (non-bias-corrected) or
- * one less than this number (bias corrected). The <i>upside semivariance</i>
- * is defined similarly, with the sum taken over values of <code>x</code> that
- * exceed the cutoff value.</p>
- *
- * <p>The cutoff value defaults to the mean, bias correction defaults to <code>true</code>
- * and the "variance direction" (upside or downside) defaults to downside. The variance direction
- * and bias correction may be set using property setters or their values can provided as
- * parameters to {@link #evaluate(double[], double, Direction, boolean, int, int)}.</p>
- *
- * <p>If the input array is null, <code>evaluate</code> methods throw
- * <code>IllegalArgumentException.</code> If the array has length 1, <code>0</code>
- * is returned, regardless of the value of the <code>cutoff.</code>
- *
- * <p><strong>Note that this class is not intended to be threadsafe.</strong> If
- * multiple threads access an instance of this class concurrently, and one or
- * more of these threads invoke property setters, external synchronization must
- * be provided to ensure correct results.</p>
- *
- * @since 2.1
- */
- public class SemiVariance extends AbstractUnivariateStatistic {
- /**
- * The UPSIDE Direction is used to specify that the observations above the
- * cutoff point will be used to calculate SemiVariance.
- */
- public static final Direction UPSIDE_VARIANCE = Direction.UPSIDE;
- /**
- * The DOWNSIDE Direction is used to specify that the observations below.
- * the cutoff point will be used to calculate SemiVariance
- */
- public static final Direction DOWNSIDE_VARIANCE = Direction.DOWNSIDE;
- /**
- * Determines whether or not bias correction is applied when computing the
- * value of the statistic. True means that bias is corrected.
- */
- private boolean biasCorrected = true;
- /**
- * Determines whether to calculate downside or upside SemiVariance.
- */
- private Direction varianceDirection = Direction.DOWNSIDE;
- /**
- * Constructs a SemiVariance with default (true) <code>biasCorrected</code>
- * property and default (Downside) <code>varianceDirection</code> property.
- */
- public SemiVariance() {
- }
- /**
- * Constructs a SemiVariance with the specified <code>biasCorrected</code>
- * property and default (Downside) <code>varianceDirection</code> property.
- *
- * @param biasCorrected setting for bias correction - true means
- * bias will be corrected and is equivalent to using the "no arg"
- * constructor
- */
- public SemiVariance(final boolean biasCorrected) {
- this.biasCorrected = biasCorrected;
- }
- /**
- * Constructs a SemiVariance with the specified <code>Direction</code> property.
- * and default (true) <code>biasCorrected</code> property
- *
- * @param direction setting for the direction of the SemiVariance
- * to calculate
- */
- public SemiVariance(final Direction direction) {
- this.varianceDirection = direction;
- }
- /**
- * Constructs a SemiVariance with the specified <code>isBiasCorrected</code>
- * property and the specified <code>Direction</code> property.
- *
- * @param corrected setting for bias correction - true means
- * bias will be corrected and is equivalent to using the "no arg"
- * constructor
- *
- * @param direction setting for the direction of the SemiVariance
- * to calculate
- */
- public SemiVariance(final boolean corrected, final Direction direction) {
- this.biasCorrected = corrected;
- this.varianceDirection = direction;
- }
- /**
- * Copy constructor, creates a new {@code SemiVariance} identical
- * to the {@code original}.
- *
- * @param original the {@code SemiVariance} instance to copy
- * @throws NullArgumentException if original is null
- */
- public SemiVariance(final SemiVariance original) throws NullArgumentException {
- copy(original, this);
- }
- /**
- * {@inheritDoc}
- */
- @Override
- public SemiVariance copy() {
- SemiVariance result = new SemiVariance();
- // No try-catch or advertised exception because args are guaranteed non-null
- copy(this, result);
- return result;
- }
- /**
- * Copies source to dest.
- * <p>Neither source nor dest can be null.</p>
- *
- * @param source SemiVariance to copy
- * @param dest SemiVariance to copy to
- * @throws NullArgumentException if either source or dest is null
- */
- public static void copy(final SemiVariance source, SemiVariance dest)
- throws NullArgumentException {
- NullArgumentException.check(source);
- NullArgumentException.check(dest);
- dest.biasCorrected = source.biasCorrected;
- dest.varianceDirection = source.varianceDirection;
- }
- /**
- * <p>Returns the {@link SemiVariance} of the designated values against the mean, using
- * instance properties varianceDirection and biasCorrection.</p>
- *
- * <p>Returns <code>NaN</code> if the array is empty and throws
- * <code>IllegalArgumentException</code> if the array is null.</p>
- *
- * @param values the input array
- * @param start index of the first array element to include
- * @param length the number of elements to include
- * @return the SemiVariance
- * @throws MathIllegalArgumentException if the parameters are not valid
- *
- */
- @Override
- public double evaluate(final double[] values, final int start, final int length)
- throws MathIllegalArgumentException {
- double m = (new Mean()).evaluate(values, start, length);
- return evaluate(values, m, varianceDirection, biasCorrected, 0, values.length);
- }
- /**
- * This method calculates {@link SemiVariance} for the entire array against the mean, using
- * the current value of the biasCorrection instance property.
- *
- * @param values the input array
- * @param direction the {@link Direction} of the semivariance
- * @return the SemiVariance
- * @throws MathIllegalArgumentException if values is null
- *
- */
- public double evaluate(final double[] values, Direction direction)
- throws MathIllegalArgumentException {
- double m = (new Mean()).evaluate(values);
- return evaluate(values, m, direction, biasCorrected, 0, values.length);
- }
- /**
- * <p>Returns the {@link SemiVariance} of the designated values against the cutoff, using
- * instance properties variancDirection and biasCorrection.</p>
- *
- * <p>Returns <code>NaN</code> if the array is empty and throws
- * <code>MathIllegalArgumentException</code> if the array is null.</p>
- *
- * @param values the input array
- * @param cutoff the reference point
- * @return the SemiVariance
- * @throws MathIllegalArgumentException if values is null
- */
- public double evaluate(final double[] values, final double cutoff)
- throws MathIllegalArgumentException {
- return evaluate(values, cutoff, varianceDirection, biasCorrected, 0, values.length);
- }
- /**
- * <p>Returns the {@link SemiVariance} of the designated values against the cutoff in the
- * given direction, using the current value of the biasCorrection instance property.</p>
- *
- * <p>Returns <code>NaN</code> if the array is empty and throws
- * <code>MathIllegalArgumentException</code> if the array is null.</p>
- *
- * @param values the input array
- * @param cutoff the reference point
- * @param direction the {@link Direction} of the semivariance
- * @return the SemiVariance
- * @throws MathIllegalArgumentException if values is null
- */
- public double evaluate(final double[] values, final double cutoff, final Direction direction)
- throws MathIllegalArgumentException {
- return evaluate(values, cutoff, direction, biasCorrected, 0, values.length);
- }
- /**
- * <p>Returns the {@link SemiVariance} of the designated values against the cutoff
- * in the given direction with the provided bias correction.</p>
- *
- * <p>Returns <code>NaN</code> if the array is empty and throws
- * <code>IllegalArgumentException</code> if the array is null.</p>
- *
- * @param values the input array
- * @param cutoff the reference point
- * @param direction the {@link Direction} of the semivariance
- * @param corrected the BiasCorrection flag
- * @param start index of the first array element to include
- * @param length the number of elements to include
- * @return the SemiVariance
- * @throws MathIllegalArgumentException if the parameters are not valid
- *
- */
- public double evaluate (final double[] values, final double cutoff, final Direction direction,
- final boolean corrected, final int start, final int length)
- throws MathIllegalArgumentException {
- MathArrays.verifyValues(values, start, length);
- if (values.length == 0) {
- return Double.NaN;
- } else {
- if (values.length == 1) {
- return 0.0;
- } else {
- final boolean booleanDirection = direction.getDirection();
- double dev = 0.0;
- double sumsq = 0.0;
- for (int i = start; i < length; i++) {
- if ((values[i] > cutoff) == booleanDirection) {
- dev = values[i] - cutoff;
- sumsq += dev * dev;
- }
- }
- if (corrected) {
- return sumsq / (length - 1.0);
- } else {
- return sumsq / length;
- }
- }
- }
- }
- /**
- * Returns true iff biasCorrected property is set to true.
- *
- * @return the value of biasCorrected.
- */
- public boolean isBiasCorrected() {
- return biasCorrected;
- }
- /**
- * Sets the biasCorrected property.
- *
- * @param biasCorrected new biasCorrected property value
- */
- public void setBiasCorrected(boolean biasCorrected) {
- this.biasCorrected = biasCorrected;
- }
- /**
- * Returns the varianceDirection property.
- *
- * @return the varianceDirection
- */
- public Direction getVarianceDirection () {
- return varianceDirection;
- }
- /**
- * Sets the variance direction.
- *
- * @param varianceDirection the direction of the semivariance
- */
- public void setVarianceDirection(Direction varianceDirection) {
- this.varianceDirection = varianceDirection;
- }
- /**
- * The direction of the semivariance - either upside or downside. The direction
- * is represented by boolean, with true corresponding to UPSIDE semivariance.
- */
- public enum Direction {
- /**
- * The UPSIDE Direction is used to specify that the observations above the.
- * cutoff point will be used to calculate SemiVariance
- */
- UPSIDE (true),
- /**
- * The DOWNSIDE Direction is used to specify that the observations below.
- * the cutoff point will be used to calculate SemiVariance
- */
- DOWNSIDE (false);
- /**
- * boolean value UPSIDE <-> true.
- */
- private boolean direction;
- /**
- * Create a Direction with the given value.
- *
- * @param b boolean value representing the Direction. True corresponds to UPSIDE.
- */
- Direction (boolean b) {
- direction = b;
- }
- /**
- * Returns the value of this Direction. True corresponds to UPSIDE.
- *
- * @return true if direction is UPSIDE; false otherwise
- */
- boolean getDirection () {
- return direction;
- }
- }
- }