average


See On Github

Data

Contributor

Generic placeholder thumbnail

by joshimoo

in java

Tags

Source Code

package average;

import java.util.*;
import java.util.stream.DoubleStream;

/**
 * @author Joshua Moody (joshimoo@hotmail.de)
 */
public final class Average {
    /**
     * Don't let anyone instantiate this class.
     */
    private Average() {}

    /**
     * calculate a regular arithmetic mean average
     * @throws NoSuchElementException if the array is empty
     */
    public static double arithmeticMean(double... numbers) {
        assert numbers != null && numbers.length > 0;
        return DoubleStream.of(numbers).average().getAsDouble();
    }

    /**
     * calculates a weighted mean average
     * numbers and weights need to have the same count
     */
    public static double weightedMean(double[] numbers, double[] weights) {
        assert numbers.length == weights.length;
        double weightedSum = 0;
        for (int i = 0; i < numbers.length; i++) {
            weightedSum += numbers[i] * weights[i];
        }

        return weightedSum / DoubleStream.of(weights).sum();
    }

    /**
     * calculates the harmonic mean average
     * according to this formula: n/(1/x1 + 1/x2 + ... + 1/xn)
     */
    public static double harmonicMean(double... numbers) {
        return numbers.length / DoubleStream.of(numbers).map(x -> 1.0 / x).sum();
    }

    /**
     * calculates the contra harmonic mean average
     * according to this formula: (x1^2 + x2^2 + ... + xn^2)/(x1 + x2 + ... + xn)
     */
    public static double contraHarmonicMean(double... numbers) {
        return DoubleStream.of(numbers).map(x -> Math.pow(x, 2.0)).sum() / DoubleStream.of(numbers).sum();
    }

    /**
     * calculates the geometric mean average
     * for small amount of numbers according to this formula: (x1*x2*...xn) ^ (1/n)
     * for large amount of numbers, by summing the logarithms of each x
     */
    public static double geometricMean(double... numbers) {
        // TODO: For large numbers, consider summing the logarithms of each x
        // (x1*x2*...xn) ^ (1/n)
        return Math.pow(DoubleStream.of(numbers).reduce(1.0, (a, x) -> (a * x)), 1.0d / numbers.length);
    }

    /**
     * calculates the quadratic mean average
     * according to  this formula: sqrt( (x1)^2+(x2)^2+(x3)^2+...+(xn)^2 /n )
     */
    public static double quadraticMean(double... numbers) {
        return generalizedMean(numbers, 2.0);
    }

    /**
     * calculates a generalized mean average
     * according to this formula: y-root( (x1)^y+(x2)^y+(x3)^y+...+(xn)^y / n )
     */
    public static double generalizedMean(double[] numbers, double power) {
        return Math.pow(arithmeticMean(DoubleStream.of(numbers).map(x -> Math.pow(x, power)).toArray()), 1.0 / power);
    }

    /**
     * Get the mid value beetwen min and max
     */
    public static double midrange(double... numbers) {
        DoubleSummaryStatistics stat = DoubleStream.of(numbers).summaryStatistics();
        return arithmeticMean(stat.getMin(), stat.getMax());
    }

    /**
     * calculates the median average for sorted list of numbers
     * @param numbers needs to be sorted
     * @return mean average of left and right for even numbers, else mid element
     */
    public static double median(double... numbers) {
        if (numbers.length % 2 == 0) {
            double left = numbers[(numbers.length / 2) - 1];
            double right = numbers[numbers.length / 2];
            return arithmeticMean(left, right);
        } else {
            double mid = numbers[((numbers.length + 1) / 2) - 1];
            return mid;
        }
    }

    /**
     * calculates the mode average
     * which returns the element, with highest occurrence count
     */
    public static double mode(double... numbers) {
        // Stupid Type System, all this boxing :(
        Map<Double, Integer> frequencies = new HashMap<>();

        for(double key : numbers) {
            frequencies.put(key, frequencies.getOrDefault(key, 0) + 1);
        }

        Double maxElement = numbers[0];
        for (Map.Entry<Double, Integer> element : frequencies.entrySet()) {
            if(element.getValue() > frequencies.get(maxElement)) {
                maxElement = element.getKey();
            }
        }

        return maxElement;
    }

}
package average;

import org.junit.Test;
import java.util.stream.DoubleStream;
import static org.junit.Assert.*;

/**
 * @author Joshua Moody (joshimoo@hotmail.de)
 */
public class AverageTest {
    private static final double DELTA = 0.01;

