Reputation: 23
I am trying to convert this piece of code into a list comprehension:
a = np.random.rand(10) #input vector
n = len(a) # element count of input vector
b = np.random.rand(3) #coefficient vector
nb = len(b) #element count of coefficients
d = nb #decimation factor (could be any integer < len(a))
c = []
for i in range(0, n, d):
psum = 0
for j in range(nb):
if i + j < n:
psum += a[i + j]*b[j]
c.append(psum)
I've tried following suggestions from:
For example:
from itertools import accumulate
c = [accumulate([a[i + j] * b[j] for j in range(nb) if i + j < n] ) for i in range(0, n, d)]
Later, when trying to get values from c
(e.g. c[:index]
):
TypeError: 'NoneType' object is not subscriptable
Or:
from functools import partial
def get_val(a, b, i, j, n):
if i + j < n:
return(a[i + j] * b[j])
else:
return(0)
c = [
list(map(partial(get_val, i=i, j=j, n=n), a, b))
for i in range(0, n, d)
for j in range(nb)
]
in get_val
, return(a[i + j] * b[j])
IndexError: invalid index to scalar variable.
Or:
psum_pieces = [[a[i + j] * b[j] if i + j < n else 0 for j in range(nb)] for i in range(0, n, d)]
c = [sum(psum) for psum in psum_pieces]
As well as many other iterations of these approaches. Any guidance would be much appreciated.
Upvotes: 2
Views: 388
Reputation: 114330
You really don't need to be using a list comprehension here. With numpy, you can create a fast pipelined solution that does not run any loops directly in the interpreter.
First convert a
into a 2D array shaped (n // d, nb)
. The missing elements (i.e., where i + j >= n
in the loop) can be zero since that will make the corresponding increment to psum
zero:
# pre-compute i+j as a 2D array
indices = np.arange(nb) + np.arange(0, n, d)[:, None]
# we only want valid locations
mask = indices < n
t = np.zeros(indices.shape)
t[mask] = a[indices[mask]]
Now you can compute c
directly as
(t * b).sum(axis=1)
I suspect that if you benchmark this solution against anything written in vanilla python not compiled with numba, it will be much faster.
Upvotes: 2
Reputation: 11929
If I've understood correctly what you want is something like
res = [sum(a[i+j]*b[j] for j in range(nb) if i+j < n) for i in range(0,n,d)]
For each i
, this will add to the resulting list the sum of the products a[i+j]*b[j]
for j
that varies from 0
to nb-1
when i+j < n
Upvotes: 1