2014-04-05 55 views
5

Tôi muốn tạo một lớp đơn giản áp dụng các số liệu thống kê phổ biến bằng cách sử dụng biểu thức lambda. Tôi tự hỏi làm thế nào tôi có thể tránh sử dụng trường hợp chuyển đổi trong phương thức thống kê()?Java 8 tái cấu trúc các biểu thức lambda

Ví dụ: tôi có thể muốn viết một lambda mới để tính toán phương sai của danh sách, v.v.

Cảm ơn bạn.

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; 
    } 

tôi có trong tâm trí của một phương pháp như thế này

private static double newStatistics(List<Double> numbers, Function<Double, Double> function){ 
     return numbers.stream().mapToDouble(Double::doubleValue).function(); 
    } 
+0

Bạn nên sử dụng hoặc là một 'enum' (nếu bạn biết trước thời hạn tất cả các chức năng) hoặc một giao diện Chiến lược (nếu bạn cần để có thể cắm những cái mới trong thời gian chạy). – chrylis

+0

Tôi nghĩ giao diện Chiến lược trả lời câu hỏi. Cảm ơn bạn! – CheJharia

Trả lời

11

Tại sao không chỉ cần sử dụng DoubleStream#summaryStatistics hoặc áp dụng một mô hình tương tự?

Bạn thậm chí có thể mở rộng các lớp để thêm các phương pháp tùy chỉnh, nói một phương sai, độ lệch và nhọn ví dụ:

/** 
* 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; } 
} 
+0

bạn có thể cung cấp mã cho hàm kết hợp để lớp này có thể được sử dụng làm bộ sưu tập không? –

+0

nhưng sẽ không sai lệch và kurtosis là sai trong kết quả cuối cùng sau đó? Tôi có nghĩa là DoubleSummaryStatistics :: kết hợp không đưa họ vào tài khoản, hoặc tôi thiếu một cái gì đó? –

+0

@ RolandGude Điểm tốt bạn hoàn toàn đúng. Đã chỉnh sửa. – assylias

8

Thay Chuỗi tham số của phương pháp Thống kê với một loại chức năng, mà phải mất một DoubleStream và lợi nhuận Tổng hợp.

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

Bây giờ, bạn có thể gọi phương pháp này như sau, mà không sử dụng một switch tuyên bố cho các hoạt động khác nhau trên dòng:

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())); 
+1

Cảm ơn bạn. Và tôi có thể đọc thêm về việc tạo ToDoubleFunction của riêng mình() ở đâu? Ví dụ, để tính toán phương sai. – CheJharia

+0

Thay vì 's -> s.sum()', bạn có thể đơn giản hóa nó thành 'DoubleStream :: sum'. Tương tự với 's -> s.count()'. – bcsb1001

+0

@ bcsb1001: Theo tôi, 's -> s.sum()' đơn giản hơn 'DoubleStream :: sum' (liên quan đến khả năng đọc và bảo trì). – nosid

Các vấn đề liên quan