    @Test
    public void testAverageInequality() throws Exception {
        double[] numbers = new double[] { 1, 2, 3, 4, 5 };

        /**
         * Mean Inequalities:
         * Some means are in a constant relationship to one another.
         * If we denote the arithmentic mean of x and y by A,
         * their geometric mean by G,
         * their harmonic mean by H,
         * their root mean square (quadratic mean) by R,
         * and their contraharmonic mean by C,
         *
         * then the following chain of inequalities is always true
         * C >= R >= A >= G >= H
         */

        double contraHarmonic = Average.contraHarmonicMean(numbers);
        double quadratic = Average.quadraticMean(numbers);
        double arithmetic = Average.arithmeticMean(numbers);
        double geometric = Average.geometricMean(numbers);
        double harmonic = Average.harmonicMean(numbers);
        assertTrue("Your Average inequalities are not correct", (contraHarmonic >= quadratic) && (quadratic >= arithmetic) && (arithmetic >= geometric) && (geometric >= harmonic));
    }

    @Test
    public void testArithmeticMean() throws Exception {
        double[] numbers = new double[] { 1, 2, 3, 4, 5 };
        double expected = DoubleStream.of(numbers).average().getAsDouble();
        assertEquals(expected, Average.arithmeticMean(numbers), DELTA);
    }

    @Test
    public void testWeightedMean() throws Exception {
        double[] numbers = new double[] { 1, 2, 3, 4, 5 };
        double[] weights = new double[] { 1, 1, 1, 1, 1 };
        double expected = DoubleStream.of(numbers).average().getAsDouble();
        assertEquals(expected, Average.weightedMean(numbers, weights), DELTA);
    }

    @Test
    public void testHarmonicMean() throws Exception {
        // n/(1/x1 + 1/x2 + ... + 1/xn)
        double[] numbers = new double[] { 1, 2, 3, 4, 5 };
        double expected = numbers.length / DoubleStream.of(numbers).map(x -> 1.0 / x).sum();
        assertEquals(expected, Average.harmonicMean(numbers), DELTA);
    }

    @Test
    public void testContraHarmonicMean() throws Exception {
        double[] numbers = new double[] { 1, 2, 3, 4, 5 };
        double expected = DoubleStream.of(numbers).map(x -> Math.pow(x, 2.0)).sum() / DoubleStream.of(numbers).sum();
        assertEquals(expected, Average.contraHarmonicMean(numbers), DELTA);
    }

    @Test
    public void testGeometricMean() throws Exception {
        // (x1*x2*...xn) ^ (1/n)
        double[] numbers = new double[] { 1, 2, 3, 4, 5 };
        double expected = Math.pow((numbers[0] * numbers[1] * numbers[2] * numbers[3] * numbers[4]), 1.0 / numbers.length);
        assertEquals(expected, Average.geometricMean(numbers), DELTA);
    }

    @Test
    public void testQuadraticMean() throws Exception {
        // sqrt((x1)^2+(x2)^2+(x3)^2+...+(xn)^2 /n)
        double[] numbers = new double[] { 1, 2, 3, 4, 5 };
        double expected = Math.sqrt(DoubleStream.of(numbers).map(x -> Math.pow(x, 2)).sum() / numbers.length);
        assertEquals(expected, Average.quadraticMean(numbers), DELTA);
    }

    @Test
    public void testGeneralizedMean() throws Exception {
        // y-root((x1)^y+(x2)^y+(x3)^y+...+(xn)^y / n)
        double[] numbers = new double[] { 1, 2, 3, 4, 5 };
        double power = 4;
        double expected = Math.pow((DoubleStream.of(numbers).map(x -> Math.pow(x, 4)).sum() / numbers.length), 1.0 / power);
        assertEquals(expected, Average.generalizedMean(numbers, power), DELTA);
    }

    @Test
    public void testMidrange() throws Exception {
        double[] numbers = new double[] { 1, 2, 3, 4, 5 };
        double expected = (numbers[0] + numbers[numbers.length - 1]) / 2.0;
        assertEquals(expected, Average.midrange(numbers), DELTA);
    }

    @Test
    public void testMedianEven() throws Exception {
        double[] numbers = new double[] { 1, 2, 3, 4, 5, 6};
        double expected = (numbers[(numbers.length / 2) - 1] + numbers[numbers.length / 2]) / 2.0;
        assertEquals(expected, Average.median(numbers), DELTA);
    }

    @Test
    public void testMedianOdd() throws Exception {
        double[] numbers = new double[] { 1, 2, 3, 4, 5 };
        double expected = numbers[((int) Math.floor(numbers.length / 2))];
        assertEquals(expected, Average.median(numbers), DELTA);
    }

    @Test
    public void testMode() throws Exception {
        // frequency based average
        double[] numbers = new double[] { 1, 2, 3, 3, 3, 4, 5 };
        double expected = 3;
        assertEquals(expected, Average.mode(numbers), DELTA);
    }
}