View Javadoc
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 static org.junit.Assert.assertEquals;
26  import static org.junit.Assert.assertTrue;
27  import static org.junit.Assert.fail;
28  
29  import java.util.Arrays;
30  
31  import org.hipparchus.UnitTestUtils;
32  import org.hipparchus.exception.NullArgumentException;
33  import org.hipparchus.stat.StatUtils;
34  import org.hipparchus.stat.descriptive.UnivariateStatisticAbstractTest;
35  import org.junit.Before;
36  import org.junit.Test;
37  
38  /**
39   * Test cases for the {@link SemiVariance} class.
40   */
41  public class SemiVarianceTest extends UnivariateStatisticAbstractTest {
42  
43      private double semiVariance;
44  
45      @Before
46      public void setup() {
47          // calculate the semivariance the same way as defined by
48          // the SemiVariance class. This is not the same as calculating
49          // the variance of the values that are below / above the cutoff.
50          double val =
51              Arrays.stream(testArray)
52                    .filter(x -> x < this.mean)
53                    .reduce(0, (x, y) -> x + (y - this.mean) * (y - this.mean));
54  
55          this.semiVariance = val / (testArray.length - 1);
56      }
57  
58      @Override
59      public SemiVariance getUnivariateStatistic() {
60          return new SemiVariance();
61      }
62  
63      @Override
64      public double expectedValue() {
65          return semiVariance;
66      }
67  
68      @Test
69      public void testInsufficientData() {
70          SemiVariance sv = getUnivariateStatistic();
71          try {
72              sv.evaluate(null);
73              fail("null is not a valid data array.");
74          } catch (NullArgumentException nae) {
75          }
76  
77          try {
78              sv = sv.withVarianceDirection(SemiVariance.UPSIDE_VARIANCE);
79              sv.evaluate(null);
80              fail("null is not a valid data array.");
81          } catch (NullArgumentException nae) {
82          }
83          assertTrue(Double.isNaN(sv.evaluate(new double[0])));
84      }
85  
86      @Test
87      public void testSingleDown() {
88          SemiVariance sv = new SemiVariance();
89          double[] values = { 50.0d };
90          double singletest = sv.evaluate(values);
91          assertEquals(0.0d, singletest, 0);
92      }
93  
94      @Test
95      public void testSingleUp() {
96          SemiVariance sv = new SemiVariance(SemiVariance.UPSIDE_VARIANCE);
97          double[] values = { 50.0d };
98          double singletest = sv.evaluate(values);
99          assertEquals(0.0d, singletest, 0);
100     }
101 
102     @Test
103     public void testSample() {
104         final double[] values = { -2.0d, 2.0d, 4.0d, -2.0d, 22.0d, 11.0d, 3.0d, 14.0d, 5.0d };
105         final int length = values.length;
106         final double mean = StatUtils.mean(values); // 6.333...
107         SemiVariance sv = getUnivariateStatistic(); // Default bias correction is true
108         final double downsideSemiVariance = sv.evaluate(values); // Downside is the default
109         assertEquals(UnitTestUtils.sumSquareDev(new double[] {-2d, 2d, 4d, -2d, 3d, 5d}, mean) / (length - 1),
110                      downsideSemiVariance, 1E-14);
111 
112         sv = sv.withVarianceDirection(SemiVariance.UPSIDE_VARIANCE);
113         final double upsideSemiVariance = sv.evaluate(values);
114         assertEquals(UnitTestUtils.sumSquareDev(new double[] {22d, 11d, 14d}, mean) / (length - 1),
115                      upsideSemiVariance, 1E-14);
116 
117         // Verify that upper + lower semivariance against the mean sum to variance
118         assertEquals(StatUtils.variance(values), downsideSemiVariance + upsideSemiVariance, 10e-12);
119     }
120 
121     @Test
122     public void testPopulation() {
123         double[] values = { -2.0d, 2.0d, 4.0d, -2.0d, 22.0d, 11.0d, 3.0d, 14.0d, 5.0d };
124         SemiVariance sv = new SemiVariance(false);
125 
126         double singletest = sv.evaluate(values);
127         assertEquals(19.556d, singletest, 0.01d);
128 
129         sv = sv.withVarianceDirection(SemiVariance.UPSIDE_VARIANCE);
130         singletest = sv.evaluate(values);
131         assertEquals(36.222d, singletest, 0.01d);
132     }
133 
134     @Test
135     public void testNonMeanCutoffs() {
136         double[] values = { -2.0d, 2.0d, 4.0d, -2.0d, 22.0d, 11.0d, 3.0d, 14.0d, 5.0d };
137         SemiVariance sv = new SemiVariance(false); // Turn off bias correction - use df = length
138 
139         double singletest = sv.evaluate(values, 1.0d, SemiVariance.DOWNSIDE_VARIANCE, false, 0, values.length);
140         assertEquals(UnitTestUtils.sumSquareDev(new double[] { -2d, -2d }, 1.0d) / values.length,
141                      singletest, 0.01d);
142 
143         singletest = sv.evaluate(values, 3.0d, SemiVariance.UPSIDE_VARIANCE, false, 0, values.length);
144         assertEquals(UnitTestUtils.sumSquareDev(new double[] { 4d, 22d, 11d, 14d, 5d }, 3.0d) / values.length,
145                      singletest,
146                      0.01d);
147     }
148 
149     /**
150      * Check that the lower + upper semivariance against the mean sum to the variance.
151      */
152     @Test
153     public void testVarianceDecompMeanCutoff() {
154         double[] values = { -2.0d, 2.0d, 4.0d, -2.0d, 22.0d, 11.0d, 3.0d, 14.0d, 5.0d };
155         double variance = StatUtils.variance(values);
156         SemiVariance sv = new SemiVariance(true); // Bias corrected
157         sv = sv.withVarianceDirection(SemiVariance.DOWNSIDE_VARIANCE);
158         final double lower = sv.evaluate(values);
159         sv = sv.withVarianceDirection(SemiVariance.UPSIDE_VARIANCE);
160         final double upper = sv.evaluate(values);
161         assertEquals(variance, lower + upper, 10e-12);
162     }
163 
164     /**
165      * Check that upper and lower semivariances against a cutoff sum to the sum
166      * of squared deviations of the full set of values against the cutoff
167      * divided by df = length - 1 (assuming bias-corrected).
168      */
169     @Test
170     public void testVarianceDecompNonMeanCutoff() {
171         double[] values = { -2.0d, 2.0d, 4.0d, -2.0d, 22.0d, 11.0d, 3.0d, 14.0d, 5.0d };
172         double target = 0;
173         double totalSumOfSquares = UnitTestUtils.sumSquareDev(values, target);
174         SemiVariance sv = new SemiVariance(true); // Bias corrected
175         sv = sv.withVarianceDirection(SemiVariance.DOWNSIDE_VARIANCE);
176         double lower = sv.evaluate(values, target);
177         sv = sv.withVarianceDirection(SemiVariance.UPSIDE_VARIANCE);
178         double upper = sv.evaluate(values, target);
179         assertEquals(totalSumOfSquares / (values.length - 1), lower + upper, 10e-12);
180     }
181 
182     @Test
183     public void testNoVariance() {
184         final double[] values = {100d, 100d, 100d, 100d};
185         SemiVariance sv = getUnivariateStatistic();
186         assertEquals(0, sv.evaluate(values), 10E-12);
187         assertEquals(0, sv.evaluate(values, 100d), 10E-12);
188         assertEquals(0, sv.evaluate(values, 100d, SemiVariance.UPSIDE_VARIANCE, false, 0, values.length), 10E-12);
189     }
190 
191 }