13

Trying to imitate Excel's SUMPRODUCT function:

SUMPRODUCT(v1, v2, ..., vN) =
    v1[0]*v2[0]*...*vN[0] + v1[1]*v2[1]*...*vN[1] + ... + v1[n]*v2[n]*...*vN[n]

where n is the number of elements in each vector.

This is similar to dot product, but for multiple vectors. I read the very detailed discussion of the regular dot product, but I don't know how to cleanly extend it to multiple vectors. For reference, I'm copying the optimized code proposed there, which I ported (trivially) to Python 3. BTW, for dot product, the last approach still wins in P3K.

def d0(v1,v2):
    """                                                                                                     
    d0 is Nominal approach:                                                                                 
    multiply/add in a loop                                                                                  
    """
    out = 0
    for k in range(len(v1)):
        out += v1[k] * v2[k]
    return out

def d1(v1,v2):
    """                                                                                                     
    d1 uses a map                                                                        
    """
    return sum(map(mul,v1,v2))

def d3(v1,v2):
    """                                                                                                     
    d3 uses a starmap (itertools) to apply the mul operator on an zipped (v1,v2)                           
    """
    return sum(starmap(mul,zip(v1,v2)))
aydow
  • 3,673
  • 2
  • 23
  • 40
max
  • 49,282
  • 56
  • 208
  • 355

3 Answers3

22
import operator

def sumproduct(*lists):
    return sum(reduce(operator.mul, data) for data in zip(*lists))

for python 3

import operator
import functools

def sumproduct(*lists):
    return sum(functools.reduce(operator.mul, data) for data in zip(*lists))
aaronasterling
  • 68,820
  • 20
  • 127
  • 125
  • 1
    And if I need sumproduct to accept a list as an argument, I need to replace `def sumproduct(*lists)` with `def sumproduct(lists)` and keep the `zip(*lists)` call unchanged. – max Oct 05 '10 at 16:29
  • 1
    Shorter alt `def sumproduct(*lists): return sum(map(operator.mul, *lists))` ... but only for two lists, not sure yet whether it could be made simpler that way. – hhh Apr 04 '11 at 17:02
12

What about good old list comprehensions? (As mentioned by @Turksarama this only works for two lists)

sum([x * y for x, y in zip(*lists)])

Testing in Python 3.6:

In [532]: import random

In [534]: x = [random.randint(0,100) for _ in range(100)]

In [535]: y = [random.randint(0,100) for _ in range(100)]

In [536]: lists = x, y

Using list comprehensions

In [543]: %timeit(sum([x * y for x, y in zip(*lists)]))
8.73 µs ± 24.3 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

Note that "tuple" comprehensions are slower

In [537]: %timeit(sum(x * y for x, y in zip(*lists)))
10.5 µs ± 170 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

Using map

In [539]: %timeit(sum(map(lambda xi, yi: xi * yi, x, y)))
12.3 µs ± 144 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

Using functools.reduce

In [542]: %timeit(sum(functools.reduce(operator.mul, data) for data in zip(*lists)))
38.6 µs ± 330 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
aydow
  • 3,673
  • 2
  • 23
  • 40
  • 1
    Only works for two lists, I think this might be one of the only times where reduce is a better choice than a list comprehension just for that reason. – Turksarama Jun 28 '18 at 00:50
8

Map the list to create a list of products, and then sum it.

This can be done in one line:

sum(map(lambda Xi, Yi: Xi * Yi, ListX, ListY))
user2352238
  • 81
  • 1
  • 2