/* * 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.stat.descriptive.moment; import org.apache.commons.math4.TestUtils; import org.apache.commons.math4.exception.NullArgumentException; import org.apache.commons.math4.stat.StatUtils; import org.apache.commons.math4.stat.descriptive.moment.SemiVariance; import org.junit.Assert; import org.junit.Test; public class SemiVarianceTest { @Test public void testInsufficientData() { double[] nothing = null; SemiVariance sv = new SemiVariance(); try { sv.evaluate(nothing); Assert.fail("null is not a valid data array."); } catch (NullArgumentException nae) { } try { sv.setVarianceDirection(SemiVariance.UPSIDE_VARIANCE); sv.evaluate(nothing); Assert.fail("null is not a valid data array."); } catch (NullArgumentException nae) { } nothing = new double[] {}; Assert.assertTrue(Double.isNaN(sv.evaluate(nothing))); } @Test public void testSingleDown() { SemiVariance sv = new SemiVariance(); double[] values = { 50.0d }; double singletest = sv.evaluate(values); Assert.assertEquals(0.0d, singletest, 0); } @Test public void testSingleUp() { SemiVariance sv = new SemiVariance(SemiVariance.UPSIDE_VARIANCE); double[] values = { 50.0d }; double singletest = sv.evaluate(values); Assert.assertEquals(0.0d, singletest, 0); } @Test public void testSample() { final double[] values = { -2.0d, 2.0d, 4.0d, -2.0d, 22.0d, 11.0d, 3.0d, 14.0d, 5.0d }; final int length = values.length; final double mean = StatUtils.mean(values); // 6.333... final SemiVariance sv = new SemiVariance(); // Default bias correction is true final double downsideSemiVariance = sv.evaluate(values); // Downside is the default Assert.assertEquals(TestUtils.sumSquareDev(new double[] {-2d, 2d, 4d, -2d, 3d, 5d}, mean) / (length - 1), downsideSemiVariance, 1E-14); sv.setVarianceDirection(SemiVariance.UPSIDE_VARIANCE); final double upsideSemiVariance = sv.evaluate(values); Assert.assertEquals(TestUtils.sumSquareDev(new double[] {22d, 11d, 14d}, mean) / (length - 1), upsideSemiVariance, 1E-14); // Verify that upper + lower semivariance against the mean sum to variance Assert.assertEquals(StatUtils.variance(values), downsideSemiVariance + upsideSemiVariance, 10e-12); } @Test public void testPopulation() { double[] values = { -2.0d, 2.0d, 4.0d, -2.0d, 22.0d, 11.0d, 3.0d, 14.0d, 5.0d }; SemiVariance sv = new SemiVariance(false); double singletest = sv.evaluate(values); Assert.assertEquals(19.556d, singletest, 0.01d); sv.setVarianceDirection(SemiVariance.UPSIDE_VARIANCE); singletest = sv.evaluate(values); Assert.assertEquals(36.222d, singletest, 0.01d); } @Test public void testNonMeanCutoffs() { double[] values = { -2.0d, 2.0d, 4.0d, -2.0d, 22.0d, 11.0d, 3.0d, 14.0d, 5.0d }; SemiVariance sv = new SemiVariance(false); // Turn off bias correction - use df = length double singletest = sv.evaluate(values, 1.0d, SemiVariance.DOWNSIDE_VARIANCE, false, 0, values.length); Assert.assertEquals(TestUtils.sumSquareDev(new double[] { -2d, -2d }, 1.0d) / values.length, singletest, 0.01d); singletest = sv.evaluate(values, 3.0d, SemiVariance.UPSIDE_VARIANCE, false, 0, values.length); Assert.assertEquals(TestUtils.sumSquareDev(new double[] { 4d, 22d, 11d, 14d, 5d }, 3.0d) / values.length, singletest, 0.01d); } /** * Check that the lower + upper semivariance against the mean sum to the * variance. */ @Test public void testVarianceDecompMeanCutoff() { double[] values = { -2.0d, 2.0d, 4.0d, -2.0d, 22.0d, 11.0d, 3.0d, 14.0d, 5.0d }; double variance = StatUtils.variance(values); SemiVariance sv = new SemiVariance(true); // Bias corrected sv.setVarianceDirection(SemiVariance.DOWNSIDE_VARIANCE); final double lower = sv.evaluate(values); sv.setVarianceDirection(SemiVariance.UPSIDE_VARIANCE); final double upper = sv.evaluate(values); Assert.assertEquals(variance, lower + upper, 10e-12); } /** * Check that upper and lower semivariances against a cutoff sum to the sum * of squared deviations of the full set of values against the cutoff * divided by df = length - 1 (assuming bias-corrected). */ @Test public void testVarianceDecompNonMeanCutoff() { double[] values = { -2.0d, 2.0d, 4.0d, -2.0d, 22.0d, 11.0d, 3.0d, 14.0d, 5.0d }; double target = 0; double totalSumOfSquares = TestUtils.sumSquareDev(values, target); SemiVariance sv = new SemiVariance(true); // Bias corrected sv.setVarianceDirection(SemiVariance.DOWNSIDE_VARIANCE); double lower = sv.evaluate(values, target); sv.setVarianceDirection(SemiVariance.UPSIDE_VARIANCE); double upper = sv.evaluate(values, target); Assert.assertEquals(totalSumOfSquares / (values.length - 1), lower + upper, 10e-12); } @Test public void testNoVariance() { final double[] values = {100d, 100d, 100d, 100d}; SemiVariance sv = new SemiVariance(); Assert.assertEquals(0, sv.evaluate(values), 10E-12); Assert.assertEquals(0, sv.evaluate(values, 100d), 10E-12); Assert.assertEquals(0, sv.evaluate(values, 100d, SemiVariance.UPSIDE_VARIANCE, false, 0, values.length), 10E-12); } }