Home > Back-end >  How to take average of Double[] in java
How to take average of Double[] in java

Time:07-06

I have a map like following

Map<String, Double[]> userVecs;
userVecs.put("foo", [1.1, 2.2, 3.3]);
userVecs.put("bar", [0, 4, 2]);

I want an average vec which in this case is:

[1.1 0, 2.2 4, 3.3 2] / 2;


= [0.55, 3.1, 2.65]

I have an ugly two loop approach. I was wondering if there is a better way to do this.

CodePudding user response:

Here is one way. It presumes the arrays are all the same length.

  • declare a sum array of the proper length.
  • iterate over the map values and add them via Arrays.setAll()
  • then find the average using userVecs.size() and Arrays.setAll()
Map<String, double[]> userVecs = new HashMap<>();;
userVecs.put("foo", new double[]{1.1, 2.2, 3.3});
userVecs.put("bar", new double[]{0, 4, 2});

double[] sums = new double[userVecs.get("foo").length];

for (double[] dar : userVecs.values()) {
     Arrays.setAll(sums, i->sums[i] dar[i]);
}

Arrays.setAll(sums, i->sums[i]/userVecs.size());

System.out.println(Arrays.toString(sums));

prints

[0.55, 3.1, 2.65]

The Arrays class has many useful features you may want to get familiar with.

CodePudding user response:

Unless you want to use an external Math library, you're gonna wanna just write the extra function.

public static void main(String[] args) {
    double[] arr1 = { 1.0, 2.0, 3.0 };
    double[] arr2 = { 1.0, 1.0, 1.0 };
    double[] avg = elementWiseAvg(arr1, arr2);
}

public static double[] elementWiseAvg(double[] arr1, double[] arr2) {
    if (arr1.length != arr2.length) {
        throw new IllegalArgumentException("Arrays must be the same length.");
    }
    double[] ret = new double[arr1.length];
    for (int i = 0; i < arr1.length;   i) {
        ret[i] = (arr1[i]   arr2[i]) / 2;
    }
    return ret;
}

CodePudding user response:

You have a two-loop approach, but that's not necessarily ugly. What you are effectively doing is summing two matrices of a single dimension. Like was pointed out in another answer unless you want to import a math library, you're going to have to roll your own.

If you would like to explore an external library, look to jblas, it's a linear algebra library for Java. Here's an example of summing and averaging those two vectors using jblas.

DoubleMatrix matrix1 = new DoubleMatrix(3, 1, 1.1, 2.2, 3.3);
DoubleMatrix matrix2 = new DoubleMatrix(3, 1, 0, 4, 2);

DoubleMatrix sum = matrix1.addColumnVector(matrix2).div(2);

log.info(sum);

The output is [0.550000; 3.100000; 2.650000]

CodePudding user response:

Here is a solution with Java 8 Streams.

The approach is to create a stream over the values of the given map, i.e. a stream of arrays Double[]. Then inside a collector, created using Collector.of() sum up array values for every index and as a final step divide each the value at each index by the map size.

In order to preserve arrays in the source map intact, we need to find out the length of these array and collect the data into a newly created array.

public static Double[] getAverage(Map<String, Double[]> userVecs) {
    if (userVecs.isEmpty()) {
        return new Double[0]; // throw an exception depending on your needs
    }
    
    int size = userVecs.values().iterator().next().length; // the map is proved to be non-empty, we can safely access one of its elements to find out the size of the resulting array
    
    return userVecs.values().stream()
        .collect(Collector.of(
            () -> Stream.generate(() -> 0d).limit(size).toArray(Double[]::new), // mutable container
            (Double[] res, Double[] next) -> mergeArrays(res, next),            // accumulator - populating the container
            (left, right) -> mergeArrays(left, right),                          // combiner - merging containers while executing in parallel
            res -> toAverage(res, userVecs.size())                              // finisher - transforming the container into a final result
        ));
}

public static Double[] mergeArrays(Double[] res, Double[] next) {
    for (int i = 0; i < res.length; i  ) res[i]  = next[i];
    return res;
}

public static Double[] toAverage(Double[] res, int mapSize) {
    for (int i = 0; i < res.length; i  ) res[i] /= mapSize;
    return res;
}

main()

public static void main(String[] args) {
    Map<String, Double[]> userVecs = new HashMap<>();
    
    userVecs.put("foo", new Double[]{1.1, 2.2, 3.3});
    userVecs.put("bar", new Double[]{0d, 4d, 2d});

    System.out.println(Arrays.toString(getAverage(userVecs)));
}

Output:

[0.55, 3.1, 2.65]

CodePudding user response:

Try this:

Map<String, Double[]> userVec = new LinkedHashMap<>();
        userVec.put("foo", new Double[]{1.1, 2.2, 3.3});
        userVec.put("bar", new Double[]{0.0, 4.0, 2.0});

        AtomicReference<Double> sum0 = new AtomicReference<>((double) 0);
        AtomicReference<Double> sum1 = new AtomicReference<>((double) 0);
        AtomicReference<Double> sum2 = new AtomicReference<>((double) 0);

        userVec.values()
                .forEach(doubles -> {
                    sum0.updateAndGet(v -> v   doubles[0]);
                    sum1.updateAndGet(v -> v   doubles[1]);
                    sum2.updateAndGet(v -> v   doubles[2]);
                });

        double avr0 = sum0.updateAndGet(v -> v / userVec.size());
        double avr1 = sum1.updateAndGet(v -> v / userVec.size());
        double avr2 = sum2.updateAndGet(v -> v / userVec.size());


        System.out.println(avr0);   //0.55
        System.out.println(avr1);   //3.1
        System.out.println(avr2);   //2.65

Hope to like it!

  • Related