Yeg
Yeg

Reputation: 424

"Multiply" 1d numpy array with a smaller one and sum the result

I want to "multiply" (for lack of better description) a numpy array X of size M with a smaller numpy array Y of size N, for every N elements in X. Then, I want to sum the resulting array (almost like a dotproduct).

I hope the example makes it more clear:

Example
X = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Y = [1,2,3]

Z = mymul(X, Y)
  = [0*1, 1*2, 2*3, 3*1, 4*2, 5*3, 6*1, 7*2, 8*3, 9*1]
  = [  0,   2,   6,   3,   8,  15,   6,  14,  24,   9]

result = sum(Z) = 87

X and Y can be of varying lengths and Y is always smaller than X, but not necessarily divisible (e.g. M % N != 0)

I have some solutions but they are quite slow. I'm hoping there is a faster way to do this.

import numpy as np
X = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=int)
Y = np.array([1,2,3], dtype=int)

# these work but are slow for large X, Y

# simple for-loop
t = 0
for i in range(len(X)):
  t += X[i] * Y[i % len(Y)]
print(t) #87

# extend Y M/N times so np.dot can be applied
Ytiled = np.tile(Y, int(np.ceil(len(X) / len(Y))))[:len(X)] 
t = np.dot(X, Ytiled)
print(t) #87

Upvotes: 0

Views: 149

Answers (2)

Divakar
Divakar

Reputation: 221514

Resize Y to same length as X and then use matrix-multiplication -

In [52]: np.dot(X, np.resize(Y,len(X)))
Out[52]: 87

Alternative to using np.resize would be with tiling. Hence, np.tile(Y,(m+n-1)//n)[:m] for m,n = len(X), len(Y), could replace np.resize(Y,len(X)) for a faster one.

Another without resizing Y to achieve memory-efficiency -

In [79]: m,n = len(X), len(Y)

In [80]: s = n*(m//n)

In [81]: X2D = X[:s].reshape(-1,n)

In [82]: X2D.dot(Y).sum() + np.dot(X[s:],Y[:m-s])
Out[82]: 87

Alternatively, we can use np.einsum('ij,j->',X2D,Y) to replace X2D.dot(Y).sum().

Upvotes: 2

BlackBear
BlackBear

Reputation: 22979

You can use convolve (documentation):

np.convolve(X, Y[::-1], 'same')[::len(Y)].sum()

Remember to reverse the second array.

Upvotes: 1

Related Questions