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  package org.hipparchus.stat;
23  
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 org.hipparchus.UnitTestUtils;
30  import org.hipparchus.exception.MathIllegalArgumentException;
31  import org.hipparchus.exception.NullArgumentException;
32  import org.hipparchus.stat.descriptive.DescriptiveStatistics;
33  import org.hipparchus.util.FastMath;
34  import org.hipparchus.util.Precision;
35  import org.junit.Test;
36  
37  /**
38   * Test cases for the {@link StatUtils} class.
39   */
40  public final class StatUtilsTest {
41  
42      private static final double ONE = 1;
43      private static final float  TWO = 2;
44      private static final int    THREE = 3;
45      private static final double MEAN = 2;
46      private static final double SUMSQ = 18;
47      private static final double SUM = 8;
48      private static final double VAR = 0.666666666666666666667;
49      private static final double MIN = 1;
50      private static final double MAX = 3;
51      private static final double TOLERANCE = 10E-15;
52      private static final double NAN = Double.NaN;
53  
54      /** test stats */
55      @Test
56      public void testStats() {
57          double[] values = new double[] { ONE, TWO, TWO, THREE };
58          assertEquals("sum", SUM, StatUtils.sum(values), TOLERANCE);
59          assertEquals("sumsq", SUMSQ, StatUtils.sumSq(values), TOLERANCE);
60          assertEquals("var", VAR, StatUtils.variance(values), TOLERANCE);
61          assertEquals("var with mean", VAR, StatUtils.variance(values, MEAN), TOLERANCE);
62          assertEquals("mean", MEAN, StatUtils.mean(values), TOLERANCE);
63          assertEquals("min", MIN, StatUtils.min(values), TOLERANCE);
64          assertEquals("max", MAX, StatUtils.max(values), TOLERANCE);
65      }
66  
67      @Test
68      public void testN0andN1Conditions() {
69          double[] values = new double[0];
70  
71          assertTrue("Mean of n = 0 set should be NaN",
72                     Double.isNaN(StatUtils.mean(values)));
73          assertTrue("Variance of n = 0 set should be NaN",
74                     Double.isNaN(StatUtils.variance(values)));
75  
76          values = new double[] { ONE };
77  
78          assertEquals("Mean of n = 1 set should be value of single item n1", ONE,
79                       StatUtils.mean(values), 0.0);
80          assertEquals("Variance of n = 1 set should be zero", 0,
81                       StatUtils.variance(values), 0.0);
82      }
83  
84      @Test
85      public void testArrayIndexConditions() {
86          double[] values = { 1.0, 2.0, 3.0, 4.0 };
87  
88          assertEquals("Sum not expected", 5.0, StatUtils.sum(values, 1, 2), Double.MIN_VALUE);
89          assertEquals("Sum not expected", 3.0, StatUtils.sum(values, 0, 2), Double.MIN_VALUE);
90          assertEquals("Sum not expected", 7.0, StatUtils.sum(values, 2, 2), Double.MIN_VALUE);
91  
92          try {
93              StatUtils.sum(values, 2, 3);
94              fail("Expected RuntimeException");
95          } catch (RuntimeException e) {
96              // expected
97          }
98  
99          try {
100             StatUtils.sum(values, -1, 2);
101             fail("Expected RuntimeException");
102         } catch (RuntimeException e) {
103             // expected
104         }
105     }
106 
107     @Test
108     public void testSumSq() {
109         double[] x = null;
110 
111         // test null
112         try {
113             StatUtils.sumSq(x);
114             fail("null is not a valid data array.");
115         } catch (NullArgumentException ex) {
116             // success
117         }
118 
119         try {
120             StatUtils.sumSq(x, 0, 4);
121             fail("null is not a valid data array.");
122         } catch (NullArgumentException ex) {
123             // success
124         }
125 
126         // test empty
127         x = new double[] {};
128         UnitTestUtils.assertEquals(0, StatUtils.sumSq(x), TOLERANCE);
129         UnitTestUtils.assertEquals(0, StatUtils.sumSq(x, 0, 0), TOLERANCE);
130 
131         // test one
132         x = new double[] {TWO};
133         UnitTestUtils.assertEquals(4, StatUtils.sumSq(x), TOLERANCE);
134         UnitTestUtils.assertEquals(4, StatUtils.sumSq(x, 0, 1), TOLERANCE);
135 
136         // test many
137         x = new double[] {ONE, TWO, TWO, THREE};
138         UnitTestUtils.assertEquals(18, StatUtils.sumSq(x), TOLERANCE);
139         UnitTestUtils.assertEquals(8, StatUtils.sumSq(x, 1, 2), TOLERANCE);
140     }
141 
142     @Test
143     public void testProduct() {
144         double[] x = null;
145 
146         // test null
147         try {
148             StatUtils.product(x);
149             fail("null is not a valid data array.");
150         } catch (NullArgumentException ex) {
151             // success
152         }
153 
154         try {
155             StatUtils.product(x, 0, 4);
156             fail("null is not a valid data array.");
157         } catch (NullArgumentException ex) {
158             // success
159         }
160 
161         // test empty
162         x = new double[] {};
163         UnitTestUtils.assertEquals(1, StatUtils.product(x), TOLERANCE);
164         UnitTestUtils.assertEquals(1, StatUtils.product(x, 0, 0), TOLERANCE);
165 
166         // test one
167         x = new double[] {TWO};
168         UnitTestUtils.assertEquals(TWO, StatUtils.product(x), TOLERANCE);
169         UnitTestUtils.assertEquals(TWO, StatUtils.product(x, 0, 1), TOLERANCE);
170 
171         // test many
172         x = new double[] {ONE, TWO, TWO, THREE};
173         UnitTestUtils.assertEquals(12, StatUtils.product(x), TOLERANCE);
174         UnitTestUtils.assertEquals(4, StatUtils.product(x, 1, 2), TOLERANCE);
175     }
176 
177     @Test
178     public void testSumLog() {
179         double[] x = null;
180 
181         // test null
182         try {
183             StatUtils.sumLog(x);
184             fail("null is not a valid data array.");
185         } catch (NullArgumentException ex) {
186             // success
187         }
188 
189         try {
190             StatUtils.sumLog(x, 0, 4);
191             fail("null is not a valid data array.");
192         } catch (NullArgumentException ex) {
193             // success
194         }
195 
196         // test empty
197         x = new double[] {};
198         UnitTestUtils.assertEquals(0, StatUtils.sumLog(x), TOLERANCE);
199         UnitTestUtils.assertEquals(0, StatUtils.sumLog(x, 0, 0), TOLERANCE);
200 
201         // test one
202         x = new double[] {TWO};
203         UnitTestUtils.assertEquals(FastMath.log(TWO), StatUtils.sumLog(x), TOLERANCE);
204         UnitTestUtils.assertEquals(FastMath.log(TWO), StatUtils.sumLog(x, 0, 1), TOLERANCE);
205 
206         // test many
207         x = new double[] {ONE, TWO, TWO, THREE};
208         UnitTestUtils.assertEquals(FastMath.log(ONE) + 2.0 * FastMath.log(TWO) + FastMath.log(THREE), StatUtils.sumLog(x), TOLERANCE);
209         UnitTestUtils.assertEquals(2.0 * FastMath.log(TWO), StatUtils.sumLog(x, 1, 2), TOLERANCE);
210     }
211 
212     @Test
213     public void testMean() {
214         double[] x = null;
215 
216         try {
217             StatUtils.mean(x, 0, 4);
218             fail("null is not a valid data array.");
219         } catch (NullArgumentException ex) {
220             // success
221         }
222 
223         // test empty
224         x = new double[] {};
225         UnitTestUtils.assertEquals(Double.NaN, StatUtils.mean(x, 0, 0), TOLERANCE);
226 
227         // test one
228         x = new double[] {TWO};
229         UnitTestUtils.assertEquals(TWO, StatUtils.mean(x, 0, 1), TOLERANCE);
230 
231         // test many
232         x = new double[] {ONE, TWO, TWO, THREE};
233         UnitTestUtils.assertEquals(2.5, StatUtils.mean(x, 2, 2), TOLERANCE);
234     }
235 
236     @Test
237     public void testVariance() {
238         double[] x = null;
239 
240         try {
241             StatUtils.variance(x, 0, 4);
242             fail("null is not a valid data array.");
243         } catch (NullArgumentException ex) {
244             // success
245         }
246 
247         // test empty
248         x = new double[] {};
249         UnitTestUtils.assertEquals(Double.NaN, StatUtils.variance(x, 0, 0), TOLERANCE);
250 
251         // test one
252         x = new double[] {TWO};
253         UnitTestUtils.assertEquals(0.0, StatUtils.variance(x, 0, 1), TOLERANCE);
254 
255         // test many
256         x = new double[] {ONE, TWO, TWO, THREE};
257         UnitTestUtils.assertEquals(0.5, StatUtils.variance(x, 2, 2), TOLERANCE);
258 
259         // test precomputed mean
260         x = new double[] {ONE, TWO, TWO, THREE};
261         UnitTestUtils.assertEquals(0.5, StatUtils.variance(x,2.5, 2, 2), TOLERANCE);
262     }
263 
264     @Test
265     public void testPopulationVariance() {
266         double[] x = null;
267 
268         try {
269             StatUtils.variance(x, 0, 4);
270             fail("null is not a valid data array.");
271         } catch (NullArgumentException ex) {
272             // success
273         }
274 
275         // test empty
276         x = new double[] {};
277         UnitTestUtils.assertEquals(Double.NaN, StatUtils.populationVariance(x, 0, 0), TOLERANCE);
278 
279         // test one
280         x = new double[] {TWO};
281         UnitTestUtils.assertEquals(0.0, StatUtils.populationVariance(x, 0, 1), TOLERANCE);
282 
283         // test many
284         x = new double[] {ONE, TWO, TWO, THREE};
285         UnitTestUtils.assertEquals(0.25, StatUtils.populationVariance(x, 0, 2), TOLERANCE);
286 
287         // test precomputed mean
288         x = new double[] {ONE, TWO, TWO, THREE};
289         UnitTestUtils.assertEquals(0.25, StatUtils.populationVariance(x, 2.5, 2, 2), TOLERANCE);
290     }
291 
292     @Test
293     public void testMax() {
294         double[] x = null;
295 
296         try {
297             StatUtils.max(x, 0, 4);
298             fail("null is not a valid data array.");
299         } catch (NullArgumentException ex) {
300             // success
301         }
302 
303         // test empty
304         x = new double[] {};
305         UnitTestUtils.assertEquals(Double.NaN, StatUtils.max(x, 0, 0), TOLERANCE);
306 
307         // test one
308         x = new double[] {TWO};
309         UnitTestUtils.assertEquals(TWO, StatUtils.max(x, 0, 1), TOLERANCE);
310 
311         // test many
312         x = new double[] {ONE, TWO, TWO, THREE};
313         UnitTestUtils.assertEquals(THREE, StatUtils.max(x, 1, 3), TOLERANCE);
314 
315         // test first nan is ignored
316         x = new double[] {NAN, TWO, THREE};
317         UnitTestUtils.assertEquals(THREE, StatUtils.max(x), TOLERANCE);
318 
319         // test middle nan is ignored
320         x = new double[] {ONE, NAN, THREE};
321         UnitTestUtils.assertEquals(THREE, StatUtils.max(x), TOLERANCE);
322 
323         // test last nan is ignored
324         x = new double[] {ONE, TWO, NAN};
325         UnitTestUtils.assertEquals(TWO, StatUtils.max(x), TOLERANCE);
326 
327         // test all nan returns nan
328         x = new double[] {NAN, NAN, NAN};
329         UnitTestUtils.assertEquals(NAN, StatUtils.max(x), TOLERANCE);
330     }
331 
332     @Test
333     public void testMin() {
334         double[] x = null;
335 
336         try {
337             StatUtils.min(x, 0, 4);
338             fail("null is not a valid data array.");
339         } catch (NullArgumentException ex) {
340             // success
341         }
342 
343         // test empty
344         x = new double[] {};
345         UnitTestUtils.assertEquals(Double.NaN, StatUtils.min(x, 0, 0), TOLERANCE);
346 
347         // test one
348         x = new double[] {TWO};
349         UnitTestUtils.assertEquals(TWO, StatUtils.min(x, 0, 1), TOLERANCE);
350 
351         // test many
352         x = new double[] {ONE, TWO, TWO, THREE};
353         UnitTestUtils.assertEquals(TWO, StatUtils.min(x, 1, 3), TOLERANCE);
354 
355         // test first nan is ignored
356         x = new double[] {NAN, TWO, THREE};
357         UnitTestUtils.assertEquals(TWO, StatUtils.min(x), TOLERANCE);
358 
359         // test middle nan is ignored
360         x = new double[] {ONE, NAN, THREE};
361         UnitTestUtils.assertEquals(ONE, StatUtils.min(x), TOLERANCE);
362 
363         // test last nan is ignored
364         x = new double[] {ONE, TWO, NAN};
365         UnitTestUtils.assertEquals(ONE, StatUtils.min(x), TOLERANCE);
366 
367         // test all nan returns nan
368         x = new double[] {NAN, NAN, NAN};
369         UnitTestUtils.assertEquals(NAN, StatUtils.min(x), TOLERANCE);
370     }
371 
372     @Test
373     public void testPercentile() {
374         double[] x = null;
375 
376         // test null
377         try {
378             StatUtils.percentile(x, .25);
379             fail("null is not a valid data array.");
380         } catch (NullArgumentException ex) {
381             // success
382         }
383 
384         try {
385             StatUtils.percentile(x, 0, 4, 0.25);
386             fail("null is not a valid data array.");
387         } catch (NullArgumentException ex) {
388             // success
389         }
390 
391         // test empty
392         x = new double[] {};
393         UnitTestUtils.assertEquals(Double.NaN, StatUtils.percentile(x, 25), TOLERANCE);
394         UnitTestUtils.assertEquals(Double.NaN, StatUtils.percentile(x, 0, 0, 25), TOLERANCE);
395 
396         // test one
397         x = new double[] {TWO};
398         UnitTestUtils.assertEquals(TWO, StatUtils.percentile(x, 25), TOLERANCE);
399         UnitTestUtils.assertEquals(TWO, StatUtils.percentile(x, 0, 1, 25), TOLERANCE);
400 
401         // test many
402         x = new double[] {ONE, TWO, TWO, THREE};
403         UnitTestUtils.assertEquals(2.5, StatUtils.percentile(x, 70), TOLERANCE);
404         UnitTestUtils.assertEquals(2.5, StatUtils.percentile(x, 1, 3, 62.5), TOLERANCE);
405     }
406 
407     @Test
408     public void testDifferenceStats() {
409         double[] sample1 = {1d, 2d, 3d, 4d};
410         double[] sample2 = {1d, 3d, 4d, 2d};
411         double[] diff = {0d, -1d, -1d, 2d};
412         double[] small = {1d, 4d};
413         double meanDifference = StatUtils.meanDifference(sample1, sample2);
414         assertEquals(StatUtils.sumDifference(sample1, sample2), StatUtils.sum(diff), TOLERANCE);
415         assertEquals(meanDifference, StatUtils.mean(diff), TOLERANCE);
416         assertEquals(StatUtils.varianceDifference(sample1, sample2, meanDifference),
417                      StatUtils.variance(diff), TOLERANCE);
418         try {
419             StatUtils.meanDifference(sample1, small);
420             fail("Expecting MathIllegalArgumentException");
421         } catch (MathIllegalArgumentException ex) {
422             // expected
423         }
424         try {
425             StatUtils.varianceDifference(sample1, small, meanDifference);
426             fail("Expecting MathIllegalArgumentException");
427         } catch (MathIllegalArgumentException ex) {
428             // expected
429         }
430         try {
431             double[] single = {1.0};
432             StatUtils.varianceDifference(single, single, meanDifference);
433             fail("Expecting MathIllegalArgumentException");
434         } catch (MathIllegalArgumentException ex) {
435             // expected
436         }
437     }
438 
439     @Test
440     public void testGeometricMean() {
441         double[] test = null;
442         try {
443             StatUtils.geometricMean(test);
444             fail("Expecting NullArgumentException");
445         } catch (NullArgumentException ex) {
446             // expected
447         }
448         test = new double[] {2, 4, 6, 8};
449         assertEquals(FastMath.exp(0.25d * StatUtils.sumLog(test)),
450                      StatUtils.geometricMean(test), Double.MIN_VALUE);
451         assertEquals(FastMath.exp(0.5 * StatUtils.sumLog(test, 0, 2)),
452                      StatUtils.geometricMean(test, 0, 2), Double.MIN_VALUE);
453     }
454 
455     /**
456      * Run the test with the values 50 and 100 and assume standardized values
457      */
458     @Test
459     public void testNormalize1() {
460         double[] sample = { 50, 100 };
461         double[]
462                         expectedSample = { -25 / FastMath.sqrt(1250), 25 / FastMath.sqrt(1250) };
463         double[] out = StatUtils.normalize(sample);
464         for (int i = 0; i < out.length; i++) {
465             assertTrue(Precision.equals(out[i], expectedSample[i], 1));
466         }
467     }
468 
469     /**
470      * Run with 77 random values, assuming that the outcome has a mean of 0 and a standard deviation of 1 with a
471      * precision of 1E-10.
472      */
473     @Test
474     public void testNormalize2() {
475         // create an sample with 77 values
476         int length = 77;
477         double[] sample = new double[length];
478         for (int i = 0; i < length; i++) {
479             sample[i] = FastMath.random();
480         }
481         // normalize this sample
482         double[] standardizedSample = StatUtils.normalize(sample);
483 
484         DescriptiveStatistics stats = new DescriptiveStatistics();
485         // Add the data from the array
486         for (int i = 0; i < length; i++) {
487             stats.addValue(standardizedSample[i]);
488         }
489         // the calculations do have a limited precision
490         double distance = 1E-10;
491         // check the mean an standard deviation
492         assertEquals(0.0, stats.getMean(), distance);
493         assertEquals(1.0, stats.getStandardDeviation(), distance);
494     }
495 
496     @Test
497     public void testMode() {
498         final double[] singleMode = {0, 1, 0, 2, 7, 11, 12};
499         final double[] modeSingle = StatUtils.mode(singleMode);
500         assertEquals(0, modeSingle[0], Double.MIN_VALUE);
501         assertEquals(1, modeSingle.length);
502 
503         final double[] twoMode = {0, 1, 2, 0, 2, 3, 7, 11};
504         final double[] modeDouble = StatUtils.mode(twoMode);
505         assertEquals(0, modeDouble[0], Double.MIN_VALUE);
506         assertEquals(2, modeDouble[1], Double.MIN_VALUE);
507         assertEquals(2, modeDouble.length);
508 
509         final double[] nanInfested = {0, 0, 0, Double.NaN, Double.NaN, Double.NaN, Double.NaN, 2, 2, 2, 3, 5};
510         final double[] modeNan = StatUtils.mode(nanInfested);
511         assertEquals(0, modeNan[0], Double.MIN_VALUE);
512         assertEquals(2, modeNan[1], Double.MIN_VALUE);
513         assertEquals(2, modeNan.length);
514 
515         final double[] infInfested = {0, 0, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY,
516             Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, 2, 2, 3, 5};
517         final double[] modeInf = StatUtils.mode(infInfested);
518         assertEquals(Double.NEGATIVE_INFINITY, modeInf[0], Double.MIN_VALUE);
519         assertEquals(0, modeInf[1], Double.MIN_VALUE);
520         assertEquals(2, modeInf[2], Double.MIN_VALUE);
521         assertEquals(Double.POSITIVE_INFINITY, modeInf[3], Double.MIN_VALUE);
522         assertEquals(4, modeInf.length);
523 
524         final double[] noData = {};
525         final double[] modeNodata = StatUtils.mode(noData);
526         assertEquals(0, modeNodata.length);
527 
528         final double[] nansOnly = {Double.NaN, Double.NaN};
529         final double[] modeNansOnly = StatUtils.mode(nansOnly);
530         assertEquals(0, modeNansOnly.length);
531 
532         final double[] nullArray = null;
533         try {
534             StatUtils.mode(nullArray);
535             fail("Expecting NullArgumentException");
536         } catch (NullArgumentException ex) {
537             // Expected
538         }
539     }
540 
541 }