Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Average a columns in a 2D array with functional programming

As an exercise in functional programming, I've decided to go through one of my projects and replace functions containing for loops with Array.prototype's higher order functions such as map and reduce.

One function in my project averages columns in a two dimensional array. It takes an arguments samples which is a 2d array of size [n][LOOKBACK]:

[
    [0.6,  4.0, -0.5],
    [1.0, -0.5, -0.8],
    ...
]

const LOOKBACK = 3

function averageChange(samples) {
  let result = []
  let count = 0,
    i, j

  for (i = 0; i < LOOKBACK; i++) {

    let accumulator = 0

    for (j = 0; j < samples.length; j++) {
      accumulator += samples[j][i]
    }

    result.push(accumulator / samples.length)
  }

  return result
}

console.log(
  averageChange([
    [0.6, 4.0, -0.5],
    [1.0, -0.5, -0.8]
  ])
)

The output should be an array of size LOOKBACK who's elements are the average of each column:

[0.8, 1.75, -0.65]

I've spent some time trying to figure out a solution to this but I can't seem to come up with one.

Is this possible using Javascript's built in Array functions?

*Update

Got an elegant solution from Kirill. If anyone else has a nice solution, I would love to see more.

like image 729
Rocky Avatar asked Oct 26 '25 06:10

Rocky


1 Answers

Try this example with reduce and forEach functions:

let a = [
    [0.6,  4.0, -0.5],
    [3.0, -0.5, -0.1],
    [1.0, -0.2, -0.8],
    [7.0, -0.5, -0.8]
];

let b = a.reduce((acc, cur) => {
    cur.forEach((e, i) => acc[i] = acc[i] ? acc[i] + e : e);
    return acc;
}, []).map(e => e / a.length);

console.log(b);

Here is the more crafty method with matrix transpose:

let a = [
    [0.6,  4.0, -0.5],
    [3.0, -0.5, -0.1],
    [1.0, -0.2, -0.8],
    [7.0, -0.5, -0.8]
];

let b = a[0].map((col, i) => a.map(row => row[i]).reduce((acc, c) => acc + c, 0) / a.length);

console.log(b);
like image 188
Kirill Simonov Avatar answered Oct 28 '25 21:10

Kirill Simonov