Calculate Weighted Mean in Java (#15644)

Co-authored-by: Grzegorz Piwowarek <gpiwowarek@gmail.com>
This commit is contained in:
Graham Cox 2024-01-26 18:41:08 +00:00 committed by GitHub
parent 899104d579
commit c72cb4238c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 153 additions and 0 deletions

View File

@ -0,0 +1,153 @@
package com.baeldung.algorithms.weightedaverage;
import org.junit.jupiter.api.Test;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
import java.util.stream.IntStream;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class WeightedAverageUnitTest {
private List<Values> values = Arrays.asList(
new Values(1, 10),
new Values(3, 20),
new Values(5, 30),
new Values(7, 50),
new Values(9, 40)
);
private Double expected = 6.2;
@Test
void twoPass() {
double top = values.stream()
.mapToDouble(v -> v.value * v.weight)
.sum();
double bottom = values.stream()
.mapToDouble(v -> v.weight)
.sum();
double result = top / bottom;
assertEquals(expected, result);
}
@Test
void onePass() {
double top = 0;
double bottom = 0;
for (Values v : values) {
top += (v.value * v.weight);
bottom += v.weight;
}
double result = top / bottom;
assertEquals(expected, result);
}
@Test
void expanding() {
double result = values.stream()
.flatMap(v -> Collections.nCopies(v.weight, v.value).stream())
.mapToInt(v -> v)
.average()
.getAsDouble();
assertEquals(expected, result);
}
@Test
void reduce() {
class WeightedAverage {
final double top;
final double bottom;
public WeightedAverage(double top, double bottom) {
this.top = top;
this.bottom = bottom;
}
double average() {
return top / bottom;
}
}
double result = values.stream()
.reduce(new WeightedAverage(0, 0),
(acc, next) -> new WeightedAverage(
acc.top + (next.value * next.weight),
acc.bottom + next.weight),
(left, right) -> new WeightedAverage(
left.top + right.top,
left.bottom + right.bottom))
.average();
assertEquals(expected, result);
}
@Test
void customCollector() {
class WeightedAverage implements Collector<Values, WeightedAverage.RunningTotals, Double> {
class RunningTotals {
double top;
double bottom;
public RunningTotals() {
this.top = 0;
this.bottom = 0;
}
}
@Override
public Supplier<RunningTotals> supplier() {
return RunningTotals::new;
}
@Override
public BiConsumer<RunningTotals, Values> accumulator() {
return (current, next) -> {
current.top += (next.value * next.weight);
current.bottom += next.weight;
};
}
@Override
public BinaryOperator<RunningTotals> combiner() {
return (left, right) -> {
left.top += right.top;
left.bottom += right.bottom;
return left;
};
}
@Override
public Function<RunningTotals, Double> finisher() {
return rt -> rt.top / rt.bottom;
}
@Override
public Set<Characteristics> characteristics() {
return Collections.singleton(Characteristics.UNORDERED);
}
}
double result = values.stream()
.collect(new WeightedAverage());
assertEquals(expected, result);
}
private static class Values {
int value;
int weight;
public Values(int value, int weight) {
this.value = value;
this.weight = weight;
}
}
}