web-dev-qa-db-ja.com

Java 8ストリームで加重平均を計算する

Map<Double, Integer>の加重平均を計算するにはどうすればよいですか。ここで、整数値は、平均化されるDouble値の重みです。例:マップには次の要素があります。

  1. (0.7、100)//値は0.7、重みは100
  2. (0.5、200)
  3. (0.3、300)
  4. (0.0、400)

Java 8ストリームを使用して次の式を適用しようとしていますが、分子と分母を一緒に計算して同時に保存する方法がわかりません。ここでリダクションを使用するにはどうすればよいですか?

enter image description here

10
Vivek Sethi

このタスク用に独自のコレクターを作成できます。

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

このカスタムコレクターは、パラメーターとして2つの関数を取ります。1つは特定のストリーム要素に使用する値を返す関数( ToDoubleFunction として)であり、もう1つは重みを返す( ToIntFunction )。収集プロセス中に分子と分母を格納するヘルパーローカルクラスを使用します。エントリが受け入れられるたびに、値にその重みを掛けた結果として分子が増加し、分母が重みとともに増加します。次に、フィニッシャーは2つの除算を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));
16
Tunaki

この手順を使用して、マップの加重平均を計算できます。マップエントリのキーには値が含まれている必要があり、マップエントリの値には重みが含まれている必要があることに注意してください。

     /**
     * 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;

お役に立てば幸いです。

2
Vahid
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;
}
0
Wellington P.G