Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 refactoring lambda expressions

I would like to create a simple class applying common statistics using lambda expression. I am wondering how can I avoid using the switch case in the statistic() method?

For example, I may want to write a new lambda to calculate the variance of the list, etc.

Thank you.

public class DescriptiveStatistics {

    public static void main(String[] args) {
        List<Double> numbers = Arrays.asList(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0);
        numbers.stream().forEach(n-> System.out.print(n + " "));
        System.out.println();
        System.out.println("Descriptive statistics");
        System.out.println("Sum: " + statistic(numbers, "Sum"));
        System.out.println("Max: " + statistic(numbers, "Max"));
        System.out.println("Min: " + statistic(numbers, "Min"));
        System.out.println("Average: " + statistic(numbers, "Average"));
        System.out.println("Count: " + statistic(numbers, "Count"));
    }

    private static double statistic(List<Double> numbers, String function) {
        switch (function.toLowerCase()) {
            case "sum":
                return numbers.stream().mapToDouble(Double::doubleValue).sum();
            case "max":
                return numbers.stream().mapToDouble(Double::doubleValue).max().getAsDouble();
            case "min":
                return numbers.stream().mapToDouble(Double::doubleValue).min().getAsDouble();
            case "average":
                return numbers.stream().mapToDouble(Double::doubleValue).average().getAsDouble();
            case "count":
                return numbers.stream().mapToDouble(Double::doubleValue).count();
        }
        return 0;
    }

I have in mind of a method like this

private static double newStatistics(List<Double> numbers, Function<Double, Double> function){
        return  numbers.stream().mapToDouble(Double::doubleValue).function();
    }
like image 648
CheJharia Avatar asked Apr 05 '14 13:04

CheJharia


People also ask

Which is correct about Java 8 lambda expression?

Explanation. Both of the above options are correct. Q 5 - Which of the following is correct about Java 8 lambda expression? A - Lambda expressions are used primarily to define inline implementation of a functional interface.

Does Java 1.8 support lambda expressions?

Lambda Expressions were added in Java 8.

Can we replace lambda expression with method reference?

If you are using a lambda expression as an anonymous function but not doing anything with the argument passed, you can replace lambda expression with method reference. In the first two cases, the method reference is equivalent to lambda expression that supplies the parameters of the method e.g. System.

Why method reference is better than lambda?

The method references can only be used to replace a single method of the lambda expression. A code is more clear and short if one uses a lambda expression rather than using an anonymous class and one can use method reference rather than using a single function lambda expression to achieve the same.


2 Answers

Why not simply use DoubleStream#summaryStatistics or apply a similar pattern?

You could even extend the class to add custom methods, say a variance, skewness and kurtosis for example:

/**
 * Algorithms derived from: Philippe Pébay, Formulas for Robust, One-Pass Parallel
 * Computation of Covariances and Arbitrary-Order Statistical Moments.
 */
public class MoreDoubleStatistics extends DoubleSummaryStatistics {

    private double M1, M2, M3, M4;

    @Override
    public void accept(double x) {
        super.accept(x);

        long n = getCount();

        double delta = x - M1;                       // δ
        double delta_n = delta / n;                  // δ / n
        double delta2_n = delta * delta_n;           // δ^2 / n
        double delta2_n2 = delta_n * delta_n;        // δ^2 / n^2
        double delta3_n2 = delta2_n * delta_n;       // δ^3 / n^2
        double delta4_n3 = delta3_n2 * delta_n;      // δ^4 / n^3

        M4 += (n - 1) * (n * n - 3 * n + 3) * delta4_n3
                + 6 * M2 * delta2_n2
                - 4 * M3 * delta_n;
        M3 += (n - 1) * (n - 2) * delta3_n2
                - 3 * M2 * delta_n;
        M2 += (n - 1) * delta2_n;
        M1 += delta_n;
    }

    @Override
    public void combine(DoubleSummaryStatistics other) {
      throw new UnsupportedOperationException(
              "Can't combine a standard DoubleSummaryStatistics with this class");
    }

    public void combine(MoreDoubleStatistics other) {
        MoreDoubleStatistics s1 = this;
        MoreDoubleStatistics s2 = other;

        long n1 = s1.n();
        long n2 = s2.n();
        long n = n1 + n2;

        double delta = s2.M1 - s1.M1;                // δ
        double delta_n = delta / n;                  // δ / n
        double delta2_n = delta * delta_n;           // δ^2 / n
        double delta2_n2 = delta_n * delta_n;        // δ^2 / n^2
        double delta3_n2 = delta2_n * delta_n;       // δ^3 / n^2
        double delta4_n3 = delta3_n2 * delta_n;      // δ^4 / n^3

        this.M4 = s1.M4 + s2.M4 + n1 * n2 * (n1 * n1 - n1 * n2 + n2 * n2) * delta4_n3
                + 6.0 * (n1 * n1 * s2.M2 + n2 * n2 * s1.M2) * delta2_n2
                + 4.0 * (n1 * s2.M3 - n2 * s1.M3) * delta_n;

        this.M3 = s1.M3 + s2.M3 + n1 * n2 * (n1 - n2) * delta3_n2
                + 3.0 * (n1 * s2.M2 - n2 * s1.M2) * delta_n;

        this.M2 = s1.M2 + s2.M2 + n1 * n2 * delta2_n;

        this.M1 = s1.M1 + n2 * delta;

        super.combine(other);
    }

    private long n() { return getCount(); }

    public double mean() { return getAverage(); }
    public double variance() { return n() <= 1 ? 0 : M2 / (n() - 1); }
    public double stdDev() { return sqrt(variance()); }
    public double skew() { return M2 == 0 ? 0 : sqrt(n()) * M3/ pow(M2, 1.5); }
    public double kurtosis() { return M2 == 0 ? 0 : n() * M4 / (M2 * M2) - 3.0; }
}
like image 79
assylias Avatar answered Oct 19 '22 14:10

assylias


Replace the String parameter of the method statistic with a function type, that takes a DoubleStream and returns the aggregate.

private static double statistic(List<Double> numbers,
                                ToDoubleFunction<DoubleStream> function) {
    return function.applyAsDouble(
        numbers.stream().mapToDouble(Double::doubleValue));
}

Now, you can invoke the method as follows, without using a switch statement for the different operations on the stream:

System.out.println("Sum: " + statistic(numbers, s -> s.sum()));
System.out.println("Max: " + statistic(numbers, s -> s.max().getAsDouble()));
System.out.println("Min: " + statistic(numbers, s -> s.min().getAsDouble()));
System.out.println("Average: " + statistic(numbers, s -> s.average().getAsDouble()));
System.out.println("Count: " + statistic(numbers, s -> s.count()));
like image 34
nosid Avatar answered Oct 19 '22 14:10

nosid