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