Numpy: efficiently multiply arrays at location based on condition

Viewed 196

I have the following four arrays with identical shape:

a = np.array([12, 6, 7])
b = np.array([2, 3, 3])
c = np.array([5, 4, 9])
d = np.array([15, 22, 28])

As long as the condition num > 20 is met for each element in d, I want to multiply a by b and replace the values in c with the individual result of that multiplication. So, c would be equal to [5, 18, 21].

My attempt:

c[d > 20] = a[d > 20] * b[d > 20]

Is there any way to implement this more efficiently for large arrays?

2 Answers

The expression, c[d > 20] = a[d > 20] * b[d > 20] is computing the result of d > 20 three times. You can improve that by doing

mask = d > 20
c[mask] = a[mask] * b[mask]

To improve further, you can use the fact that np.multiply is a ufunc. This means that is has a where option to determine where the calculation will actually be done without making any temp arrays, and an out option to emplace the output:

np.multiply(a, b, where=d > 20, out=c)

A quick timeit shows that the second option is indeed approximately 2.5x faster (on my machine at least):

a = np.random.randint(50, size=10000)
b = np.random.randint(50, size=10000)
c = np.random.randint(50, size=10000)
d = np.random.randint(50, size=10000)

%%timeit
m = d > 20
c[m] = a[m] * b[m]
172 µs ± 460 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

%timeit np.multiply(a, b, where=d > 20, out=c)
66.4 µs ± 219 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

An even more important lesson to be had here is that this particular operation will almost certainly not be your bottleneck.

You can use np.where too:

c = np.where(d > 20, a * b, c)

which places a * b's values in the output where d > 20 and c's values otherwise.

timing: (setup from other answer)

a = np.random.randint(50, size=10000)
b = np.random.randint(50, size=10000)
c = np.random.randint(50, size=10000)
d = np.random.randint(50, size=10000)

%timeit np.where(d > 20, a * b, c)
28.2 µs ± 504 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Related