【问题标题】:Using two streams in Java lambda to compute covariance在 Java lambda 中使用两个流来计算协方差
【发布时间】:2014-05-31 17:14:06
【问题描述】:

假设我有两个双精度数组。 我一直在尝试使用 Java 8 中的 Stream。我想我已经理解了主要思想 但后来我意识到我不确定如何同时操作两个 Streams。

例如,我想计算两个数组的协方差。

public class foo {

    public static double mean(double[] xs) {
        return Arrays.stream(xs).average().getAsDouble();
}

    public static void main(String[] args) {
        double[] xs = {1, 2, 3, 4, 5, 6, 7, 8, 9};
        double[] ys = {1517.93, 1757.78, 1981.1, 2215.73, 2942.66, 3558.32, 4063.91, 4521.16, 5101.76, 5234.12};

        System.out.println("Mean of xs: " + mean(xs));
        double xs_sumDeviation = Arrays.stream(xs)
            .boxed()
            .mapToDouble(d -> d.doubleValue() - mean(xs))
            .sum();
       // Covariance
        double covXY = Arrays.stream(xs, ys)
            .mapToDouble(x,y -> {
                  double numerator = (x-mean(xs)* (y-mean(ys);
                  double denominator = Math.sqrt((x-mean(xs)* (x-mean(xs));
                  return numerator / denominator;
             })
            .sum();

    }
}

感谢您的建议。

尝试 1。

public static double covariance(double[] xs, double[] ys) {
    double xmean = mean(xs);
    double ymean = mean(ys);
    double numerator = IntStream.range(0, Math.min(xs.length, ys.length))
            .parallel()
            .mapToDouble(i -> (xs[i] - xmean) * (ys[i] - ymean))
            .sum();
    double denominator = Math.sqrt(IntStream.range(0, xs.length)
            .parallel()
            .mapToDouble(i -> (xs[i] - xmean) * (xs[i] - xmean))
            .sum());
    return numerator / denominator;

【问题讨论】:

  • 还有什么问题?
  • 对不起。问题是如何实现java lambda表达式来计算两个数组的协方差。

标签: java lambda statistics java-8 java-stream


【解决方案1】:

在其他编程语言中,有某种 zip 函数,它接受多个 iterables,并返回一个 iterator,它聚合来自每个 iterables。例如,请参阅 Python 库中的函数 zip

虽然可以在 Java 中制作类似的功能,但很难以支持高效并行执行的方式实现它。但是,Java 中有一个常用的模式,即有点不同。在您的情况下,它可能如下所示:

public static double covariance(double[] xs, double[] ys) {
    double xmean = mean(xs);
    double ymean = mean(ys);
    return IntStream.range(0, Math.min(xs.length, ys.length))
        .parallel()
        .mapToDouble(i -> {
                double numerator = (xs[i] - xmean) * (ys[i] - ymean);
                double denominator = ...;
                return numerator / denominator;
            })
        .sum();
}

您无需组合两个流,而是创建一个包含所有索引的IntStream,并通过索引访问不同集合的元素。只要集合支持随机访问操作,它就可以很好地工作。

【讨论】:

  • 谢谢。现在我学习了另一种使用 lambda 表达式的方法。函数 Math.sqrt() 应仅在求和后应用于分母。我想这就是你写...的原因……但这是否意味着我必须在另一个 Stream 中计算分母?
  • @CheJharia:我使用... 专注于基本要素。目的是保持原始语义。查看协方差函数的非流实现会有所帮助,因为有不同种类的 variancecovariance
【解决方案2】:

其他 FP 语言有 zip 操作,它从两个元素流中生成一个 pairs 流。这在 Java 中不可用。

但是,如果是数组,您可以轻松地创建一个数组索引流,并让您的函数关闭数组,通过索引参数访问它们。

我还应该在这一行警告你

.mapToDouble(d -> d.doubleValue() - mean(xs))

您的程序复杂度为 O(n2),因为在每一步都会重新计算 mean。您应该预先计算并关闭 lambda 中的结果。

【讨论】:

    猜你喜欢
    • 2011-09-18
    • 2018-08-25
    • 2011-11-25
    • 1970-01-01
    • 1970-01-01
    • 2012-11-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多