簡體   English   中英

使用 Java 8 流計算加權平均值

[英]Calculate weighted average with Java 8 streams

我如何計算Map<Double, Integer>加權平均值,其中 Integer 值是要平均的 Double 值的權重。 例如:地圖具有以下元素:

  1. (0.7, 100) // 值為 0.7,權重為 100
  2. (0.5, 200)
  3. (0.3, 300)
  4. (0.0, 400)

我希望使用 Java 8 流應用以下公式,但不確定如何一起計算分子和分母並同時保留它。 這里如何使用reduce?

在此處輸入圖片說明

您可以為此任務創建自己的收集器:

static <T> Collector<T,?,Double> averagingWeighted(ToDoubleFunction<T> valueFunction, ToIntFunction<T> weightFunction) {
    class Box {
        double num = 0;
        long denom = 0;
    }
    return Collector.of(
             Box::new,
             (b, e) -> { 
                 b.num += valueFunction.applyAsDouble(e) * weightFunction.applyAsInt(e); 
                 b.denom += weightFunction.applyAsInt(e);
             },
             (b1, b2) -> { b1.num += b2.num; b1.denom += b2.denom; return b1; },
             b -> b.num / b.denom
           );
}

這個自定義收集器采用兩個函數作為參數:一個函數返回用於給定流元素的值(作為ToDoubleFunction ),另一個返回權重(作為ToIntFunction )。 它使用輔助本地類在收集過程中存儲分子和分母。 每次接受一個條目時,分子隨着值乘以其權重而增加,分母隨着權重增加。 完成者然后將兩者的除法作為Double

示例用法是:

Map<Double,Integer> map = new HashMap<>();
map.put(0.7, 100);
map.put(0.5, 200);

double weightedAverage =
  map.entrySet().stream().collect(averagingWeighted(Map.Entry::getKey, Map.Entry::getValue));

您可以使用此過程來計算地圖的加權平均值。 請注意,映射條目的鍵應包含值,映射條目的值應包含權重。

     /**
     * Calculates the weighted average of a map.
     *
     * @throws ArithmeticException If divide by zero happens
     * @param map A map of values and weights
     * @return The weighted average of the map
     */
    static Double calculateWeightedAverage(Map<Double, Integer> map) throws ArithmeticException {
        double num = 0;
        double denom = 0;
        for (Map.Entry<Double, Integer> entry : map.entrySet()) {
            num += entry.getKey() * entry.getValue();
            denom += entry.getValue();
        }

        return num / denom;
    }

您可以查看其單元測試以查看用例。

     /**
     * Tests our method to calculate the weighted average.
     */
    @Test
    public void testAveragingWeighted() {
        Map<Double, Integer> map = new HashMap<>();
        map.put(0.7, 100);
        map.put(0.5, 200);
        Double weightedAverage = calculateWeightedAverage(map);
        Assert.assertTrue(weightedAverage.equals(0.5666666666666667));
    }

您需要這些導入進行單元測試:

import org.junit.Assert;
import org.junit.Test;

您需要這些代碼導入:

import java.util.HashMap;
import java.util.Map;

我希望它有幫助。

static float weightedMean(List<Double> value, List<Integer> weighted, int n) {
    int sum = 0;
    double numWeight = 0;

    for (int i = 0; i < n; i++) {
        numWeight = numWeight + value.get(i).doubleValue() * weighted.get(i).intValue();
        sum = sum + weighted.get(i).intValue();
    }

    return (float) (numWeight) / sum;
}
public static double weightedAvg(Collection<Map.Entry<? extends Number, ? extends Number> data) {
    var sumWeights = data.stream()
        .map(Map.Entry::getKey)
        .mapToDouble(Number::doubleValue)
        .sum();
    var sumData = data.stream()
        .mapToDouble(e -> e.getKey().doubleValue() * e.getValue().doubleValue())
        .sum();
    return sumData / sumWeights;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM