001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.commons.math3.stat;
018    
019    
020    import org.apache.commons.math3.TestUtils;
021    import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;
022    import org.apache.commons.math3.util.FastMath;
023    import org.apache.commons.math3.util.Precision;
024    import org.apache.commons.math3.exception.MathIllegalArgumentException;
025    import org.junit.Assert;
026    import org.junit.Test;
027    
028    /**
029     * Test cases for the {@link StatUtils} class.
030     * @version $Id: StatUtilsTest.java 1364030 2012-07-21 01:10:04Z erans $
031     */
032    
033    public final class StatUtilsTest {
034    
035        private double one = 1;
036        private float two = 2;
037        private int three = 3;
038        private double mean = 2;
039        private double sumSq = 18;
040        private double sum = 8;
041        private double var = 0.666666666666666666667;
042        private double min = 1;
043        private double max = 3;
044        private double tolerance = 10E-15;
045        private double nan = Double.NaN;
046    
047        /** test stats */
048        @Test
049        public void testStats() {
050            double[] values = new double[] { one, two, two, three };
051            Assert.assertEquals("sum", sum, StatUtils.sum(values), tolerance);
052            Assert.assertEquals("sumsq", sumSq, StatUtils.sumSq(values), tolerance);
053            Assert.assertEquals("var", var, StatUtils.variance(values), tolerance);
054            Assert.assertEquals("var with mean", var, StatUtils.variance(values, mean), tolerance);
055            Assert.assertEquals("mean", mean, StatUtils.mean(values), tolerance);
056            Assert.assertEquals("min", min, StatUtils.min(values), tolerance);
057            Assert.assertEquals("max", max, StatUtils.max(values), tolerance);
058        }
059    
060        @Test
061        public void testN0andN1Conditions() {
062            double[] values = new double[0];
063    
064            Assert.assertTrue(
065                "Mean of n = 0 set should be NaN",
066                Double.isNaN(StatUtils.mean(values)));
067            Assert.assertTrue(
068                "Variance of n = 0 set should be NaN",
069                Double.isNaN(StatUtils.variance(values)));
070    
071            values = new double[] { one };
072    
073            Assert.assertTrue(
074                "Mean of n = 1 set should be value of single item n1",
075                StatUtils.mean(values) == one);
076            Assert.assertTrue(
077                "Variance of n = 1 set should be zero",
078                StatUtils.variance(values) == 0);
079        }
080    
081        @Test
082        public void testArrayIndexConditions() {
083            double[] values = { 1.0, 2.0, 3.0, 4.0 };
084    
085            Assert.assertEquals(
086                "Sum not expected",
087                5.0,
088                StatUtils.sum(values, 1, 2),
089                Double.MIN_VALUE);
090            Assert.assertEquals(
091                "Sum not expected",
092                3.0,
093                StatUtils.sum(values, 0, 2),
094                Double.MIN_VALUE);
095            Assert.assertEquals(
096                "Sum not expected",
097                7.0,
098                StatUtils.sum(values, 2, 2),
099                Double.MIN_VALUE);
100    
101            try {
102                StatUtils.sum(values, 2, 3);
103                Assert.fail("Expected RuntimeException");
104            } catch (RuntimeException e) {
105                // expected
106            }
107    
108            try {
109                StatUtils.sum(values, -1, 2);
110                Assert.fail("Expected RuntimeException");
111            } catch (RuntimeException e) {
112                // expected
113            }
114    
115        }
116    
117        @Test
118        public void testSumSq() {
119            double[] x = null;
120    
121            // test null
122            try {
123                StatUtils.sumSq(x);
124                Assert.fail("null is not a valid data array.");
125            } catch (MathIllegalArgumentException ex) {
126                // success
127            }
128    
129            try {
130                StatUtils.sumSq(x, 0, 4);
131                Assert.fail("null is not a valid data array.");
132            } catch (MathIllegalArgumentException ex) {
133                // success
134            }
135    
136            // test empty
137            x = new double[] {};
138            TestUtils.assertEquals(0, StatUtils.sumSq(x), tolerance);
139            TestUtils.assertEquals(0, StatUtils.sumSq(x, 0, 0), tolerance);
140    
141            // test one
142            x = new double[] {two};
143            TestUtils.assertEquals(4, StatUtils.sumSq(x), tolerance);
144            TestUtils.assertEquals(4, StatUtils.sumSq(x, 0, 1), tolerance);
145    
146            // test many
147            x = new double[] {one, two, two, three};
148            TestUtils.assertEquals(18, StatUtils.sumSq(x), tolerance);
149            TestUtils.assertEquals(8, StatUtils.sumSq(x, 1, 2), tolerance);
150        }
151    
152        @Test
153        public void testProduct() {
154            double[] x = null;
155    
156            // test null
157            try {
158                StatUtils.product(x);
159                Assert.fail("null is not a valid data array.");
160            } catch (MathIllegalArgumentException ex) {
161                // success
162            }
163    
164            try {
165                StatUtils.product(x, 0, 4);
166                Assert.fail("null is not a valid data array.");
167            } catch (MathIllegalArgumentException ex) {
168                // success
169            }
170    
171            // test empty
172            x = new double[] {};
173            TestUtils.assertEquals(1, StatUtils.product(x), tolerance);
174            TestUtils.assertEquals(1, StatUtils.product(x, 0, 0), tolerance);
175    
176            // test one
177            x = new double[] {two};
178            TestUtils.assertEquals(two, StatUtils.product(x), tolerance);
179            TestUtils.assertEquals(two, StatUtils.product(x, 0, 1), tolerance);
180    
181            // test many
182            x = new double[] {one, two, two, three};
183            TestUtils.assertEquals(12, StatUtils.product(x), tolerance);
184            TestUtils.assertEquals(4, StatUtils.product(x, 1, 2), tolerance);
185        }
186    
187        @Test
188        public void testSumLog() {
189            double[] x = null;
190    
191            // test null
192            try {
193                StatUtils.sumLog(x);
194                Assert.fail("null is not a valid data array.");
195            } catch (MathIllegalArgumentException ex) {
196                // success
197            }
198    
199            try {
200                StatUtils.sumLog(x, 0, 4);
201                Assert.fail("null is not a valid data array.");
202            } catch (MathIllegalArgumentException ex) {
203                // success
204            }
205    
206            // test empty
207            x = new double[] {};
208            TestUtils.assertEquals(0, StatUtils.sumLog(x), tolerance);
209            TestUtils.assertEquals(0, StatUtils.sumLog(x, 0, 0), tolerance);
210    
211            // test one
212            x = new double[] {two};
213            TestUtils.assertEquals(FastMath.log(two), StatUtils.sumLog(x), tolerance);
214            TestUtils.assertEquals(FastMath.log(two), StatUtils.sumLog(x, 0, 1), tolerance);
215    
216            // test many
217            x = new double[] {one, two, two, three};
218            TestUtils.assertEquals(FastMath.log(one) + 2.0 * FastMath.log(two) + FastMath.log(three), StatUtils.sumLog(x), tolerance);
219            TestUtils.assertEquals(2.0 * FastMath.log(two), StatUtils.sumLog(x, 1, 2), tolerance);
220        }
221    
222        @Test
223        public void testMean() {
224            double[] x = null;
225    
226            try {
227                StatUtils.mean(x, 0, 4);
228                Assert.fail("null is not a valid data array.");
229            } catch (MathIllegalArgumentException ex) {
230                // success
231            }
232    
233            // test empty
234            x = new double[] {};
235            TestUtils.assertEquals(Double.NaN, StatUtils.mean(x, 0, 0), tolerance);
236    
237            // test one
238            x = new double[] {two};
239            TestUtils.assertEquals(two, StatUtils.mean(x, 0, 1), tolerance);
240    
241            // test many
242            x = new double[] {one, two, two, three};
243            TestUtils.assertEquals(2.5, StatUtils.mean(x, 2, 2), tolerance);
244        }
245    
246        @Test
247        public void testVariance() {
248            double[] x = null;
249    
250            try {
251                StatUtils.variance(x, 0, 4);
252                Assert.fail("null is not a valid data array.");
253            } catch (MathIllegalArgumentException ex) {
254                // success
255            }
256    
257            // test empty
258            x = new double[] {};
259            TestUtils.assertEquals(Double.NaN, StatUtils.variance(x, 0, 0), tolerance);
260    
261            // test one
262            x = new double[] {two};
263            TestUtils.assertEquals(0.0, StatUtils.variance(x, 0, 1), tolerance);
264    
265            // test many
266            x = new double[] {one, two, two, three};
267            TestUtils.assertEquals(0.5, StatUtils.variance(x, 2, 2), tolerance);
268    
269            // test precomputed mean
270            x = new double[] {one, two, two, three};
271            TestUtils.assertEquals(0.5, StatUtils.variance(x,2.5, 2, 2), tolerance);
272        }
273        
274        @Test
275        public void testPopulationVariance() {
276            double[] x = null;
277    
278            try {
279                StatUtils.variance(x, 0, 4);
280                Assert.fail("null is not a valid data array.");
281            } catch (MathIllegalArgumentException ex) {
282                // success
283            }
284    
285            // test empty
286            x = new double[] {};
287            TestUtils.assertEquals(Double.NaN, StatUtils.populationVariance(x, 0, 0), tolerance);
288    
289            // test one
290            x = new double[] {two};
291            TestUtils.assertEquals(0.0, StatUtils.populationVariance(x, 0, 1), tolerance);
292    
293            // test many
294            x = new double[] {one, two, two, three};
295            TestUtils.assertEquals(0.25, StatUtils.populationVariance(x, 0, 2), tolerance);
296    
297            // test precomputed mean
298            x = new double[] {one, two, two, three};
299            TestUtils.assertEquals(0.25, StatUtils.populationVariance(x, 2.5, 2, 2), tolerance);
300        }
301    
302    
303        @Test
304        public void testMax() {
305            double[] x = null;
306    
307            try {
308                StatUtils.max(x, 0, 4);
309                Assert.fail("null is not a valid data array.");
310            } catch (MathIllegalArgumentException ex) {
311                // success
312            }
313    
314            // test empty
315            x = new double[] {};
316            TestUtils.assertEquals(Double.NaN, StatUtils.max(x, 0, 0), tolerance);
317    
318            // test one
319            x = new double[] {two};
320            TestUtils.assertEquals(two, StatUtils.max(x, 0, 1), tolerance);
321    
322            // test many
323            x = new double[] {one, two, two, three};
324            TestUtils.assertEquals(three, StatUtils.max(x, 1, 3), tolerance);
325    
326            // test first nan is ignored
327            x = new double[] {nan, two, three};
328            TestUtils.assertEquals(three, StatUtils.max(x), tolerance);
329    
330            // test middle nan is ignored
331            x = new double[] {one, nan, three};
332            TestUtils.assertEquals(three, StatUtils.max(x), tolerance);
333    
334            // test last nan is ignored
335            x = new double[] {one, two, nan};
336            TestUtils.assertEquals(two, StatUtils.max(x), tolerance);
337    
338            // test all nan returns nan
339            x = new double[] {nan, nan, nan};
340            TestUtils.assertEquals(nan, StatUtils.max(x), tolerance);
341        }
342    
343        @Test
344        public void testMin() {
345            double[] x = null;
346    
347            try {
348                StatUtils.min(x, 0, 4);
349                Assert.fail("null is not a valid data array.");
350            } catch (MathIllegalArgumentException ex) {
351                // success
352            }
353    
354            // test empty
355            x = new double[] {};
356            TestUtils.assertEquals(Double.NaN, StatUtils.min(x, 0, 0), tolerance);
357    
358            // test one
359            x = new double[] {two};
360            TestUtils.assertEquals(two, StatUtils.min(x, 0, 1), tolerance);
361    
362            // test many
363            x = new double[] {one, two, two, three};
364            TestUtils.assertEquals(two, StatUtils.min(x, 1, 3), tolerance);
365    
366            // test first nan is ignored
367            x = new double[] {nan, two, three};
368            TestUtils.assertEquals(two, StatUtils.min(x), tolerance);
369    
370            // test middle nan is ignored
371            x = new double[] {one, nan, three};
372            TestUtils.assertEquals(one, StatUtils.min(x), tolerance);
373    
374            // test last nan is ignored
375            x = new double[] {one, two, nan};
376            TestUtils.assertEquals(one, StatUtils.min(x), tolerance);
377    
378            // test all nan returns nan
379            x = new double[] {nan, nan, nan};
380            TestUtils.assertEquals(nan, StatUtils.min(x), tolerance);
381        }
382    
383        @Test
384        public void testPercentile() {
385            double[] x = null;
386    
387            // test null
388            try {
389                StatUtils.percentile(x, .25);
390                Assert.fail("null is not a valid data array.");
391            } catch (MathIllegalArgumentException ex) {
392                // success
393            }
394    
395            try {
396                StatUtils.percentile(x, 0, 4, 0.25);
397                Assert.fail("null is not a valid data array.");
398            } catch (MathIllegalArgumentException ex) {
399                // success
400            }
401    
402            // test empty
403            x = new double[] {};
404            TestUtils.assertEquals(Double.NaN, StatUtils.percentile(x, 25), tolerance);
405            TestUtils.assertEquals(Double.NaN, StatUtils.percentile(x, 0, 0, 25), tolerance);
406    
407            // test one
408            x = new double[] {two};
409            TestUtils.assertEquals(two, StatUtils.percentile(x, 25), tolerance);
410            TestUtils.assertEquals(two, StatUtils.percentile(x, 0, 1, 25), tolerance);
411    
412            // test many
413            x = new double[] {one, two, two, three};
414            TestUtils.assertEquals(2.5, StatUtils.percentile(x, 70), tolerance);
415            TestUtils.assertEquals(2.5, StatUtils.percentile(x, 1, 3, 62.5), tolerance);
416        }
417    
418        @Test
419        public void testDifferenceStats() {
420            double sample1[] = {1d, 2d, 3d, 4d};
421            double sample2[] = {1d, 3d, 4d, 2d};
422            double diff[] = {0d, -1d, -1d, 2d};
423            double small[] = {1d, 4d};
424            double meanDifference = StatUtils.meanDifference(sample1, sample2);
425            Assert.assertEquals(StatUtils.sumDifference(sample1, sample2), StatUtils.sum(diff), tolerance);
426            Assert.assertEquals(meanDifference, StatUtils.mean(diff), tolerance);
427            Assert.assertEquals(StatUtils.varianceDifference(sample1, sample2, meanDifference),
428                    StatUtils.variance(diff), tolerance);
429            try {
430                StatUtils.meanDifference(sample1, small);
431                Assert.fail("Expecting MathIllegalArgumentException");
432            } catch (MathIllegalArgumentException ex) {
433                // expected
434            }
435            try {
436                StatUtils.varianceDifference(sample1, small, meanDifference);
437                Assert.fail("Expecting MathIllegalArgumentException");
438            } catch (MathIllegalArgumentException ex) {
439                // expected
440            }
441            try {
442                double[] single = {1.0};
443                StatUtils.varianceDifference(single, single, meanDifference);
444                Assert.fail("Expecting MathIllegalArgumentException");
445            } catch (MathIllegalArgumentException ex) {
446                // expected
447            }
448        }
449    
450        @Test
451        public void testGeometricMean() {
452            double[] test = null;
453            try {
454                StatUtils.geometricMean(test);
455                Assert.fail("Expecting MathIllegalArgumentException");
456            } catch (MathIllegalArgumentException ex) {
457                // expected
458            }
459            test = new double[] {2, 4, 6, 8};
460            Assert.assertEquals(FastMath.exp(0.25d * StatUtils.sumLog(test)),
461                    StatUtils.geometricMean(test), Double.MIN_VALUE);
462            Assert.assertEquals(FastMath.exp(0.5 * StatUtils.sumLog(test, 0, 2)),
463                    StatUtils.geometricMean(test, 0, 2), Double.MIN_VALUE);
464        }
465        
466        
467        /**
468         * Run the test with the values 50 and 100 and assume standardized values    
469         */
470    
471        @Test
472        public void testNormalize1() {
473            double sample[] = { 50, 100 };
474            double expectedSample[] = { -25 / Math.sqrt(1250), 25 / Math.sqrt(1250) };
475            double[] out = StatUtils.normalize(sample);
476            for (int i = 0; i < out.length; i++) {
477                Assert.assertTrue(Precision.equals(out[i], expectedSample[i], 1));
478            }
479    
480        }
481    
482        /**
483         * Run with 77 random values, assuming that the outcome has a mean of 0 and a standard deviation of 1 with a
484         * precision of 1E-10.
485         */
486    
487        @Test
488        public void testNormalize2() {
489            // create an sample with 77 values    
490            int length = 77;
491            double sample[] = new double[length];
492            for (int i = 0; i < length; i++) {
493                sample[i] = Math.random();
494            }
495            // normalize this sample
496            double standardizedSample[] = StatUtils.normalize(sample);
497    
498            DescriptiveStatistics stats = new DescriptiveStatistics();
499            // Add the data from the array
500            for (int i = 0; i < length; i++) {
501                stats.addValue(standardizedSample[i]);
502            }
503            // the calculations do have a limited precision    
504            double distance = 1E-10;
505            // check the mean an standard deviation
506            Assert.assertEquals(0.0, stats.getMean(), distance);
507            Assert.assertEquals(1.0, stats.getStandardDeviation(), distance);
508    
509        }
510        
511    }