Map<Double, Integer>
の加重平均を計算するにはどうすればよいですか。ここで、整数値は、平均化されるDouble値の重みです。例:マップには次の要素があります。
Java 8ストリームを使用して次の式を適用しようとしていますが、分子と分母を一緒に計算して同時に保存する方法がわかりません。ここでリダクションを使用するにはどうすればよいですか?
このタスク用に独自のコレクターを作成できます。
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));
この手順を使用して、マップの加重平均を計算できます。マップエントリのキーには値が含まれている必要があり、マップエントリの値には重みが含まれている必要があることに注意してください。
/**
* 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;
}