I'd appreciate some help in finding and understanding a pythonic way to optimize the following array manipulations in nested for loops:
def _func(a, b, radius):
"Return 0 if a>b, otherwise return 1"
if distance.euclidean(a, b) < radius:
return 1
else:
return 0
def _make_mask(volume, roi, radius):
mask = numpy.zeros(volume.shape)
for x in range(volume.shape[0]):
for y in range(volume.shape[1]):
for z in range(volume.shape[2]):
mask[x, y, z] = _func((x, y, z), roi, radius)
return mask
Where volume.shape
(182, 218, 200) and roi.shape
(3,) are both ndarray
types; and radius
is an int
Approach #1
Here's a vectorized approach -
Possible improvement : We can probably speedup the last step with
numexpr
module -Approach #2
We can also gradually build the three ranges corresponding to the shape parameters and perform the subtraction against the three elements of
roi
on the fly without actually creating the meshes as done earlier withnp.mgrid
. This would be benefited by the use ofbroadcasting
for efficiency purposes. The implementation would look like this -Simplified version : Thanks to @Bi Rico for suggesting an improvement here as we can use
np.ogrid
to perform those operations in a bit more concise manner, like so -Runtime test
Function definitions -
Timings -
So, as always
broadcasting
showing its magic for a crazy almost10,000x
speedup over the original code and more than10x
better than creating meshes by using on-the-fly broadcasted operations!Say you first build an
xyzy
array:Now, using
numpy.linalg.norm
,checks whether the distance for each tuple from
roi
is smaller than radius.Finally, just
reshape
the result to the dimensions you need.