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.descriptive;
18  
19  import java.util.Locale;
20  
21  import org.apache.commons.math4.legacy.TestUtils;
22  import org.apache.commons.math4.legacy.exception.MathIllegalArgumentException;
23  import org.apache.commons.math4.legacy.stat.descriptive.moment.GeometricMean;
24  import org.apache.commons.math4.legacy.stat.descriptive.moment.Mean;
25  import org.apache.commons.math4.legacy.stat.descriptive.moment.Variance;
26  import org.apache.commons.math4.legacy.stat.descriptive.rank.Max;
27  import org.apache.commons.math4.legacy.stat.descriptive.rank.Min;
28  import org.apache.commons.math4.legacy.stat.descriptive.rank.Percentile;
29  import org.apache.commons.math4.legacy.stat.descriptive.summary.Sum;
30  import org.apache.commons.math4.legacy.stat.descriptive.summary.SumOfSquares;
31  import org.apache.commons.numbers.core.Precision;
32  import org.apache.commons.rng.UniformRandomProvider;
33  import org.apache.commons.rng.simple.RandomSource;
34  import org.junit.Assert;
35  import org.junit.Test;
36  
37  /**
38   * Test cases for the {@link DescriptiveStatistics} class.
39   */
40  public class DescriptiveStatisticsTest {
41      private static UniformRandomProvider random = RandomSource.WELL_1024_A.create(2345789432894L);
42  
43      protected DescriptiveStatistics createDescriptiveStatistics() {
44          return new DescriptiveStatistics();
45      }
46  
47      @Test
48      public void testSetterInjection() {
49          DescriptiveStatistics stats = createDescriptiveStatistics();
50          stats.addValue(1);
51          stats.addValue(3);
52          Assert.assertEquals(2, stats.getMean(), 1E-10);
53          // Now lets try some new math
54          stats.setMeanImpl(new DeepMean());
55          Assert.assertEquals(42, stats.getMean(), 1E-10);
56      }
57  
58      @Test
59      public void testCopy() {
60          DescriptiveStatistics stats = createDescriptiveStatistics();
61          stats.addValue(1);
62          stats.addValue(3);
63          DescriptiveStatistics copy = new DescriptiveStatistics(stats);
64          Assert.assertEquals(2, copy.getMean(), 1E-10);
65          // Now lets try some new math
66          stats.setMeanImpl(new DeepMean());
67          copy = stats.copy();
68          Assert.assertEquals(42, copy.getMean(), 1E-10);
69      }
70  
71      @Test
72      public void testWindowSize() {
73          DescriptiveStatistics stats = createDescriptiveStatistics();
74          stats.setWindowSize(300);
75          for (int i = 0; i < 100; ++i) {
76              stats.addValue(i + 1);
77          }
78          int refSum = (100 * 101) / 2;
79          Assert.assertEquals(refSum / 100.0, stats.getMean(), 1E-10);
80          Assert.assertEquals(300, stats.getWindowSize());
81          try {
82              stats.setWindowSize(-3);
83              Assert.fail("an exception should have been thrown");
84          } catch (MathIllegalArgumentException iae) {
85              // expected
86          }
87          Assert.assertEquals(300, stats.getWindowSize());
88          stats.setWindowSize(50);
89          Assert.assertEquals(50, stats.getWindowSize());
90          int refSum2 = refSum - (50 * 51) / 2;
91          Assert.assertEquals(refSum2 / 50.0, stats.getMean(), 1E-10);
92      }
93  
94      @Test
95      public void testGetValues() {
96          DescriptiveStatistics stats = createDescriptiveStatistics();
97          for (int i = 100; i > 0; --i) {
98              stats.addValue(i);
99          }
100         int refSum = (100 * 101) / 2;
101         Assert.assertEquals(refSum / 100.0, stats.getMean(), 1E-10);
102         double[] v = stats.getValues();
103         for (int i = 0; i < v.length; ++i) {
104             Assert.assertEquals(100.0 - i, v[i], 1.0e-10);
105         }
106         double[] s = stats.getSortedValues();
107         for (int i = 0; i < s.length; ++i) {
108             Assert.assertEquals(i + 1.0, s[i], 1.0e-10);
109         }
110         Assert.assertEquals(12.0, stats.getElement(88), 1.0e-10);
111     }
112 
113     @Test
114     public void testQuadraticMean() {
115         final double[] values = { 1.2, 3.4, 5.6, 7.89 };
116         final DescriptiveStatistics stats = new DescriptiveStatistics(values);
117 
118         final int len = values.length;
119         double expected = 0;
120         for (int i = 0; i < len; i++) {
121             final double v = values[i];
122             expected += v * v / len;
123         }
124         expected = Math.sqrt(expected);
125 
126         Assert.assertEquals(expected, stats.getQuadraticMean(), Math.ulp(expected));
127     }
128 
129     @Test
130     public void testToString() {
131         DescriptiveStatistics stats = createDescriptiveStatistics();
132         stats.addValue(1);
133         stats.addValue(2);
134         stats.addValue(3);
135         Locale d = Locale.getDefault();
136         Locale.setDefault(Locale.US);
137         Assert.assertEquals("DescriptiveStatistics:\n" +
138                      "n: 3\n" +
139                      "min: 1.0\n" +
140                      "max: 3.0\n" +
141                      "mean: 2.0\n" +
142                      "std dev: 1.0\n" +
143                      "median: 2.0\n" +
144                      "skewness: 0.0\n" +
145                      "kurtosis: NaN\n",  stats.toString());
146         Locale.setDefault(d);
147     }
148 
149     @Test
150     public void testShuffledStatistics() {
151         // the purpose of this test is only to check the get/set methods
152         // we are aware shuffling statistics like this is really not
153         // something sensible to do in production ...
154         DescriptiveStatistics reference = createDescriptiveStatistics();
155         DescriptiveStatistics shuffled  = createDescriptiveStatistics();
156 
157         UnivariateStatistic tmp = shuffled.getGeometricMeanImpl();
158         shuffled.setGeometricMeanImpl(shuffled.getMeanImpl());
159         shuffled.setMeanImpl(shuffled.getKurtosisImpl());
160         shuffled.setKurtosisImpl(shuffled.getSkewnessImpl());
161         shuffled.setSkewnessImpl(shuffled.getVarianceImpl());
162         shuffled.setVarianceImpl(shuffled.getMaxImpl());
163         shuffled.setMaxImpl(shuffled.getMinImpl());
164         shuffled.setMinImpl(shuffled.getSumImpl());
165         shuffled.setSumImpl(shuffled.getSumsqImpl());
166         shuffled.setSumsqImpl(tmp);
167 
168         for (int i = 100; i > 0; --i) {
169             reference.addValue(i);
170             shuffled.addValue(i);
171         }
172 
173         Assert.assertEquals(reference.getMean(),          shuffled.getGeometricMean(), 1.0e-10);
174         Assert.assertEquals(reference.getKurtosis(),      shuffled.getMean(),          1.0e-10);
175         Assert.assertEquals(reference.getSkewness(),      shuffled.getKurtosis(), 1.0e-10);
176         Assert.assertEquals(reference.getVariance(),      shuffled.getSkewness(), 1.0e-10);
177         Assert.assertEquals(reference.getMax(),           shuffled.getVariance(), 1.0e-10);
178         Assert.assertEquals(reference.getMin(),           shuffled.getMax(), 1.0e-10);
179         Assert.assertEquals(reference.getSum(),           shuffled.getMin(), 1.0e-10);
180         Assert.assertEquals(reference.getSumsq(),         shuffled.getSum(), 1.0e-10);
181         Assert.assertEquals(reference.getGeometricMean(), shuffled.getSumsq(), 1.0e-10);
182     }
183 
184     @Test
185     public void testPercentileSetter() {
186         DescriptiveStatistics stats = createDescriptiveStatistics();
187         stats.addValue(1);
188         stats.addValue(2);
189         stats.addValue(3);
190         Assert.assertEquals(2, stats.getPercentile(50.0), 1E-10);
191 
192         // Inject wrapped Percentile impl
193         stats.setPercentileImpl(new GoodPercentile());
194         Assert.assertEquals(2, stats.getPercentile(50.0), 1E-10);
195 
196         // Try "new math" impl
197         stats.setPercentileImpl(new SubPercentile());
198         Assert.assertEquals(10.0, stats.getPercentile(10.0), 1E-10);
199 
200         // Try to set bad impl
201         try {
202             stats.setPercentileImpl(new BadPercentile());
203             Assert.fail("Expecting MathIllegalArgumentException");
204         } catch (MathIllegalArgumentException ex) {
205             // expected
206         }
207     }
208 
209     @Test
210     public void test20090720() {
211         DescriptiveStatistics descriptiveStatistics = new DescriptiveStatistics(100);
212         for (int i = 0; i < 161; i++) {
213             descriptiveStatistics.addValue(1.2);
214         }
215         descriptiveStatistics.clear();
216         descriptiveStatistics.addValue(1.2);
217         Assert.assertEquals(1, descriptiveStatistics.getN());
218     }
219 
220     @Test
221     public void testRemoval() {
222 
223         final DescriptiveStatistics dstat = createDescriptiveStatistics();
224 
225         checkremoval(dstat, 1, 6.0, 0.0, Double.NaN);
226         checkremoval(dstat, 3, 5.0, 3.0, 4.5);
227         checkremoval(dstat, 6, 3.5, 2.5, 3.0);
228         checkremoval(dstat, 9, 3.5, 2.5, 3.0);
229         checkremoval(dstat, DescriptiveStatistics.INFINITE_WINDOW, 3.5, 2.5, 3.0);
230     }
231 
232     @Test
233     public void testSummaryConsistency() {
234         final DescriptiveStatistics dstats = new DescriptiveStatistics();
235         final SummaryStatistics sstats = new SummaryStatistics();
236         final int windowSize = 5;
237         dstats.setWindowSize(windowSize);
238         final double tol = 1E-12;
239         for (int i = 0; i < 20; i++) {
240             dstats.addValue(i);
241             sstats.clear();
242             double[] values = dstats.getValues();
243             for (int j = 0; j < values.length; j++) {
244                 sstats.addValue(values[j]);
245             }
246             TestUtils.assertEquals(dstats.getMean(), sstats.getMean(), tol);
247             TestUtils.assertEquals(new Mean().evaluate(values), dstats.getMean(), tol);
248             TestUtils.assertEquals(dstats.getMax(), sstats.getMax(), tol);
249             TestUtils.assertEquals(new Max().evaluate(values), dstats.getMax(), tol);
250             TestUtils.assertEquals(dstats.getGeometricMean(), sstats.getGeometricMean(), tol);
251             TestUtils.assertEquals(new GeometricMean().evaluate(values), dstats.getGeometricMean(), tol);
252             TestUtils.assertEquals(dstats.getMin(), sstats.getMin(), tol);
253             TestUtils.assertEquals(new Min().evaluate(values), dstats.getMin(), tol);
254             TestUtils.assertEquals(dstats.getStandardDeviation(), sstats.getStandardDeviation(), tol);
255             TestUtils.assertEquals(dstats.getVariance(), sstats.getVariance(), tol);
256             TestUtils.assertEquals(new Variance().evaluate(values), dstats.getVariance(), tol);
257             TestUtils.assertEquals(dstats.getSum(), sstats.getSum(), tol);
258             TestUtils.assertEquals(new Sum().evaluate(values), dstats.getSum(), tol);
259             TestUtils.assertEquals(dstats.getSumsq(), sstats.getSumsq(), tol);
260             TestUtils.assertEquals(new SumOfSquares().evaluate(values), dstats.getSumsq(), tol);
261             TestUtils.assertEquals(dstats.getPopulationVariance(), sstats.getPopulationVariance(), tol);
262             TestUtils.assertEquals(new Variance(false).evaluate(values), dstats.getPopulationVariance(), tol);
263         }
264     }
265 
266     @Test
267     public void testMath1129(){
268         final double[] data = new double[] {
269             -0.012086732064244697,
270             -0.24975668704012527,
271             0.5706168483164684,
272             -0.322111769955327,
273             0.24166759508327315,
274             Double.NaN,
275             0.16698443218942854,
276             -0.10427763937565114,
277             -0.15595963093172435,
278             -0.028075857595882995,
279             -0.24137994506058857,
280             0.47543170476574426,
281             -0.07495595384947631,
282             0.37445697625436497,
283             -0.09944199541668033
284         };
285 
286         final DescriptiveStatistics ds = new DescriptiveStatistics(data);
287 
288         final double t = ds.getPercentile(75);
289         final double o = ds.getPercentile(25);
290 
291         final double iqr = t - o;
292         // System.out.println(String.format("25th percentile %s 75th percentile %s", o, t));
293         Assert.assertTrue(iqr >= 0);
294     }
295 
296     @Test
297     public void testInit0() {
298         //test window constructor
299         int window = 1 + random.nextInt(Integer.MAX_VALUE-1);
300         DescriptiveStatistics instance = new DescriptiveStatistics(window);
301         Assert.assertEquals(window,
302                             instance.getWindowSize());
303     }
304 
305     @Test
306     public void testInitDouble() {
307         //test double[] constructor
308         double[] initialDoubleArray = null;
309         new DescriptiveStatistics(initialDoubleArray);
310             //a null argument corresponds to DescriptiveStatistics(), so test
311             //that no exception is thrown
312         int initialDoubleArraySize = random.nextInt(1024 //some random
313             //memory consumption and test size limitation
314         );
315 //        System.out.println(String.format("initialDoubleArraySize: %s",
316 //                initialDoubleArraySize));
317         initialDoubleArray = new double[initialDoubleArraySize];
318         for(int i = 0; i < initialDoubleArraySize; i++) {
319             double value = random.nextDouble();
320             initialDoubleArray[i] = value;
321         }
322         new DescriptiveStatistics(initialDoubleArray);
323     }
324 
325     @Test
326     public void testInitDoubleWrapper() {
327         //test Double[] constructor
328         Double[] initialDoubleWrapperArray = null;
329         new DescriptiveStatistics(initialDoubleWrapperArray);
330         int initialDoubleWrapperArraySize = random.nextInt(1024 //some random
331             //memory consumption and test size limitation
332         );
333         initialDoubleWrapperArray = generateInitialDoubleArray(initialDoubleWrapperArraySize);
334         new DescriptiveStatistics(initialDoubleWrapperArray);
335     }
336 
337     @Test
338     public void testInitCopy() {
339         //test copy constructor
340         int initialDoubleArray = random.nextInt(1024 //some random
341             //memory consumption and test size limitation
342         );
343         DescriptiveStatistics original = new DescriptiveStatistics(initialDoubleArray);
344         DescriptiveStatistics instance = new DescriptiveStatistics(original);
345         Assert.assertEquals(original.getGeometricMean(),
346                             instance.getGeometricMean(),
347                             0);
348         Assert.assertEquals(original.getKurtosis(),
349                             instance.getKurtosis(),
350                             0);
351         Assert.assertEquals(original.getMax(),
352                             instance.getMax(),
353                             0);
354         Assert.assertEquals(original.getMean(),
355                             instance.getMean(),
356                             0);
357         Assert.assertEquals(original.getMin(),
358                             instance.getMin(),
359                             0);
360         Assert.assertEquals(original.getN(),
361                             instance.getN());
362         Assert.assertEquals(original.getSkewness(),
363                             instance.getSkewness(),
364                             0);
365         Assert.assertArrayEquals(original.getValues(),
366                                  instance.getValues(),
367                                  0);
368         Assert.assertEquals(original.getWindowSize(),
369                             instance.getWindowSize());
370             //doesn't implement equals
371     }
372 
373     public void checkremoval(DescriptiveStatistics dstat, int wsize,
374                              double mean1, double mean2, double mean3) {
375 
376         dstat.setWindowSize(wsize);
377         dstat.clear();
378 
379         for (int i = 1 ; i <= 6 ; ++i) {
380             dstat.addValue(i);
381         }
382 
383         Assert.assertTrue(Precision.equalsIncludingNaN(mean1, dstat.getMean()));
384         dstat.replaceMostRecentValue(0);
385         Assert.assertTrue(Precision.equalsIncludingNaN(mean2, dstat.getMean()));
386         dstat.removeMostRecentValue();
387         Assert.assertTrue(Precision.equalsIncludingNaN(mean3, dstat.getMean()));
388     }
389 
390     private Double[] generateInitialDoubleArray(int size) {
391         Double[] retValue = new Double[size];
392         for(int i = 0; i < size; i++) {
393             Double value = random.nextDouble();
394             retValue[i] = value;
395         }
396         return retValue;
397     }
398 
399     // Test UnivariateStatistics impls for setter injection tests
400 
401     /**
402      * A new way to compute the mean
403      */
404     static class DeepMean implements UnivariateStatistic {
405 
406         @Override
407         public double evaluate(double[] values, int begin, int length) {
408             return 42;
409         }
410 
411         @Override
412         public double evaluate(double[] values) {
413             return 42;
414         }
415         @Override
416         public UnivariateStatistic copy() {
417             return new DeepMean();
418         }
419     }
420 
421     /**
422      * Test percentile implementation - wraps a Percentile
423      */
424     static class GoodPercentile implements UnivariateStatistic {
425         private final Percentile percentile = new Percentile();
426         public void setQuantile(double quantile) {
427             percentile.setQuantile(quantile);
428         }
429         @Override
430         public double evaluate(double[] values, int begin, int length) {
431             return percentile.evaluate(values, begin, length);
432         }
433         @Override
434         public double evaluate(double[] values) {
435             return percentile.evaluate(values);
436         }
437         @Override
438         public UnivariateStatistic copy() {
439             GoodPercentile result = new GoodPercentile();
440             result.setQuantile(percentile.getQuantile());
441             return result;
442         }
443     }
444 
445     /**
446      * Test percentile subclass - another "new math" impl
447      * Always returns currently set quantile
448      */
449     static class SubPercentile extends Percentile {
450         @Override
451         public double evaluate(double[] values, int begin, int length) {
452             return getQuantile();
453         }
454         @Override
455         public double evaluate(double[] values) {
456             return getQuantile();
457         }
458         private static final long serialVersionUID = 8040701391045914979L;
459         @Override
460         public Percentile copy() {
461             SubPercentile result = new SubPercentile();
462             return result;
463         }
464     }
465 
466     /**
467      * "Bad" test percentile implementation - no setQuantile
468      */
469     static class BadPercentile implements UnivariateStatistic {
470         private final Percentile percentile = new Percentile();
471         @Override
472         public double evaluate(double[] values, int begin, int length) {
473             return percentile.evaluate(values, begin, length);
474         }
475         @Override
476         public double evaluate(double[] values) {
477             return percentile.evaluate(values);
478         }
479         @Override
480         public UnivariateStatistic copy() {
481             return new BadPercentile();
482         }
483     }
484 }