[英]Calculate weighted average with Java 8 streams
您可以为此任务创建自己的收集器:
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.