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