Home > Software design >  Calculating weighted average using two different list of lists
Calculating weighted average using two different list of lists

Time:06-21

I have 2 list of lists as follows:

A = [[],[],[],[500,540],[510,560,550],[460,510,600,450]]

B = [[],[],[],[4000,2500],[3500,3600,3000],[3000,2900,3300,3500]]

I would like to calculate the weighted average of values in every list of A using lists of B as corresponding weights. My output C should like like:

C = [[],[],[],[515],[540],[505]]

What I tried so far is using

C = []
for i in range(len(A)):
        avg = np.average(A[i], weights=B[i]) 
        C.append(avg)

Many thanks for your help.

CodePudding user response:

The code you have done works so far, except that it does not work for lists in A that have 0 len. So you can account for it through the following modification:

import numpy as np

A = [[],[],[],[500,540],[510,560,550],[460,510,600,450]]

B = [[],[],[],[4000,2500],[3500,3600,3000],[3000,2900,3300,3500]]

C = []
for i in range(len(A)):
    if len(A[i]) > 0:
        avg = np.average(A[i], weights=B[i]) 
        C.append(avg)
    else:
        C.append([])

Output:

C
Out[10]: [[], [], [], 515.3846153846154, 539.7029702970297, 505.03937007874015]

CodePudding user response:

The only issue is that weights can't be the empty list:

Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "<__array_function__ internals>", line 180, in average
  File "site-packages/numpy/lib/function_base.py", line 524, in average
    raise ZeroDivisionError(
ZeroDivisionError: Weights sum to zero, can't be normalized

However, B[:3] == [[], [], []], so you have three empty weights.

You can handle these empty lists accordingly:

C = []
for i in range(len(A)):
    avg = np.average(A[i], weights=B[i]) if B[i] else 0
    C.append(avg)

Output:

C == [0, 0, 0, 515.3846153846154, 539.7029702970297, 505.03937007874015]

CodePudding user response:

This is my best solution:

import numpy as np

A = [[], [], [], [500, 540], [510, 560, 550], [460, 510, 600, 450]]

B = [[], [], [], [4000, 2500], [3500, 3600, 3000], [3000, 2900, 3300, 3500]]
C = list()

for list_values, list_weights in zip(A, B):
    if len(list_values) == 0 or len(list_weights) == 0:
        C.append([])
    else:
        weighted_average = np.average(list_values, weights=list_weights)
        C.append(weighted_average)

CodePudding user response:

As others have pointed out, you need to deal with the empty lists. But no-one has given you the output you asked for. The other solutions also use counters (probably to mirror your own code), but this is not usually considered idiomatic Python.

I would probably do this in a list comprehension, and instead of indexing into both lists I'd take advantage of zip().

You said you wanted rounded integers in in 1-item lists, like:

 C = [[int(np.round(np.average(a, weights=b), 0))] if a else [] for a, b in zip(A, B)]

This returns what you specified in your question:

[[], [], [], [515], [540], [505]]

But if it was me, I think I'd want the results as floats like:

C = [np.average(a, weights=b) if a else np.nan for a, b in zip(A, B)]

This results in:

[nan, nan, nan, 515.3846153846154, 539.7029702970297, 505.03937007874015]

which personally I would prefer.

  • Related