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.math.stat;
18  
19  import junit.framework.Test;
20  import junit.framework.TestCase;
21  import junit.framework.TestSuite;
22  
23  import org.apache.commons.math.TestUtils;
24  
25  /**
26   * Test cases for the {@link StatUtils} class.
27   * @version $Revision: 566833 $ $Date: 2007-08-16 13:36:33 -0700 (Thu, 16 Aug 2007) $
28   */
29  
30  public final class StatUtilsTest extends TestCase {
31  
32      private double one = 1;
33      private float two = 2;
34      private int three = 3;
35      private double mean = 2;
36      private double sumSq = 18;
37      private double sum = 8;
38      private double var = 0.666666666666666666667;
39      private double min = 1;
40      private double max = 3;
41      private double tolerance = 10E-15;
42      private double nan = Double.NaN;
43  
44      public StatUtilsTest(String name) {
45          super(name);
46      }
47  
48      public void setUp() {
49      }
50  
51      public static Test suite() {
52          TestSuite suite = new TestSuite(StatUtilsTest.class);
53          suite.setName("StatUtil Tests");
54          return suite;
55      }
56  
57      /** test stats */
58      public void testStats() {
59          double[] values = new double[] { one, two, two, three };
60          assertEquals("sum", sum, StatUtils.sum(values), tolerance);
61          assertEquals("sumsq", sumSq, StatUtils.sumSq(values), tolerance);
62          assertEquals("var", var, StatUtils.variance(values), tolerance);
63          assertEquals("var with mean", var, StatUtils.variance(values, mean), tolerance);
64          assertEquals("mean", mean, StatUtils.mean(values), tolerance);
65          assertEquals("min", min, StatUtils.min(values), tolerance);
66          assertEquals("max", max, StatUtils.max(values), tolerance);
67      }
68  
69      public void testN0andN1Conditions() throws Exception {
70          double[] values = new double[0];
71  
72          assertTrue(
73              "Mean of n = 0 set should be NaN",
74              Double.isNaN(StatUtils.mean(values)));
75          assertTrue(
76              "Variance of n = 0 set should be NaN",
77              Double.isNaN(StatUtils.variance(values)));
78  
79          values = new double[] { one };
80  
81          assertTrue(
82              "Mean of n = 1 set should be value of single item n1",
83              StatUtils.mean(values) == one);
84          assertTrue(
85              "Variance of n = 1 set should be zero",
86              StatUtils.variance(values) == 0);
87      }
88  
89      public void testArrayIndexConditions() throws Exception {
90          double[] values = { 1.0, 2.0, 3.0, 4.0 };
91  
92          assertEquals(
93              "Sum not expected",
94              5.0,
95              StatUtils.sum(values, 1, 2),
96              Double.MIN_VALUE);
97          assertEquals(
98              "Sum not expected",
99              3.0,
100             StatUtils.sum(values, 0, 2),
101             Double.MIN_VALUE);
102         assertEquals(
103             "Sum not expected",
104             7.0,
105             StatUtils.sum(values, 2, 2),
106             Double.MIN_VALUE);
107 
108         try {
109             StatUtils.sum(values, 2, 3);
110             assertTrue("Didn't throw exception", false);
111         } catch (Exception e) {
112             assertTrue(true);
113         }
114 
115         try {
116             StatUtils.sum(values, -1, 2);
117             assertTrue("Didn't throw exception", false);
118         } catch (Exception e) {
119             assertTrue(true);
120         }
121 
122     }
123     
124     public void testSumSq() {
125         double[] x = null;
126         
127         // test null
128         try {
129             StatUtils.sumSq(x);
130             fail("null is not a valid data array.");
131         } catch (IllegalArgumentException ex) {
132             // success
133         }
134         
135         try {
136             StatUtils.sumSq(x, 0, 4);
137             fail("null is not a valid data array.");
138         } catch (IllegalArgumentException ex) {
139             // success
140         }
141         
142         // test empty
143         x = new double[] {};
144         TestUtils.assertEquals(Double.NaN, StatUtils.sumSq(x), tolerance);
145         TestUtils.assertEquals(Double.NaN, StatUtils.sumSq(x, 0, 0), tolerance);
146         
147         // test one
148         x = new double[] {two};
149         TestUtils.assertEquals(4, StatUtils.sumSq(x), tolerance);
150         TestUtils.assertEquals(4, StatUtils.sumSq(x, 0, 1), tolerance);
151         
152         // test many
153         x = new double[] {one, two, two, three};
154         TestUtils.assertEquals(18, StatUtils.sumSq(x), tolerance);
155         TestUtils.assertEquals(8, StatUtils.sumSq(x, 1, 2), tolerance);
156     }
157     
158     public void testProduct() {
159         double[] x = null;
160         
161         // test null
162         try {
163             StatUtils.product(x);
164             fail("null is not a valid data array.");
165         } catch (IllegalArgumentException ex) {
166             // success
167         }
168         
169         try {
170             StatUtils.product(x, 0, 4);
171             fail("null is not a valid data array.");
172         } catch (IllegalArgumentException ex) {
173             // success
174         }
175         
176         // test empty
177         x = new double[] {};
178         TestUtils.assertEquals(Double.NaN, StatUtils.product(x), tolerance);
179         TestUtils.assertEquals(Double.NaN, StatUtils.product(x, 0, 0), tolerance);
180         
181         // test one
182         x = new double[] {two};
183         TestUtils.assertEquals(two, StatUtils.product(x), tolerance);
184         TestUtils.assertEquals(two, StatUtils.product(x, 0, 1), tolerance);
185         
186         // test many
187         x = new double[] {one, two, two, three};
188         TestUtils.assertEquals(12, StatUtils.product(x), tolerance);
189         TestUtils.assertEquals(4, StatUtils.product(x, 1, 2), tolerance);
190     }
191     
192     public void testSumLog() {
193         double[] x = null;
194         
195         // test null
196         try {
197             StatUtils.sumLog(x);
198             fail("null is not a valid data array.");
199         } catch (IllegalArgumentException ex) {
200             // success
201         }
202         
203         try {
204             StatUtils.sumLog(x, 0, 4);
205             fail("null is not a valid data array.");
206         } catch (IllegalArgumentException ex) {
207             // success
208         }
209         
210         // test empty
211         x = new double[] {};
212         TestUtils.assertEquals(Double.NaN, StatUtils.sumLog(x), tolerance);
213         TestUtils.assertEquals(Double.NaN, StatUtils.sumLog(x, 0, 0), tolerance);
214         
215         // test one
216         x = new double[] {two};
217         TestUtils.assertEquals(Math.log(two), StatUtils.sumLog(x), tolerance);
218         TestUtils.assertEquals(Math.log(two), StatUtils.sumLog(x, 0, 1), tolerance);
219         
220         // test many
221         x = new double[] {one, two, two, three};
222         TestUtils.assertEquals(Math.log(one) + 2.0 * Math.log(two) + Math.log(three), StatUtils.sumLog(x), tolerance);
223         TestUtils.assertEquals(2.0 * Math.log(two), StatUtils.sumLog(x, 1, 2), tolerance);
224     }
225     
226     public void testMean() {
227         double[] x = null;
228         
229         try {
230             StatUtils.mean(x, 0, 4);
231             fail("null is not a valid data array.");
232         } catch (IllegalArgumentException ex) {
233             // success
234         }
235         
236         // test empty
237         x = new double[] {};
238         TestUtils.assertEquals(Double.NaN, StatUtils.mean(x, 0, 0), tolerance);
239         
240         // test one
241         x = new double[] {two};
242         TestUtils.assertEquals(two, StatUtils.mean(x, 0, 1), tolerance);
243         
244         // test many
245         x = new double[] {one, two, two, three};
246         TestUtils.assertEquals(2.5, StatUtils.mean(x, 2, 2), tolerance);
247     }
248     
249     public void testVariance() {
250         double[] x = null;
251         
252         try {
253             StatUtils.variance(x, 0, 4);
254             fail("null is not a valid data array.");
255         } catch (IllegalArgumentException ex) {
256             // success
257         }
258         
259         // test empty
260         x = new double[] {};
261         TestUtils.assertEquals(Double.NaN, StatUtils.variance(x, 0, 0), tolerance);
262         
263         // test one
264         x = new double[] {two};
265         TestUtils.assertEquals(0.0, StatUtils.variance(x, 0, 1), tolerance);
266         
267         // test many
268         x = new double[] {one, two, two, three};
269         TestUtils.assertEquals(0.5, StatUtils.variance(x, 2, 2), tolerance);
270         
271         // test precomputed mean
272         x = new double[] {one, two, two, three};
273         TestUtils.assertEquals(0.5, StatUtils.variance(x,2.5, 2, 2), tolerance);
274     }
275     
276     public void testMax() {
277         double[] x = null;
278         
279         try {
280             StatUtils.max(x, 0, 4);
281             fail("null is not a valid data array.");
282         } catch (IllegalArgumentException ex) {
283             // success
284         }
285         
286         // test empty
287         x = new double[] {};
288         TestUtils.assertEquals(Double.NaN, StatUtils.max(x, 0, 0), tolerance);
289         
290         // test one
291         x = new double[] {two};
292         TestUtils.assertEquals(two, StatUtils.max(x, 0, 1), tolerance);
293         
294         // test many
295         x = new double[] {one, two, two, three};
296         TestUtils.assertEquals(three, StatUtils.max(x, 1, 3), tolerance);
297 
298         // test first nan is ignored
299         x = new double[] {nan, two, three};
300         TestUtils.assertEquals(three, StatUtils.max(x), tolerance);
301 
302         // test middle nan is ignored
303         x = new double[] {one, nan, three};
304         TestUtils.assertEquals(three, StatUtils.max(x), tolerance);
305         
306         // test last nan is ignored
307         x = new double[] {one, two, nan};
308         TestUtils.assertEquals(two, StatUtils.max(x), tolerance);
309 
310         // test all nan returns nan
311         x = new double[] {nan, nan, nan};
312         TestUtils.assertEquals(nan, StatUtils.max(x), tolerance);
313     }
314     
315     public void testMin() {
316         double[] x = null;
317         
318         try {
319             StatUtils.min(x, 0, 4);
320             fail("null is not a valid data array.");
321         } catch (IllegalArgumentException ex) {
322             // success
323         }
324         
325         // test empty
326         x = new double[] {};
327         TestUtils.assertEquals(Double.NaN, StatUtils.min(x, 0, 0), tolerance);
328         
329         // test one
330         x = new double[] {two};
331         TestUtils.assertEquals(two, StatUtils.min(x, 0, 1), tolerance);
332         
333         // test many
334         x = new double[] {one, two, two, three};
335         TestUtils.assertEquals(two, StatUtils.min(x, 1, 3), tolerance);
336 
337         // test first nan is ignored
338         x = new double[] {nan, two, three};
339         TestUtils.assertEquals(two, StatUtils.min(x), tolerance);
340 
341         // test middle nan is ignored
342         x = new double[] {one, nan, three};
343         TestUtils.assertEquals(one, StatUtils.min(x), tolerance);
344         
345         // test last nan is ignored
346         x = new double[] {one, two, nan};
347         TestUtils.assertEquals(one, StatUtils.min(x), tolerance);
348 
349         // test all nan returns nan
350         x = new double[] {nan, nan, nan};
351         TestUtils.assertEquals(nan, StatUtils.min(x), tolerance);
352     }
353     
354     public void testPercentile() {
355         double[] x = null;
356         
357         // test null
358         try {
359             StatUtils.percentile(x, .25);
360             fail("null is not a valid data array.");
361         } catch (IllegalArgumentException ex) {
362             // success
363         }
364         
365         try {
366             StatUtils.percentile(x, 0, 4, 0.25);
367             fail("null is not a valid data array.");
368         } catch (IllegalArgumentException ex) {
369             // success
370         }
371         
372         // test empty
373         x = new double[] {};
374         TestUtils.assertEquals(Double.NaN, StatUtils.percentile(x, 25), tolerance);
375         TestUtils.assertEquals(Double.NaN, StatUtils.percentile(x, 0, 0, 25), tolerance);
376         
377         // test one
378         x = new double[] {two};
379         TestUtils.assertEquals(two, StatUtils.percentile(x, 25), tolerance);
380         TestUtils.assertEquals(two, StatUtils.percentile(x, 0, 1, 25), tolerance);
381         
382         // test many
383         x = new double[] {one, two, two, three};
384         TestUtils.assertEquals(2.5, StatUtils.percentile(x, 70), tolerance);
385         TestUtils.assertEquals(2.5, StatUtils.percentile(x, 1, 3, 62.5), tolerance);
386     }
387     
388     public void testDifferenceStats() throws Exception {
389         double sample1[] = {1d, 2d, 3d, 4d};
390         double sample2[] = {1d, 3d, 4d, 2d};
391         double diff[] = {0d, -1d, -1d, 2d};
392         double small[] = {1d, 4d};
393         double meanDifference = StatUtils.meanDifference(sample1, sample2);
394         assertEquals(StatUtils.sumDifference(sample1, sample2), StatUtils.sum(diff), tolerance);
395         assertEquals(meanDifference, StatUtils.mean(diff), tolerance);
396         assertEquals(StatUtils.varianceDifference(sample1, sample2, meanDifference), 
397                 StatUtils.variance(diff), tolerance);
398         try {
399             StatUtils.meanDifference(sample1, small);
400             fail("Expecting IllegalArgumentException");
401         } catch (IllegalArgumentException ex) {
402             // expected
403         }
404         try {
405             StatUtils.varianceDifference(sample1, small, meanDifference);
406             fail("Expecting IllegalArgumentException");
407         } catch (IllegalArgumentException ex) {
408             // expected
409         }
410         try {
411             double[] single = {1.0};
412             StatUtils.varianceDifference(single, single, meanDifference);
413             fail("Expecting IllegalArgumentException");
414         } catch (IllegalArgumentException ex) {
415             // expected
416         }
417     }
418     
419     public void testGeometricMean() throws Exception {
420         double[] test = null;
421         try {
422             StatUtils.geometricMean(test);
423             fail("Expecting IllegalArgumentException");
424         } catch (IllegalArgumentException ex) {
425             // expected
426         }
427         test = new double[] {2, 4, 6, 8};
428         assertEquals(Math.exp(0.25d * StatUtils.sumLog(test)), 
429                 StatUtils.geometricMean(test), Double.MIN_VALUE);
430         assertEquals(Math.exp(0.5 * StatUtils.sumLog(test, 0, 2)), 
431                 StatUtils.geometricMean(test, 0, 2), Double.MIN_VALUE);
432     }
433 }