Reputation:
I have one list including sets of vectors, all with 3 coordinates. I need to get a new list that will include the average of the respective coordinates of these vectors, as per their teams. In particular, I have a list of lists (of lists) of numbers:
list=[[[1,1,1],[0,1,0]],[[0,0,2]],[[1,1,1],[2,2,2],[2,2,1]]]
What I want is to get the following list:
new_list=[[1/2,1,1/2],[0,0,2],[5/3,5/3,4/3]]
which comes from adding the respective coordinates of the first two vectors of list
and then dividing by their number, i.e.:
[(1+0)/2, (1+1)/2, (0+1)/2]
then concatenating the third element of list
[0,0,2]
and finally adding the respective "coordinates" of the last 3 vectors and dividing them by their number, i.e.:
[(1+2+2)/3,(1+2+2)/3,(1+2+1)/3]
I have tried doing the above with a series of nested for loops
, but I keep loosing track of it and getting wrong results.
I am sorry if my problem is misstated, but I really could not find a better way to describe my situation.
Upvotes: 0
Views: 2881
Reputation: 5286
First of all, I would use tuples for the three-element lists as you are not gonna mutate them:
l = [
[
(1, 1, 1),
(0, 1, 0)
],
[
(0, 0, 2)
],
[
(1, 1, 1),
(2, 2, 2),
(2, 2, 1)
]
]
If you want a one-liner pythonic solution go for:
print(list(map(lambda *args: sum(args)/len(args), *(map(lambda *args: sum(args)/len(args), *item) for item in l))))
It could also be done with a function definition instead of lambda as its used twice:
def mean(*args):
return sum(args)/len(args)
print(list(map(mean, *(map(mean, *item) for item in l))))
As you can see the solution is using a lambda/function that computes the mean of the arguments passed. It is also using generators and map
.
Let's see it step by step:
for items in (map(mean, *item) for item in l):
print(list(items))
# [0.5, 1.0, 0.5]
# [0.0, 0.0, 2.0]
# [1.66666667, 1.666666667, 1.33333333]
step_1 = (map(mean, *item) for item in l)
step_2 = map(mean, *step_1)
print(list(step_2))
In the first step we are creating a generator (think of it as if it was a memory efficient array that you can only use once) for every element in the list, the *
is extracting the second level lists and passing them as multiple arguments to the function map. So basically map is receiving map(mean, (1,1,1), (0,1,0))
in the first iteration. Map function calls the mean function once per element in the tuples passing one element from each tuple: mean(1,0)
, mean(1,1)
and mean(1,0)
and then groups them in a map object which you can think is the same than the generator. And remember all this was done for each element in the first level of the list. So we have a generator that will yield 3 map objects: one with mean(1,0)
, mean(1,1)
and mean(1,0)
; a second one with mean(0)
, mean(0)
and mean(2)
and a last one with mean(1,2,2)
, mean(1,2,2)
and mean(1,2,1)
.
The second step is applying the same function with the same map expresion once more so that we get a single map object with: mean(mean(1,0),mean(0),mean(1,2,2))
, mean(mean(1,1),mean(0),mean(1,2,2))
and mean(mean(1,0),mean(2),mean(1,2,1))
. By forcinf it to transform to a list we can print it in the console.
The lambda version is exaclty the same but with anonymous functions.
If you are using Python 2.7 you will need to change len(args)
for float(len(args))
(2 time sin the lambda version, only once in the def version.
Upvotes: 0
Reputation: 3787
You need zip! Iterate through your input list, zip the sub lists, and then, for every element in zipped result, find the average!
>>> lis = [[[1,1,1],[0,1,0]],[[0,0,2]],[[1,1,1],[2,2,2],[2,2,1]]]
>>> centroid = lambda inp: [[sum(m)/float(len(m)) for m in zip(*l)] for l in inp]
>>> centroid(lis)
[[0.5, 1.0, 0.5], [0.0, 0.0, 2.0], [1.6666666666666667, 1.6666666666666667, 1.3333333333333333]]
To explain the centroid function,
>>> [zip(*l) for l in lis] #iterate through the input list and zip them to get what you call `adding the respective coordinates of the first two vectors of list`
[[(1, 0), (1, 1), (1, 0)], [(0,), (0,), (2,)], [(1, 2, 2), (1, 2, 2), (1, 2, 1)]]
>>> [[m for m in zip(*l)] for l in lis]
[[(1, 0), (1, 1), (1, 0)], [(0,), (0,), (2,)], [(1, 2, 2), (1, 2, 2), (1, 2, 1)]]
>>> [[sum(m) for m in zip(*l)] for l in lis]
[[1, 2, 1], [0, 0, 2], [5, 5, 4]]
>>> [[sum(m)/len(m) for m in zip(*l)] for l in lis]
[[0, 1, 0], [0, 0, 2], [1, 1, 1]]
>>> [[sum(m)/float(len(m)) for m in zip(*l)] for l in lis]
[[0.5, 1.0, 0.5], [0.0, 0.0, 2.0], [1.6666666666666667, 1.6666666666666667, 1.3333333333333333]]
Note: If you are using python3 and above, you need not convert your integer to float() explicitly while you divide.
Upvotes: 2
Reputation: 1980
This is more of an accounting problem than anything. I suggest breaking things down, and not trying to be fancy (just get it to work!)
mlist=[[[1,1,1],[0,1,0]],[[0,0,2]],[[1,1,1],[2,2,2],[2,2,1]]]
A = mlist[0]
A1,A2 = A[0],A[1]
B = mlist[1]
C = mlist[2]
C1,C2,C3 = C[0],C[1],C[2]
A = [(x + y)/2 for x,y, in zip(A1,A2)]
C = [np.sum(x)/len(x) for x in zip(C1,C2,C3)]
new_list = [A,B,C]
Upvotes: 0