简体   繁体   中英

Sum of products of pairs in a list

This is the problem I have. Given a list

xList = [9, 13, 10, 5, 3]

I would like to calculate for sum of each element multiplied by subsequent elements

sum([9*13, 9*10, 9*5 , 9*3]) + 
sum([13*10, 13*5, 13*3]) + 
sum([10*5, 10*3]) + 
sum ([5*3])

in this case the answer is 608 .

Is there a way to do this perhaps with itertools or natively with numpy ?

Below is a function I came up with. It does the job but it is far from ideal as I would like to add other stuff as well.

    def SumProduct(xList):
        ''' compute the sum of the product 
        of a list 
        e.g. 
        xList = [9, 13, 10, 5, 3]
        the result will be 
        sum([9*13, 9*10, 9*5 , 9*3]) + 
        sum([13*10, 13*5, 13*3]) + 
        sum([10*5, 10*3]) + 
        sum ([5*3])
        '''
        xSum = 0
        for xnr, x in enumerate(xList):
            #print xnr, x
            xList_1 = np.array(xList[xnr+1:])
            #print x * xList_1
            xSum = xSum + sum(x * xList_1)
        return xSum

Any help appreciated.

NB: In case you wonder, I am trying to implement Krippendorf's alpha with pandas

x = array([9, 13, 10, 5, 3])
result = (x.sum()**2 - x.dot(x)) / 2

This takes advantage of some mathematical simplifications to work in linear time and constant space, compared to other solutions that might have quadratic performance.

Here's a diagram of how this works. Suppose x = array([2, 3, 1]) . Then if you view the products as the areas of rectangles:

x is this stick: -- --- -

x.sum()**2 is this rectangle:
   -- --- -
  |xx xxx x
  |xx xxx x

  |xx xxx x
  |xx xxx x
  |xx xxx x

  |xx xxx x

x.dot(x) is this diagonal bit:
   -- --- -
  |xx
  |xx

  |   xxx
  |   xxx
  |   xxx

  |       x

(x.sum()**2 - x.dot(x)) is the non-diagonal parts:
   -- --- -
  |   xxx x
  |   xxx x

  |xx     x
  |xx     x
  |xx     x

  |xx xxx

and (x.sum()**2 - x.dot(x)) / 2 is the product you want:
   -- --- -
  |   xxx x
  |   xxx x

  |       x
  |       x
  |       x

  |

You actually want combinations not product:

from itertools import combinations

print(sum(a*b for a,b in combinations(xList,2)))
608

Even creating a numpy array from a python list, @user2357112 answer wipes the floor with the rest of us.

In [38]: timeit sum(a*b for a,b in combinations(xlist,2))
10000 loops, best of 3:
89.7 µs per loop

In [40]: timeit sum(mul(*t) for t in itertools.combinations(xlist, 2))
1000 loops, best of 3:
165 µs per loop

In [41]: %%timeit                                        
x = array(arr)
(x.sum()**2 - (x**2).sum()) / 2
   ....: 
100000 loops, best of 3:
 10.9 µs per loop

In [42]: timeit np.triu(np.outer(x, x), k=1).sum()
10000 loops, best of 3:
48.1 µs per loop
In [59]: %%timeit
....: xarr = np.array(xList)
....: N = xarr.size
....: range1 = np.arange(N)
....: mask = range1[:,None] < range1
....: out = ((mask*xarr)*xarr[:,None]).sum()
10000 loops, best of 3: 30.4 µs per loop

All the lists/arrays had 50 elements.

Stealing the logic from user2357112 and using it on a normal list with sum python is pretty darn efficient:

In [63]: timeit result = (sum(xList)**2 - sum(x ** 2 for x in xList)) / 2
100000 loops, best of 3: 
4.63 µs per loop

But for a large array the numpy solution is still significantly faster.

Here's one way:

In [14]: x = [9, 13, 10, 5, 3]

In [15]: np.triu(np.outer(x, x), k=1).sum()
Out[15]: 608

but I'd go with @user2357112's answer.

It looks like you want to get every combination of two elements (pairs) in that list, compute the product of each pair, and sum over these products:

import itertools

xlist = [9, 13, 10, 5, 3]
pairs = itertools.combinations(xlist, 2)
answer = 0
for pair in pairs:
    answer += pair[0] * pair[1]

The one-liner to do this:

import itertools
import operator

sum(operator.mul(*t) for t in itertools.combinations(xlist, 2))

One approach -

xarr = np.array(xList)

N = xarr.size
range1 = np.arange(N)

mask = range1[:,None] < range1
out = ((mask*xarr)*xarr[:,None]).sum()

Another one -

xarr = np.array(xList)

N = xarr.size
range1 = np.arange(N)

R,C = np.where(range1[:,None] < range1)
out = (xarr[R]*xarr[C]).sum()

If you are interested in doing this by hand (without help from the stdlib):

def combinations(L):
    for i,elem in enumerate(L):
        for e in L[i+1:]:
            yield (elem, e)

def main(xlist):
    answer = 0
    for a,b in combinations(xlist):
        answer += a*b
    return answer

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM