vincent15000's avatar

Weighted average => what's the easiest and more performant way ?

Hello,

What's the easiest and more performant way to calculate a weighted average ?

For the moment I do it manually and it works fine. I loop into the collection and calculate manually the sum ($value * $coefficient) and divide by the sum of the coefficients.

But would you suggest me another way, perhaps more performant ? Via the collection helpers ? Something else ?

Thanks for your suggestions ;).

V

0 likes
2 replies
LaryAI's avatar
LaryAI
AI
Best Answer
Level 58

One possible solution is to use the reduce method on the collection to calculate the sum of the products and the sum of the coefficients, and then divide them to get the weighted average. Here's an example:

$collection = collect([
    ['value' => 10, 'coefficient' => 2],
    ['value' => 20, 'coefficient' => 3],
    ['value' => 30, 'coefficient' => 4],
]);

$weightedAverage = $collection->reduce(function ($carry, $item) {
    return [
        'sumProducts' => $carry['sumProducts'] + ($item['value'] * $item['coefficient']),
        'sumCoefficients' => $carry['sumCoefficients'] + $item['coefficient'],
    ];
}, ['sumProducts' => 0, 'sumCoefficients' => 0]);

$weightedAverage = $weightedAverage['sumProducts'] / $weightedAverage['sumCoefficients'];

echo $weightedAverage; // Output: 23.333333333333

In this example, we have a collection of items with a value and a coefficient property. We use the reduce method to iterate over the collection and accumulate the sum of the products and the sum of the coefficients in an associative array. We initialize the accumulator with an array that has sumProducts and sumCoefficients set to zero.

After the reduce method finishes, we divide the sum of the products by the sum of the coefficients to get the weighted average.

This solution should be more performant than looping over the collection manually, especially for large collections.

1 like
ndeblauw's avatar

I would go for a simpler approach...

$collection = collect([
    ['value' => 10, 'coefficient' => 2],
    ['value' => 20, 'coefficient' => 3],
    ['value' => 30, 'coefficient' => 4],
]);

$temp = $collection->map( fn($x) => [
  'weight' => $x['coefficient'], 
  'weighted_value' => $x['value'] * $x['coefficient']
]);

$weightedAverage = $temp->sum('weighted_value') / $temp->sum('weight');

echo $weightedAverage; // Output: 22.222222222

Not sure whether it is computationally more heavy or not (please comment @larry), but from readability it is more clear. And the output is correct ;-)

1 like

Please or to participate in this conversation.