Reputation: 1762
edit - second class does not have indexed based access, instead it implements iterable
Suppose a class structure like this:
public class Values
{
public int getValue(int i)
{
return values_[i];
}
private int[] values_;
}
and a second class like this
public class ValuesCollection implements Iterable
{
private Values[] valuesCollection_;
}
Is there a way using java8 streams API to operate statistics along each dimension for instance: sum, mean, min, max, range, variance, std, etc. For example [[2,4,8],[1,5,7],[3,9,6]], for getting min it would return [1,4,6]
The closest I can come up with is something like this:
public int[] getMin(ValuesCollection valuesCollection)
{
IntStream.range(0, valuesCollection.size()).boxed().collect(Collectors.toList())
.forEach(i -> {
List<Integer> vals = valuesCollection.stream()
.map(values -> values.getValue(i))
.collect(Collectors.toList());
// operate statistics on vals
// no way to return the statistics
});
}
Upvotes: 3
Views: 338
Reputation: 37655
You can do it. I've used arrays rather than your wrapper classes. Also, I should probably have included some validation that the array is rectangular, and used orElseThrow
rather than getAsInt
, but you get the idea.
int[][] vals = {{2, 4, 8}, {1, 5, 7}, {3, 9, 6}};
int[] min = IntStream
.range(0, vals[0].length)
.map(j -> IntStream.range(0, vals.length).map(i -> vals[i][j]).min().getAsInt())
.toArray();
System.out.println(Arrays.toString(min)); // Prints [1, 4, 6] as expected
(Since I used arrays, I could have used this line instead
.map(j -> Arrays.stream(vals).mapToInt(arr -> arr[j]).min().getAsInt())
but I wrote it like I did to closely model your situation where your objects are not arrays but do have indexed based access.)
Doing it for standard deviation is obviously harder, but you can do it by combining my answer with this one.
Edit
If your outer class does not have indexed based access, but instead implements Iterable
you can do it by converting the Iterable
to a Stream
.
Iterable<int[]> vals = Arrays.asList(new int[][] {{2, 4, 8}, {1, 5, 7}, {3, 9, 6}});
int[] min = IntStream
.range(0, vals.iterator().next().length)
.map(j -> StreamSupport.stream(vals.spliterator(), false).mapToInt(a -> a[j]).min().getAsInt())
.toArray();
System.out.println(Arrays.toString(min)); // Prints [1, 4, 6] as expected
Upvotes: 2
Reputation: 1697
You can do it by simply flattens the Integer[][] into Integer[] then operate on it with a cycle equal to the length of the array. The following algorithm perform only min,max,sum!!
public class Test {
public static void main(String... strings) {
Integer[][] vals = {{2, 4, 8}, {1, 5, 7}, {3, 9, 6}};
BinaryOperator<Integer> minFunction = (x, y) -> x < y? x: y;
BinaryOperator<Integer> maxFunction = (x, y) -> x > y? x: y;
BinaryOperator<Integer> sumFunction = (x, y) -> x+y;
Integer[] performStatistic = performStatistic(vals, minFunction); // 1 4 6
Integer[] performStatistic2 = performStatistic(vals, maxFunction); // 3 9 8
Integer[] performStatistic3 = performStatistic(vals, sumFunction); // 6 18 21
}
public static Integer[] performStatistic(Integer[][] vals, BinaryOperator<Integer> f){
List<Integer> res = new ArrayList<>(vals.length);
int[] i = {0};
Arrays.asList(vals).stream().flatMap((Integer[] x)-> Arrays.asList(x).stream())
.forEach(x -> {
if(i[0]<vals.length){
res.add(i[0], x);
}else{
int cyclicPos = i[0]%vals.length;
res.set(cyclicPos, f.apply(res.get(cyclicPos), x));
}
i[0]++;
});
return res.toArray(new Integer[res.size()]);
}
}
To perform the others operations you can follow the same steps and use BiFunction<Integer,Double,Double>
to perform the range, variance ...
Maybe the following could help you !!
double avg = 100; // the avg you can get it from the previous algorithm
BiFunction<Integer,Double,Double> varianceFunction = (Integer x, Double y) -> {
return Math.pow(new Double(x) - avg, 2)+y;
};// after getting the result just divided it by the (size of the array -1)
Upvotes: 1