/* JAI-Ext - OpenSource Java Advanced Image Extensions Library * http://www.geo-solutions.it/ * Copyright 2014 GeoSolutions * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package it.geosolutions.jaiext.stats; import static org.junit.Assert.*; import java.util.ArrayList; import java.util.Collections; import java.util.List; import it.geosolutions.jaiext.range.Range; import it.geosolutions.jaiext.range.RangeFactory; import org.junit.BeforeClass; import org.junit.Test; /** * This test-class verifies that the Statistics object is able to compute the statistics correctly. For achieving this purpose, all the subclasses of * the {@link Statistics} abstract class are tested. The first 3 tests compares the ability to calculate statistics of the subclasses. Then is checked * the ability to accumulate data previously calculated, by calling the method accumulateStats(). Also is tested the capability of clearing the stored * data. The last 7 tests evaluates if the subclasses are capable to throw an exception when they call the accumulateStats() method with another * Statistics object different from their type. */ public class StatisticsTest { /** Dimension of the random samples array */ private final static int ARRAY_DIMENSIONS = 100; /** Tolerance value used for comparison between double */ private final static double TOLERANCE = 0.1d; /** Random samples array */ private static double[] testArray; /** Object used for calculating the mean */ private static Statistics meanObj; /** Object used for calculating the sum */ private static Statistics sumObj; /** Object used for calculating the maximum */ private static Statistics maxObj; /** Object used for calculating the minimum */ private static Statistics minObj; /** Object used for calculating the extrema */ private static Statistics extremaObj; /** Object used for calculating the variance */ private static Statistics varianceObj; /** Object used for calculating the standard deviation */ private static Statistics devstdObj; /** Object used for calculating the histogram */ private static Statistics histogramObj; /** Object used for calculating the mode */ private static Statistics modeObj; /** Object used for calculating the median */ private static Statistics medianObj; /** Minimum bound for complex statistics */ private static double minBound; /** Maximum bound for complex statistics */ private static double maxBound; /** Bin size for complex statistics */ private static double binInterval; /** Bin number for complex statistics */ private static int numBins; /** Values interval for complex statistics */ private static Range interval; @BeforeClass public static void initialSetup() { // Creation of an array with random values testArray = new double[ARRAY_DIMENSIONS]; // Definition of the Histogram parameters minBound = -3; maxBound = 3; numBins = 20; binInterval = (maxBound - minBound) / numBins; interval = RangeFactory.create(minBound, true, maxBound, false, false); for (int i = 0; i < ARRAY_DIMENSIONS; i++) { testArray[i] = Math.random() * 2 + 2; } // Creation of the Statistics Object meanObj = StatsFactory.createSimpleStatisticsObjectFromInt(0); sumObj = StatsFactory.createSimpleStatisticsObjectFromInt(1); maxObj = StatsFactory.createSimpleStatisticsObjectFromInt(2); minObj = StatsFactory.createSimpleStatisticsObjectFromInt(3); extremaObj = StatsFactory.createSimpleStatisticsObjectFromInt(4); varianceObj = StatsFactory.createSimpleStatisticsObjectFromInt(5); devstdObj = StatsFactory.createSimpleStatisticsObjectFromInt(6); histogramObj = StatsFactory.createComplexStatisticsObjectFromInt(7, minBound, maxBound, numBins); modeObj = StatsFactory.createComplexStatisticsObjectFromInt(8, minBound, maxBound, numBins); medianObj = StatsFactory.createComplexStatisticsObjectFromInt(9, minBound, maxBound, numBins); } // Private method for calculating the bin/index related to the sample private int getIndex(double sample) { int index = (int) ((sample - minBound) / binInterval); return index; } // This test is used for checking if the mean and sum objects // have a correct behavior @Test public void testMeanAndSum() { double mean = 0; double sum = calculateSumMean(sumObj, meanObj); // Comparison double sum2 = (Double) (sumObj.getResult()); assertEquals(sum, sum2, TOLERANCE); mean = sum / (ARRAY_DIMENSIONS - 1); double mean2 = (Double) (meanObj.getResult()); assertEquals(mean, mean2, TOLERANCE); } // tests mean not using a "static" (ie. not computed by the class under test) // as the expected value @Test public void testMeanCorrectness() { MeanSum mean = new MeanSum(false); mean.addSample(2); mean.addSample(2); assertEquals(2d, (Double)(mean.getResult()), TOLERANCE); } // This test is used for checking if the min, max and extrema objects // have a correct behavior @Test public void testMinMaxExtrema() { double[] minMax = calculateMaxMinExtrema(minObj, maxObj, extremaObj); double min = minMax[0]; double max = minMax[1]; // Comparison double[] array = (double[]) (extremaObj.getResult()); double max2 = array[1]; double min2 = array[0]; assertEquals(min, min2, TOLERANCE); assertEquals(max, max2, TOLERANCE); double min3 = (Double) (minObj.getResult()); assertEquals(min, min3, TOLERANCE); double max3 = (Double) (maxObj.getResult()); assertEquals(max, max3, TOLERANCE); } // This test is used for checking if the variance and devStd objects // have a correct behavior @Test public void testDevStdVariance() { double mean = 0; double variance = 0; double std = 0; double sum = calculateVarianceAndStd(varianceObj, devstdObj); mean = sum / (ARRAY_DIMENSIONS - 1); double sum2 = 0; for (int i = 0; i < ARRAY_DIMENSIONS; i++) { sum2 += Math.pow((testArray[i] - mean), 2); } // Comparison variance = sum2 / (ARRAY_DIMENSIONS - 1); double variance2 = (Double) (varianceObj.getResult()); assertEquals(variance, variance2, TOLERANCE); std = Math.sqrt(variance); double std2 = (Double) (devstdObj.getResult()); assertEquals(std, std2, TOLERANCE); } // This test is used for checking if the histogram,mode and median objects // have a correct behavior @Test public void testHistModeMedian() { double[] hist = new double[numBins]; List<Double> listData = new ArrayList<Double>(); double median = 0; // Mean and sum calculation for (int i = 0; i < ARRAY_DIMENSIONS; i++) { if (interval.contains(testArray[i])) { int index = getIndex(testArray[i]); hist[index]++; listData.add(testArray[i]); } modeObj.addSample(testArray[i]); histogramObj.addSample(testArray[i]); medianObj.addSample(testArray[i]); } // Selection of the median Collections.sort(listData); int listSize = listData.size(); if (listSize == 0) { median = Double.NaN; } else if (listSize == 1) { median = listData.get(0); } else { int halfSize = listSize / 2; double halfValue = listData.get(halfSize); if (listData.size() % 2 == 1) { median = halfValue; } else { median = (halfValue + listData.get(halfSize + 1)) / 2; } } // Selection of the mode double max = Double.NEGATIVE_INFINITY; double indexMax = 0; for (int i = 0; i < numBins; i++) { if (hist[i] > max) { max = hist[i]; indexMax = i; } } if (max != 0) { indexMax = indexMax + minBound; } // Comparison double indexMax2 = (Double) (modeObj.getResult()); assertEquals(indexMax, indexMax2, TOLERANCE); double[] hist2 = (double[]) (histogramObj.getResult()); for (int i = 0; i < numBins; i++) { assertEquals(hist[i], hist2[i], TOLERANCE); } double median2 = (Double) (medianObj.getResult()); assertEquals(median, median2, TOLERANCE); } // This test is used for checking if the cumulation of the statistics continue to mantain // correct results @Test public void testCumulativeStats() { // Calculation of the statistics Statistics oldMeanObj = StatsFactory.createMeanObject(); Statistics oldSumObj = StatsFactory.createSumObject(); Statistics oldMaxObj = StatsFactory.createMaxObject(); Statistics oldMinObj = StatsFactory.createMinObject(); Statistics oldExtremaObj = StatsFactory.createExtremaObject(); Statistics oldVarianceObj = StatsFactory.createVarianceObject(); Statistics oldDevStdObj = StatsFactory.createDevStdObject(); // Calculate sum and mean calculateSumMean(oldSumObj, oldMeanObj); // Calculate Max and Min and Extrema calculateMaxMinExtrema(oldMinObj, oldMaxObj, oldExtremaObj); // Calculate Variance and Std calculateVarianceAndStd(oldVarianceObj, oldDevStdObj); // Addition of dummy data Statistics newMeanObj = StatsFactory.createMeanObject(); newMeanObj.addSample(1); Statistics newSumObj = StatsFactory.createSumObject(); newSumObj.addSample(1); Statistics newMaxObj = StatsFactory.createMaxObject(); newMaxObj.addSample(1); Statistics newMinObj = StatsFactory.createMinObject(); newMinObj.addSample(1); Statistics newExtremaObj = StatsFactory.createExtremaObject(); newExtremaObj.addSample(1); Statistics newVarianceObj = StatsFactory.createVarianceObject(); newVarianceObj.addSample(1); Statistics newDevStdObj = StatsFactory.createDevStdObject(); newDevStdObj.addSample(1); // Statistics accumulation newMeanObj.accumulateStats(oldMeanObj); newSumObj.accumulateStats(oldSumObj); newMaxObj.accumulateStats(oldMaxObj); newMinObj.accumulateStats(oldMinObj); newExtremaObj.accumulateStats(oldExtremaObj); newVarianceObj.accumulateStats(oldVarianceObj); newDevStdObj.accumulateStats(oldDevStdObj); // Storage of the updated statistics double newMeanUpdated = (Double) (newMeanObj.getResult()); double newSumUpdated = (Double) (newSumObj.getResult()); double newMaxUpdated = (Double) (newMaxObj.getResult()); double newMinUpdated = (Double) (newMinObj.getResult()); double[] newExtrema = (double[]) (newExtremaObj.getResult()); double newExmin = newExtrema[0]; double newExmax = newExtrema[1]; double newVarianceUpdated = (Double) (newVarianceObj.getResult()); double newStdUpdated = (Double) (newDevStdObj.getResult()); // New calculation of the statistics double sum = 0; double min = Double.POSITIVE_INFINITY; double max = Double.NEGATIVE_INFINITY; for (int i = 0; i < ARRAY_DIMENSIONS; i++) { sum += testArray[i]; if (testArray[i] > max) { max = testArray[i]; } if (testArray[i] < min) { min = testArray[i]; } } double meanCalc = (sum + 1) / ARRAY_DIMENSIONS; double sumCalc = (sum + 1); double maxCalc = 1 > max ? 1 : max; double minCalc = 1 < min ? 1 : min; double varianceCalc = 0; double stdCalc = 0; double sum2 = 0; for (int i = 0; i < ARRAY_DIMENSIONS; i++) { sum2 += Math.pow((testArray[i] - meanCalc), 2); } sum2 += Math.pow((1 - meanCalc), 2); varianceCalc = sum2 / (ARRAY_DIMENSIONS); stdCalc = Math.sqrt(varianceCalc); // Comparison assertEquals(meanCalc, newMeanUpdated, TOLERANCE); assertEquals(sumCalc, newSumUpdated, TOLERANCE); assertEquals(maxCalc, newMaxUpdated, TOLERANCE); assertEquals(minCalc, newMinUpdated, TOLERANCE); assertEquals(maxCalc, newExmax, TOLERANCE); assertEquals(minCalc, newExmin, TOLERANCE); assertEquals(varianceCalc, newVarianceUpdated, TOLERANCE); assertEquals(stdCalc, newStdUpdated, TOLERANCE); } // This test is used for checking if the statistics are correctly cleared @Test public void testEmptyStats() { // Addition of dummy data Statistics newMeanObj = StatsFactory.createMeanObject(); newMeanObj.addSample(1); Statistics newSumObj = StatsFactory.createSumObject(); newSumObj.addSample(1); Statistics newMaxObj = StatsFactory.createMaxObject(); newMaxObj.addSample(1); Statistics newMinObj = StatsFactory.createMinObject(); newMinObj.addSample(1); Statistics newExtremaObj = StatsFactory.createExtremaObject(); newExtremaObj.addSample(1); Statistics newVarianceObj = StatsFactory.createVarianceObject(); newVarianceObj.addSample(1); Statistics newDevStdObj = StatsFactory.createDevStdObject(); newDevStdObj.addSample(1); Statistics newHistObj = StatsFactory.createHistogramObject(numBins, minBound, maxBound); newHistObj.addSample(1); Statistics newModeObj = StatsFactory.createModeObject(numBins, minBound, maxBound); newModeObj.addSample(1); Statistics newMedianObj = StatsFactory.createMedianObject(minBound, maxBound); newMedianObj.addSample(1); // Clearing of the statistics newMeanObj.clearStats(); newSumObj.clearStats(); newMaxObj.clearStats(); newMinObj.clearStats(); newExtremaObj.clearStats(); newVarianceObj.clearStats(); newDevStdObj.clearStats(); newHistObj.clearStats(); newModeObj.clearStats(); newMedianObj.clearStats(); // Storage of the cleared statistics double newMeanUpdated = (Double) (newMeanObj.getResult()); double newSumUpdated = (Double) (newSumObj.getResult()); double newMaxUpdated = (Double) (newMaxObj.getResult()); double newMinUpdated = (Double) (newMinObj.getResult()); double[] newExtrema = (double[]) (newExtremaObj.getResult()); double newExmin = newExtrema[0]; double newExmax = newExtrema[1]; double newVarianceUpdated = (Double) (newVarianceObj.getResult()); double newStdUpdated = (Double) (newDevStdObj.getResult()); double[] newHistUpdated = (double[]) (newHistObj.getResult()); double newModeUpdated = (Double) (newModeObj.getResult()); double newMedianUpdated = (Double) (newMedianObj.getResult()); // Comparison assertEquals(0, newMeanUpdated, TOLERANCE); assertEquals(0, newSumUpdated, TOLERANCE); assertEquals(Double.NEGATIVE_INFINITY, newMaxUpdated, TOLERANCE); assertEquals(Double.POSITIVE_INFINITY, newMinUpdated, TOLERANCE); assertEquals(Double.NEGATIVE_INFINITY, newExmax, TOLERANCE); assertEquals(Double.POSITIVE_INFINITY, newExmin, TOLERANCE); assertEquals(Double.NaN, newVarianceUpdated, TOLERANCE); assertEquals(Double.NaN, newStdUpdated, TOLERANCE); for (int i = 0; i < numBins; i++) { assertEquals(0, newHistUpdated[i], TOLERANCE); } assertEquals(0, newModeUpdated, TOLERANCE); assertEquals(Double.NaN, newMedianUpdated, TOLERANCE); } /* * These tests are used for checking if the accumulateStats() method returns an exception when the given statistical object does not belong to the * same StatsType of the receiver or if it is not supported */ @Test(expected = IllegalArgumentException.class) public void testMeanException() { meanObj.accumulateStats(sumObj); } @Test(expected = IllegalArgumentException.class) public void testSumException() { sumObj.accumulateStats(meanObj); } @Test(expected = IllegalArgumentException.class) public void testMaxException() { maxObj.accumulateStats(sumObj); } @Test(expected = IllegalArgumentException.class) public void testMinException() { minObj.accumulateStats(sumObj); } @Test(expected = IllegalArgumentException.class) public void testExtremaException() { extremaObj.accumulateStats(sumObj); } @Test(expected = IllegalArgumentException.class) public void testVarianceException() { varianceObj.accumulateStats(sumObj); } @Test(expected = IllegalArgumentException.class) public void testDevStdException() { devstdObj.accumulateStats(sumObj); } @Test(expected = UnsupportedOperationException.class) public void testHistException() { histogramObj.accumulateStats(sumObj); } @Test(expected = UnsupportedOperationException.class) public void testModeException() { modeObj.accumulateStats(sumObj); } @Test(expected = UnsupportedOperationException.class) public void testMedianException() { medianObj.accumulateStats(sumObj); } private double calculateSumMean(Statistics sumObj, Statistics meanObj) { double sum = 0; // Mean and sum calculation for (int i = 0; i < ARRAY_DIMENSIONS; i++) { sum += testArray[i]; sumObj.addSample(testArray[i]); meanObj.addSample(testArray[i]); } return sum; } private double[] calculateMaxMinExtrema(Statistics minObj, Statistics maxObj, Statistics extremaObj) { double[] minMax = new double[]{Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY}; // Maximum and minimum calculation for (int i = 0; i < ARRAY_DIMENSIONS; i++) { if (testArray[i] > minMax[1]) { minMax[1] = testArray[i]; } if (testArray[i] < minMax[0]) { minMax[0] = testArray[i]; } minObj.addSample(testArray[i]); maxObj.addSample(testArray[i]); extremaObj.addSample(testArray[i]); } return minMax; } private double calculateVarianceAndStd(Statistics varianceObj, Statistics devstdObj) { double sum = 0; // Variance and standard deviation calculation for (int i = 0; i < ARRAY_DIMENSIONS; i++) { sum += testArray[i]; varianceObj.addSample(testArray[i]); devstdObj.addSample(testArray[i]); } return sum; } }