You can also use a compiled version of the function to calculate density. You can use cython or numba for that. I use numba to jit compile the density calculation function in the ans, as it is as easy as putting in a decorator.
Pros :
- You can write
if
conditions as you mention in your comments
- Slightly faster than the numpy version mentioned in the ans by
@Willem Van Onsem, as we have to iterate through the boolean array to
calculate the sum in
density.astype(int).sum()
.
Cons:
- Write an ugly three level loop. Looses the beauty of the singlish liner numpy solution.
Code:
import numba as nb
@nb.jit(nopython=True, cache=True)
def calc_density(xx, yy, zz, R0, hz):
threshold = R0 * R0
dimensions = xx.shape
density = 0
for i in range(dimensions[0]):
for j in range(dimensions[1]):
for k in range(dimensions[2]):
r_xy = xx[i][j][k] ** 2 + yy[i][j][k] ** 2
if(r_xy <= threshold and -hz <= zz[i][j][k] <= hz):
density+=1
return density
Running times:
Willem Van Onsem solution, f2 variant : 1.28s without sum, 2.01 with sum.
Numba solution( calc_density, on second run, to discount the compile time) : 0.48s.
As suggested in the comments, we need not calculate the meshgrid also. We can directly pass the x, y, z
to the function. Thus:
@nb.jit(nopython=True, cache=True)
def calc_density2(x, y, z, R0, hz):
threshold = R0 * R0
dimensions = len(x), len(y), len(z)
density = 0
for i in range(dimensions[0]):
for j in range(dimensions[1]):
for k in range(dimensions[2]):
r_xy = x[i] ** 2 + y[j] ** 2
if(r_xy <= threshold and -hz <= z[k] <= hz):
density+=1
return density
Now, for fair comparison, we also include the time of np.meshgrid
in @Willem Van Onsem's ans.
Running times:
Willem Van Onsem solution, f2 variant(np.meshgrid time included) : 2.24s
Numba solution( calc_density2, on second run, to discount the compile time) : 0.079s.