Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

multiplying lists of lists with different lengths

Tags:

python

I have:

a = [[1,2],[3,4],[7,10]]
b = [[8,6],[1,9],[2,1],[8,8]]

I want to multiply (pairwise) every element of a with b

1*8+2*6+1*1+2*9+.....+1*8+2*8+3*8+4*6+......+7*8+10*8

Here's my code so far:

def f(a, b):
    new = [x for x in a or x in b]
    newer = []
    for tuple1, tuple2 in new:
        newer.append(map(lambda s,t: s*t, new, new))
    return sum(newer)

so my plan of attack was to get all the lists in one list and then multiply everything together. I have seen that lambda work for multiplying lists pairwise but I can't get it to work for the one list.

like image 592
user342624 Avatar asked Feb 12 '18 18:02

user342624


3 Answers

That kind of combination is called the Cartesian product. I'd use itertools.product for this, which can easily cope with more than 2 lists, if you want.

Firstly, here's a short demo that shows how to get all of the pairs and how to use tuple assignment to grab the individual elements of the pairs of sublists.

from itertools import product

a = [[1,2],[3,4],[7,10]]
b = [[8,6],[1,9],[2,1],[8,8]]

for (u0, u1), (v0, v1) in product(a, b):
    print(u0, u1, v0, v1)

output

1 2 8 6
1 2 1 9
1 2 2 1
1 2 8 8
3 4 8 6
3 4 1 9
3 4 2 1
3 4 8 8
7 10 8 6
7 10 1 9
7 10 2 1
7 10 8 8

And here's how to find the sum of products that you want.

total = sum(u0 * v0 + u1 * v1 for (u0, u1), (v0, v1) in product(a, b))
print(total)

output

593

Here's an alternative approach, using the distributive property, as mentioned by Prune.

Firstly, here's an unreadable list comprehension version. ;)

a = [[1,2],[3,4],[7,10]]
b = [[8,6],[1,9],[2,1],[8,8]]

print(sum([u*v for u,v in zip(*[[sum(t) for t in zip(*u)] for u in (a, b)])]))

output

593

How it works

By the distributive law, the sum you want from the given input data can be written as

(1 + 3 + 7) * (8 + 1 + 2 + 8) + (6 + 9 + 1 + 8) * (2 + 4 + 10)

We can re-arrange the data to produce that expression as follows.

# Use zip to transpose each of the a & b lists.
for u in (a, b):
    for t in zip(*u):
        print(t)

output

(1, 3, 7)
(2, 4, 10)
(8, 1, 2, 8)
(6, 9, 1, 8)

Now we modify that slightly to get the sums of those lists

# Use zip to transpose each of the a & b lists and compute the partial sums.
partial_sums = []
for u in (a, b):
    c = []
    for t in zip(*u):
        c.append(sum(t))
    partial_sums.append(c)
print(partial_sums)        

output

[[11, 16], [19, 24]]

Now we just need to multiply the corresponding items of those lists, and add those products together to get the final sum. Once again, we use zip to perform the transposition.

total = 0
for u, v in zip(*partial_sums):
    print(u, v)
    total += u * v
print(total)        

output

11 19
16 24
593
like image 188
PM 2Ring Avatar answered Oct 03 '22 13:10

PM 2Ring


If your only need is to return that conglomerate sum, I suggest that you quit doing all of that complex work: apply the distributive property. From your example, I'm not sure how thoroughly you are making your cross-products, but this collapses the example to do all elements of each.

sum1 = sum(sum(_) for _ in a)
sum2 = sum(sum(_) for _ in b)
return sum1 * sum2
like image 32
Prune Avatar answered Oct 03 '22 12:10

Prune


I suggest stepping back from the code and breaking this down into smaller parts. First, multiply each element from two lists, pairwise.

Second, given a list of lists a and a list of numbers b, multiply each list of a pairwise by b. This can reuse the solution to part 1.

Finally, solve your original problem with two lists of lists by reusing part 2.

Note how I am describing each of these pieces in words without worrying too much about Python syntax. Now that we have a description in words, it is easier to translate it into Python.

like image 42
Code-Apprentice Avatar answered Oct 03 '22 11:10

Code-Apprentice